mirror of
https://github.com/hypervortex/VH-Bombsquad-Modded-Server-Files
synced 2025-11-07 17:36:08 +00:00
Added new files
This commit is contained in:
parent
5e004af549
commit
77ccb73089
1783 changed files with 566966 additions and 0 deletions
46
dist/ba_data/python-site-packages/flask/__init__.py
vendored
Normal file
46
dist/ba_data/python-site-packages/flask/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
from werkzeug.exceptions import abort as abort
|
||||
from werkzeug.utils import redirect as redirect
|
||||
|
||||
from . import json as json
|
||||
from .app import Flask as Flask
|
||||
from .app import Request as Request
|
||||
from .app import Response as Response
|
||||
from .blueprints import Blueprint as Blueprint
|
||||
from .config import Config as Config
|
||||
from .ctx import after_this_request as after_this_request
|
||||
from .ctx import copy_current_request_context as copy_current_request_context
|
||||
from .ctx import has_app_context as has_app_context
|
||||
from .ctx import has_request_context as has_request_context
|
||||
from .globals import _app_ctx_stack as _app_ctx_stack
|
||||
from .globals import _request_ctx_stack as _request_ctx_stack
|
||||
from .globals import current_app as current_app
|
||||
from .globals import g as g
|
||||
from .globals import request as request
|
||||
from .globals import session as session
|
||||
from .helpers import flash as flash
|
||||
from .helpers import get_flashed_messages as get_flashed_messages
|
||||
from .helpers import get_template_attribute as get_template_attribute
|
||||
from .helpers import make_response as make_response
|
||||
from .helpers import safe_join as safe_join
|
||||
from .helpers import send_file as send_file
|
||||
from .helpers import send_from_directory as send_from_directory
|
||||
from .helpers import stream_with_context as stream_with_context
|
||||
from .helpers import url_for as url_for
|
||||
from .json import jsonify as jsonify
|
||||
from .signals import appcontext_popped as appcontext_popped
|
||||
from .signals import appcontext_pushed as appcontext_pushed
|
||||
from .signals import appcontext_tearing_down as appcontext_tearing_down
|
||||
from .signals import before_render_template as before_render_template
|
||||
from .signals import got_request_exception as got_request_exception
|
||||
from .signals import message_flashed as message_flashed
|
||||
from .signals import request_finished as request_finished
|
||||
from .signals import request_started as request_started
|
||||
from .signals import request_tearing_down as request_tearing_down
|
||||
from .signals import signals_available as signals_available
|
||||
from .signals import template_rendered as template_rendered
|
||||
from .templating import render_template as render_template
|
||||
from .templating import render_template_string as render_template_string
|
||||
|
||||
__version__ = "2.0.3"
|
||||
3
dist/ba_data/python-site-packages/flask/__main__.py
vendored
Normal file
3
dist/ba_data/python-site-packages/flask/__main__.py
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from .cli import main
|
||||
|
||||
main()
|
||||
BIN
dist/ba_data/python-site-packages/flask/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/app.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/app.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/blueprints.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/blueprints.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/cli.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/cli.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/config.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/config.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/ctx.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/ctx.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/globals.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/globals.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/helpers.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/helpers.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/logging.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/logging.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/scaffold.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/scaffold.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/sessions.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/sessions.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/signals.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/signals.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/templating.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/templating.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/typing.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/typing.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/__pycache__/wrappers.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/__pycache__/wrappers.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
2091
dist/ba_data/python-site-packages/flask/app.py
vendored
Normal file
2091
dist/ba_data/python-site-packages/flask/app.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
609
dist/ba_data/python-site-packages/flask/blueprints.py
vendored
Normal file
609
dist/ba_data/python-site-packages/flask/blueprints.py
vendored
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
import os
|
||||
import typing as t
|
||||
from collections import defaultdict
|
||||
from functools import update_wrapper
|
||||
|
||||
from .scaffold import _endpoint_from_view_func
|
||||
from .scaffold import _sentinel
|
||||
from .scaffold import Scaffold
|
||||
from .typing import AfterRequestCallable
|
||||
from .typing import BeforeFirstRequestCallable
|
||||
from .typing import BeforeRequestCallable
|
||||
from .typing import TeardownCallable
|
||||
from .typing import TemplateContextProcessorCallable
|
||||
from .typing import TemplateFilterCallable
|
||||
from .typing import TemplateGlobalCallable
|
||||
from .typing import TemplateTestCallable
|
||||
from .typing import URLDefaultCallable
|
||||
from .typing import URLValuePreprocessorCallable
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
from .typing import ErrorHandlerCallable
|
||||
|
||||
DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
|
||||
|
||||
|
||||
class BlueprintSetupState:
|
||||
"""Temporary holder object for registering a blueprint with the
|
||||
application. An instance of this class is created by the
|
||||
:meth:`~flask.Blueprint.make_setup_state` method and later passed
|
||||
to all register callback functions.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
blueprint: "Blueprint",
|
||||
app: "Flask",
|
||||
options: t.Any,
|
||||
first_registration: bool,
|
||||
) -> None:
|
||||
#: a reference to the current application
|
||||
self.app = app
|
||||
|
||||
#: a reference to the blueprint that created this setup state.
|
||||
self.blueprint = blueprint
|
||||
|
||||
#: a dictionary with all options that were passed to the
|
||||
#: :meth:`~flask.Flask.register_blueprint` method.
|
||||
self.options = options
|
||||
|
||||
#: as blueprints can be registered multiple times with the
|
||||
#: application and not everything wants to be registered
|
||||
#: multiple times on it, this attribute can be used to figure
|
||||
#: out if the blueprint was registered in the past already.
|
||||
self.first_registration = first_registration
|
||||
|
||||
subdomain = self.options.get("subdomain")
|
||||
if subdomain is None:
|
||||
subdomain = self.blueprint.subdomain
|
||||
|
||||
#: The subdomain that the blueprint should be active for, ``None``
|
||||
#: otherwise.
|
||||
self.subdomain = subdomain
|
||||
|
||||
url_prefix = self.options.get("url_prefix")
|
||||
if url_prefix is None:
|
||||
url_prefix = self.blueprint.url_prefix
|
||||
#: The prefix that should be used for all URLs defined on the
|
||||
#: blueprint.
|
||||
self.url_prefix = url_prefix
|
||||
|
||||
self.name = self.options.get("name", blueprint.name)
|
||||
self.name_prefix = self.options.get("name_prefix", "")
|
||||
|
||||
#: A dictionary with URL defaults that is added to each and every
|
||||
#: URL that was defined with the blueprint.
|
||||
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
||||
self.url_defaults.update(self.options.get("url_defaults", ()))
|
||||
|
||||
def add_url_rule(
|
||||
self,
|
||||
rule: str,
|
||||
endpoint: t.Optional[str] = None,
|
||||
view_func: t.Optional[t.Callable] = None,
|
||||
**options: t.Any,
|
||||
) -> None:
|
||||
"""A helper method to register a rule (and optionally a view function)
|
||||
to the application. The endpoint is automatically prefixed with the
|
||||
blueprint's name.
|
||||
"""
|
||||
if self.url_prefix is not None:
|
||||
if rule:
|
||||
rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
|
||||
else:
|
||||
rule = self.url_prefix
|
||||
options.setdefault("subdomain", self.subdomain)
|
||||
if endpoint is None:
|
||||
endpoint = _endpoint_from_view_func(view_func) # type: ignore
|
||||
defaults = self.url_defaults
|
||||
if "defaults" in options:
|
||||
defaults = dict(defaults, **options.pop("defaults"))
|
||||
|
||||
self.app.add_url_rule(
|
||||
rule,
|
||||
f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."),
|
||||
view_func,
|
||||
defaults=defaults,
|
||||
**options,
|
||||
)
|
||||
|
||||
|
||||
class Blueprint(Scaffold):
|
||||
"""Represents a blueprint, a collection of routes and other
|
||||
app-related functions that can be registered on a real application
|
||||
later.
|
||||
|
||||
A blueprint is an object that allows defining application functions
|
||||
without requiring an application object ahead of time. It uses the
|
||||
same decorators as :class:`~flask.Flask`, but defers the need for an
|
||||
application by recording them for later registration.
|
||||
|
||||
Decorating a function with a blueprint creates a deferred function
|
||||
that is called with :class:`~flask.blueprints.BlueprintSetupState`
|
||||
when the blueprint is registered on an application.
|
||||
|
||||
See :doc:`/blueprints` for more information.
|
||||
|
||||
:param name: The name of the blueprint. Will be prepended to each
|
||||
endpoint name.
|
||||
:param import_name: The name of the blueprint package, usually
|
||||
``__name__``. This helps locate the ``root_path`` for the
|
||||
blueprint.
|
||||
:param static_folder: A folder with static files that should be
|
||||
served by the blueprint's static route. The path is relative to
|
||||
the blueprint's root path. Blueprint static files are disabled
|
||||
by default.
|
||||
:param static_url_path: The url to serve static files from.
|
||||
Defaults to ``static_folder``. If the blueprint does not have
|
||||
a ``url_prefix``, the app's static route will take precedence,
|
||||
and the blueprint's static files won't be accessible.
|
||||
:param template_folder: A folder with templates that should be added
|
||||
to the app's template search path. The path is relative to the
|
||||
blueprint's root path. Blueprint templates are disabled by
|
||||
default. Blueprint templates have a lower precedence than those
|
||||
in the app's templates folder.
|
||||
:param url_prefix: A path to prepend to all of the blueprint's URLs,
|
||||
to make them distinct from the rest of the app's routes.
|
||||
:param subdomain: A subdomain that blueprint routes will match on by
|
||||
default.
|
||||
:param url_defaults: A dict of default values that blueprint routes
|
||||
will receive by default.
|
||||
:param root_path: By default, the blueprint will automatically set
|
||||
this based on ``import_name``. In certain situations this
|
||||
automatic detection can fail, so the path can be specified
|
||||
manually instead.
|
||||
|
||||
.. versionchanged:: 1.1.0
|
||||
Blueprints have a ``cli`` group to register nested CLI commands.
|
||||
The ``cli_group`` parameter controls the name of the group under
|
||||
the ``flask`` command.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
|
||||
warn_on_modifications = False
|
||||
_got_registered_once = False
|
||||
|
||||
#: Blueprint local JSON encoder class to use. Set to ``None`` to use
|
||||
#: the app's :class:`~flask.Flask.json_encoder`.
|
||||
json_encoder = None
|
||||
#: Blueprint local JSON decoder class to use. Set to ``None`` to use
|
||||
#: the app's :class:`~flask.Flask.json_decoder`.
|
||||
json_decoder = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
import_name: str,
|
||||
static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
|
||||
static_url_path: t.Optional[str] = None,
|
||||
template_folder: t.Optional[str] = None,
|
||||
url_prefix: t.Optional[str] = None,
|
||||
subdomain: t.Optional[str] = None,
|
||||
url_defaults: t.Optional[dict] = None,
|
||||
root_path: t.Optional[str] = None,
|
||||
cli_group: t.Optional[str] = _sentinel, # type: ignore
|
||||
):
|
||||
super().__init__(
|
||||
import_name=import_name,
|
||||
static_folder=static_folder,
|
||||
static_url_path=static_url_path,
|
||||
template_folder=template_folder,
|
||||
root_path=root_path,
|
||||
)
|
||||
|
||||
if "." in name:
|
||||
raise ValueError("'name' may not contain a dot '.' character.")
|
||||
|
||||
self.name = name
|
||||
self.url_prefix = url_prefix
|
||||
self.subdomain = subdomain
|
||||
self.deferred_functions: t.List[DeferredSetupFunction] = []
|
||||
|
||||
if url_defaults is None:
|
||||
url_defaults = {}
|
||||
|
||||
self.url_values_defaults = url_defaults
|
||||
self.cli_group = cli_group
|
||||
self._blueprints: t.List[t.Tuple["Blueprint", dict]] = []
|
||||
|
||||
def _is_setup_finished(self) -> bool:
|
||||
return self.warn_on_modifications and self._got_registered_once
|
||||
|
||||
def record(self, func: t.Callable) -> None:
|
||||
"""Registers a function that is called when the blueprint is
|
||||
registered on the application. This function is called with the
|
||||
state as argument as returned by the :meth:`make_setup_state`
|
||||
method.
|
||||
"""
|
||||
if self._got_registered_once and self.warn_on_modifications:
|
||||
from warnings import warn
|
||||
|
||||
warn(
|
||||
Warning(
|
||||
"The blueprint was already registered once but is"
|
||||
" getting modified now. These changes will not show"
|
||||
" up."
|
||||
)
|
||||
)
|
||||
self.deferred_functions.append(func)
|
||||
|
||||
def record_once(self, func: t.Callable) -> None:
|
||||
"""Works like :meth:`record` but wraps the function in another
|
||||
function that will ensure the function is only called once. If the
|
||||
blueprint is registered a second time on the application, the
|
||||
function passed is not called.
|
||||
"""
|
||||
|
||||
def wrapper(state: BlueprintSetupState) -> None:
|
||||
if state.first_registration:
|
||||
func(state)
|
||||
|
||||
return self.record(update_wrapper(wrapper, func))
|
||||
|
||||
def make_setup_state(
|
||||
self, app: "Flask", options: dict, first_registration: bool = False
|
||||
) -> BlueprintSetupState:
|
||||
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
||||
object that is later passed to the register callback functions.
|
||||
Subclasses can override this to return a subclass of the setup state.
|
||||
"""
|
||||
return BlueprintSetupState(self, app, options, first_registration)
|
||||
|
||||
def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
|
||||
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword
|
||||
arguments passed to this method will override the defaults set
|
||||
on the blueprint.
|
||||
|
||||
.. versionchanged:: 2.0.1
|
||||
The ``name`` option can be used to change the (pre-dotted)
|
||||
name the blueprint is registered with. This allows the same
|
||||
blueprint to be registered multiple times with unique names
|
||||
for ``url_for``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
if blueprint is self:
|
||||
raise ValueError("Cannot register a blueprint on itself")
|
||||
self._blueprints.append((blueprint, options))
|
||||
|
||||
def register(self, app: "Flask", options: dict) -> None:
|
||||
"""Called by :meth:`Flask.register_blueprint` to register all
|
||||
views and callbacks registered on the blueprint with the
|
||||
application. Creates a :class:`.BlueprintSetupState` and calls
|
||||
each :meth:`record` callback with it.
|
||||
|
||||
:param app: The application this blueprint is being registered
|
||||
with.
|
||||
:param options: Keyword arguments forwarded from
|
||||
:meth:`~Flask.register_blueprint`.
|
||||
|
||||
.. versionchanged:: 2.0.1
|
||||
Nested blueprints are registered with their dotted name.
|
||||
This allows different blueprints with the same name to be
|
||||
nested at different locations.
|
||||
|
||||
.. versionchanged:: 2.0.1
|
||||
The ``name`` option can be used to change the (pre-dotted)
|
||||
name the blueprint is registered with. This allows the same
|
||||
blueprint to be registered multiple times with unique names
|
||||
for ``url_for``.
|
||||
|
||||
.. versionchanged:: 2.0.1
|
||||
Registering the same blueprint with the same name multiple
|
||||
times is deprecated and will become an error in Flask 2.1.
|
||||
"""
|
||||
name_prefix = options.get("name_prefix", "")
|
||||
self_name = options.get("name", self.name)
|
||||
name = f"{name_prefix}.{self_name}".lstrip(".")
|
||||
|
||||
if name in app.blueprints:
|
||||
existing_at = f" '{name}'" if self_name != name else ""
|
||||
|
||||
if app.blueprints[name] is not self:
|
||||
raise ValueError(
|
||||
f"The name '{self_name}' is already registered for"
|
||||
f" a different blueprint{existing_at}. Use 'name='"
|
||||
" to provide a unique name."
|
||||
)
|
||||
else:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
f"The name '{self_name}' is already registered for"
|
||||
f" this blueprint{existing_at}. Use 'name=' to"
|
||||
" provide a unique name. This will become an error"
|
||||
" in Flask 2.1.",
|
||||
stacklevel=4,
|
||||
)
|
||||
|
||||
first_bp_registration = not any(bp is self for bp in app.blueprints.values())
|
||||
first_name_registration = name not in app.blueprints
|
||||
|
||||
app.blueprints[name] = self
|
||||
self._got_registered_once = True
|
||||
state = self.make_setup_state(app, options, first_bp_registration)
|
||||
|
||||
if self.has_static_folder:
|
||||
state.add_url_rule(
|
||||
f"{self.static_url_path}/<path:filename>",
|
||||
view_func=self.send_static_file,
|
||||
endpoint="static",
|
||||
)
|
||||
|
||||
# Merge blueprint data into parent.
|
||||
if first_bp_registration or first_name_registration:
|
||||
|
||||
def extend(bp_dict, parent_dict):
|
||||
for key, values in bp_dict.items():
|
||||
key = name if key is None else f"{name}.{key}"
|
||||
parent_dict[key].extend(values)
|
||||
|
||||
for key, value in self.error_handler_spec.items():
|
||||
key = name if key is None else f"{name}.{key}"
|
||||
value = defaultdict(
|
||||
dict,
|
||||
{
|
||||
code: {
|
||||
exc_class: func for exc_class, func in code_values.items()
|
||||
}
|
||||
for code, code_values in value.items()
|
||||
},
|
||||
)
|
||||
app.error_handler_spec[key] = value
|
||||
|
||||
for endpoint, func in self.view_functions.items():
|
||||
app.view_functions[endpoint] = func
|
||||
|
||||
extend(self.before_request_funcs, app.before_request_funcs)
|
||||
extend(self.after_request_funcs, app.after_request_funcs)
|
||||
extend(
|
||||
self.teardown_request_funcs,
|
||||
app.teardown_request_funcs,
|
||||
)
|
||||
extend(self.url_default_functions, app.url_default_functions)
|
||||
extend(self.url_value_preprocessors, app.url_value_preprocessors)
|
||||
extend(self.template_context_processors, app.template_context_processors)
|
||||
|
||||
for deferred in self.deferred_functions:
|
||||
deferred(state)
|
||||
|
||||
cli_resolved_group = options.get("cli_group", self.cli_group)
|
||||
|
||||
if self.cli.commands:
|
||||
if cli_resolved_group is None:
|
||||
app.cli.commands.update(self.cli.commands)
|
||||
elif cli_resolved_group is _sentinel:
|
||||
self.cli.name = name
|
||||
app.cli.add_command(self.cli)
|
||||
else:
|
||||
self.cli.name = cli_resolved_group
|
||||
app.cli.add_command(self.cli)
|
||||
|
||||
for blueprint, bp_options in self._blueprints:
|
||||
bp_options = bp_options.copy()
|
||||
bp_url_prefix = bp_options.get("url_prefix")
|
||||
|
||||
if bp_url_prefix is None:
|
||||
bp_url_prefix = blueprint.url_prefix
|
||||
|
||||
if state.url_prefix is not None and bp_url_prefix is not None:
|
||||
bp_options["url_prefix"] = (
|
||||
state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/")
|
||||
)
|
||||
elif bp_url_prefix is not None:
|
||||
bp_options["url_prefix"] = bp_url_prefix
|
||||
elif state.url_prefix is not None:
|
||||
bp_options["url_prefix"] = state.url_prefix
|
||||
|
||||
bp_options["name_prefix"] = name
|
||||
blueprint.register(app, bp_options)
|
||||
|
||||
def add_url_rule(
|
||||
self,
|
||||
rule: str,
|
||||
endpoint: t.Optional[str] = None,
|
||||
view_func: t.Optional[t.Callable] = None,
|
||||
provide_automatic_options: t.Optional[bool] = None,
|
||||
**options: t.Any,
|
||||
) -> None:
|
||||
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
||||
the :func:`url_for` function is prefixed with the name of the blueprint.
|
||||
"""
|
||||
if endpoint and "." in endpoint:
|
||||
raise ValueError("'endpoint' may not contain a dot '.' character.")
|
||||
|
||||
if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__:
|
||||
raise ValueError("'view_func' name may not contain a dot '.' character.")
|
||||
|
||||
self.record(
|
||||
lambda s: s.add_url_rule(
|
||||
rule,
|
||||
endpoint,
|
||||
view_func,
|
||||
provide_automatic_options=provide_automatic_options,
|
||||
**options,
|
||||
)
|
||||
)
|
||||
|
||||
def app_template_filter(
|
||||
self, name: t.Optional[str] = None
|
||||
) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]:
|
||||
"""Register a custom template filter, available application wide. Like
|
||||
:meth:`Flask.template_filter` but for a blueprint.
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
|
||||
self.add_app_template_filter(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_filter(
|
||||
self, f: TemplateFilterCallable, name: t.Optional[str] = None
|
||||
) -> None:
|
||||
"""Register a custom template filter, available application wide. Like
|
||||
:meth:`Flask.add_template_filter` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_filter` decorator.
|
||||
|
||||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state: BlueprintSetupState) -> None:
|
||||
state.app.jinja_env.filters[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_test(
|
||||
self, name: t.Optional[str] = None
|
||||
) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]:
|
||||
"""Register a custom template test, available application wide. Like
|
||||
:meth:`Flask.template_test` but for a blueprint.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
|
||||
self.add_app_template_test(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_test(
|
||||
self, f: TemplateTestCallable, name: t.Optional[str] = None
|
||||
) -> None:
|
||||
"""Register a custom template test, available application wide. Like
|
||||
:meth:`Flask.add_template_test` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_test` decorator.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state: BlueprintSetupState) -> None:
|
||||
state.app.jinja_env.tests[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_global(
|
||||
self, name: t.Optional[str] = None
|
||||
) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]:
|
||||
"""Register a custom template global, available application wide. Like
|
||||
:meth:`Flask.template_global` but for a blueprint.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
|
||||
self.add_app_template_global(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_global(
|
||||
self, f: TemplateGlobalCallable, name: t.Optional[str] = None
|
||||
) -> None:
|
||||
"""Register a custom template global, available application wide. Like
|
||||
:meth:`Flask.add_template_global` but for a blueprint. Works exactly
|
||||
like the :meth:`app_template_global` decorator.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state: BlueprintSetupState) -> None:
|
||||
state.app.jinja_env.globals[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def before_app_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
|
||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||
before each request, even if outside of a blueprint.
|
||||
"""
|
||||
self.record_once(
|
||||
lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def before_app_first_request(
|
||||
self, f: BeforeFirstRequestCallable
|
||||
) -> BeforeFirstRequestCallable:
|
||||
"""Like :meth:`Flask.before_first_request`. Such a function is
|
||||
executed before the first request to the application.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
|
||||
return f
|
||||
|
||||
def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
|
||||
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
||||
is executed after each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(
|
||||
lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def teardown_app_request(self, f: TeardownCallable) -> TeardownCallable:
|
||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
||||
function is executed when tearing down each request, even if outside of
|
||||
the blueprint.
|
||||
"""
|
||||
self.record_once(
|
||||
lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_context_processor(
|
||||
self, f: TemplateContextProcessorCallable
|
||||
) -> TemplateContextProcessorCallable:
|
||||
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
||||
function is executed each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(
|
||||
lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_errorhandler(self, code: t.Union[t.Type[Exception], int]) -> t.Callable:
|
||||
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
||||
handler is used for all requests, even if outside of the blueprint.
|
||||
"""
|
||||
|
||||
def decorator(
|
||||
f: "ErrorHandlerCallable[Exception]",
|
||||
) -> "ErrorHandlerCallable[Exception]":
|
||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def app_url_value_preprocessor(
|
||||
self, f: URLValuePreprocessorCallable
|
||||
) -> URLValuePreprocessorCallable:
|
||||
"""Same as :meth:`url_value_preprocessor` but application wide."""
|
||||
self.record_once(
|
||||
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable:
|
||||
"""Same as :meth:`url_defaults` but application wide."""
|
||||
self.record_once(
|
||||
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
999
dist/ba_data/python-site-packages/flask/cli.py
vendored
Normal file
999
dist/ba_data/python-site-packages/flask/cli.py
vendored
Normal file
|
|
@ -0,0 +1,999 @@
|
|||
import ast
|
||||
import inspect
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import warnings
|
||||
from functools import update_wrapper
|
||||
from operator import attrgetter
|
||||
from threading import Lock
|
||||
from threading import Thread
|
||||
|
||||
import click
|
||||
from werkzeug.utils import import_string
|
||||
|
||||
from .globals import current_app
|
||||
from .helpers import get_debug_flag
|
||||
from .helpers import get_env
|
||||
from .helpers import get_load_dotenv
|
||||
|
||||
try:
|
||||
import dotenv
|
||||
except ImportError:
|
||||
dotenv = None
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
ssl = None # type: ignore
|
||||
|
||||
|
||||
class NoAppException(click.UsageError):
|
||||
"""Raised if an application cannot be found or loaded."""
|
||||
|
||||
|
||||
def find_best_app(script_info, module):
|
||||
"""Given a module instance this tries to find the best possible
|
||||
application in the module or raises an exception.
|
||||
"""
|
||||
from . import Flask
|
||||
|
||||
# Search for the most common names first.
|
||||
for attr_name in ("app", "application"):
|
||||
app = getattr(module, attr_name, None)
|
||||
|
||||
if isinstance(app, Flask):
|
||||
return app
|
||||
|
||||
# Otherwise find the only object that is a Flask instance.
|
||||
matches = [v for v in module.__dict__.values() if isinstance(v, Flask)]
|
||||
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
elif len(matches) > 1:
|
||||
raise NoAppException(
|
||||
"Detected multiple Flask applications in module"
|
||||
f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
|
||||
f" to specify the correct one."
|
||||
)
|
||||
|
||||
# Search for app factory functions.
|
||||
for attr_name in ("create_app", "make_app"):
|
||||
app_factory = getattr(module, attr_name, None)
|
||||
|
||||
if inspect.isfunction(app_factory):
|
||||
try:
|
||||
app = call_factory(script_info, app_factory)
|
||||
|
||||
if isinstance(app, Flask):
|
||||
return app
|
||||
except TypeError as e:
|
||||
if not _called_with_wrong_args(app_factory):
|
||||
raise
|
||||
|
||||
raise NoAppException(
|
||||
f"Detected factory {attr_name!r} in module {module.__name__!r},"
|
||||
" but could not call it without arguments. Use"
|
||||
f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\""
|
||||
" to specify arguments."
|
||||
) from e
|
||||
|
||||
raise NoAppException(
|
||||
"Failed to find Flask application or factory in module"
|
||||
f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
|
||||
" to specify one."
|
||||
)
|
||||
|
||||
|
||||
def call_factory(script_info, app_factory, args=None, kwargs=None):
|
||||
"""Takes an app factory, a ``script_info` object and optionally a tuple
|
||||
of arguments. Checks for the existence of a script_info argument and calls
|
||||
the app_factory depending on that and the arguments provided.
|
||||
"""
|
||||
sig = inspect.signature(app_factory)
|
||||
args = [] if args is None else args
|
||||
kwargs = {} if kwargs is None else kwargs
|
||||
|
||||
if "script_info" in sig.parameters:
|
||||
warnings.warn(
|
||||
"The 'script_info' argument is deprecated and will not be"
|
||||
" passed to the app factory function in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
kwargs["script_info"] = script_info
|
||||
|
||||
if not args and len(sig.parameters) == 1:
|
||||
first_parameter = next(iter(sig.parameters.values()))
|
||||
|
||||
if (
|
||||
first_parameter.default is inspect.Parameter.empty
|
||||
# **kwargs is reported as an empty default, ignore it
|
||||
and first_parameter.kind is not inspect.Parameter.VAR_KEYWORD
|
||||
):
|
||||
warnings.warn(
|
||||
"Script info is deprecated and will not be passed as the"
|
||||
" single argument to the app factory function in Flask"
|
||||
" 2.1.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
args.append(script_info)
|
||||
|
||||
return app_factory(*args, **kwargs)
|
||||
|
||||
|
||||
def _called_with_wrong_args(f):
|
||||
"""Check whether calling a function raised a ``TypeError`` because
|
||||
the call failed or because something in the factory raised the
|
||||
error.
|
||||
|
||||
:param f: The function that was called.
|
||||
:return: ``True`` if the call failed.
|
||||
"""
|
||||
tb = sys.exc_info()[2]
|
||||
|
||||
try:
|
||||
while tb is not None:
|
||||
if tb.tb_frame.f_code is f.__code__:
|
||||
# In the function, it was called successfully.
|
||||
return False
|
||||
|
||||
tb = tb.tb_next
|
||||
|
||||
# Didn't reach the function.
|
||||
return True
|
||||
finally:
|
||||
# Delete tb to break a circular reference.
|
||||
# https://docs.python.org/2/library/sys.html#sys.exc_info
|
||||
del tb
|
||||
|
||||
|
||||
def find_app_by_string(script_info, module, app_name):
|
||||
"""Check if the given string is a variable name or a function. Call
|
||||
a function to get the app instance, or return the variable directly.
|
||||
"""
|
||||
from . import Flask
|
||||
|
||||
# Parse app_name as a single expression to determine if it's a valid
|
||||
# attribute name or function call.
|
||||
try:
|
||||
expr = ast.parse(app_name.strip(), mode="eval").body
|
||||
except SyntaxError:
|
||||
raise NoAppException(
|
||||
f"Failed to parse {app_name!r} as an attribute name or function call."
|
||||
) from None
|
||||
|
||||
if isinstance(expr, ast.Name):
|
||||
name = expr.id
|
||||
args = kwargs = None
|
||||
elif isinstance(expr, ast.Call):
|
||||
# Ensure the function name is an attribute name only.
|
||||
if not isinstance(expr.func, ast.Name):
|
||||
raise NoAppException(
|
||||
f"Function reference must be a simple name: {app_name!r}."
|
||||
)
|
||||
|
||||
name = expr.func.id
|
||||
|
||||
# Parse the positional and keyword arguments as literals.
|
||||
try:
|
||||
args = [ast.literal_eval(arg) for arg in expr.args]
|
||||
kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.keywords}
|
||||
except ValueError:
|
||||
# literal_eval gives cryptic error messages, show a generic
|
||||
# message with the full expression instead.
|
||||
raise NoAppException(
|
||||
f"Failed to parse arguments as literal values: {app_name!r}."
|
||||
) from None
|
||||
else:
|
||||
raise NoAppException(
|
||||
f"Failed to parse {app_name!r} as an attribute name or function call."
|
||||
)
|
||||
|
||||
try:
|
||||
attr = getattr(module, name)
|
||||
except AttributeError as e:
|
||||
raise NoAppException(
|
||||
f"Failed to find attribute {name!r} in {module.__name__!r}."
|
||||
) from e
|
||||
|
||||
# If the attribute is a function, call it with any args and kwargs
|
||||
# to get the real application.
|
||||
if inspect.isfunction(attr):
|
||||
try:
|
||||
app = call_factory(script_info, attr, args, kwargs)
|
||||
except TypeError as e:
|
||||
if not _called_with_wrong_args(attr):
|
||||
raise
|
||||
|
||||
raise NoAppException(
|
||||
f"The factory {app_name!r} in module"
|
||||
f" {module.__name__!r} could not be called with the"
|
||||
" specified arguments."
|
||||
) from e
|
||||
else:
|
||||
app = attr
|
||||
|
||||
if isinstance(app, Flask):
|
||||
return app
|
||||
|
||||
raise NoAppException(
|
||||
"A valid Flask application was not obtained from"
|
||||
f" '{module.__name__}:{app_name}'."
|
||||
)
|
||||
|
||||
|
||||
def prepare_import(path):
|
||||
"""Given a filename this will try to calculate the python path, add it
|
||||
to the search path and return the actual module name that is expected.
|
||||
"""
|
||||
path = os.path.realpath(path)
|
||||
|
||||
fname, ext = os.path.splitext(path)
|
||||
if ext == ".py":
|
||||
path = fname
|
||||
|
||||
if os.path.basename(path) == "__init__":
|
||||
path = os.path.dirname(path)
|
||||
|
||||
module_name = []
|
||||
|
||||
# move up until outside package structure (no __init__.py)
|
||||
while True:
|
||||
path, name = os.path.split(path)
|
||||
module_name.append(name)
|
||||
|
||||
if not os.path.exists(os.path.join(path, "__init__.py")):
|
||||
break
|
||||
|
||||
if sys.path[0] != path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
return ".".join(module_name[::-1])
|
||||
|
||||
|
||||
def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
|
||||
try:
|
||||
__import__(module_name)
|
||||
except ImportError:
|
||||
# Reraise the ImportError if it occurred within the imported module.
|
||||
# Determine this by checking whether the trace has a depth > 1.
|
||||
if sys.exc_info()[2].tb_next:
|
||||
raise NoAppException(
|
||||
f"While importing {module_name!r}, an ImportError was"
|
||||
f" raised:\n\n{traceback.format_exc()}"
|
||||
) from None
|
||||
elif raise_if_not_found:
|
||||
raise NoAppException(f"Could not import {module_name!r}.") from None
|
||||
else:
|
||||
return
|
||||
|
||||
module = sys.modules[module_name]
|
||||
|
||||
if app_name is None:
|
||||
return find_best_app(script_info, module)
|
||||
else:
|
||||
return find_app_by_string(script_info, module, app_name)
|
||||
|
||||
|
||||
def get_version(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
|
||||
import werkzeug
|
||||
from . import __version__
|
||||
|
||||
click.echo(
|
||||
f"Python {platform.python_version()}\n"
|
||||
f"Flask {__version__}\n"
|
||||
f"Werkzeug {werkzeug.__version__}",
|
||||
color=ctx.color,
|
||||
)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
version_option = click.Option(
|
||||
["--version"],
|
||||
help="Show the flask version",
|
||||
expose_value=False,
|
||||
callback=get_version,
|
||||
is_flag=True,
|
||||
is_eager=True,
|
||||
)
|
||||
|
||||
|
||||
class DispatchingApp:
|
||||
"""Special application that dispatches to a Flask application which
|
||||
is imported by name in a background thread. If an error happens
|
||||
it is recorded and shown as part of the WSGI handling which in case
|
||||
of the Werkzeug debugger means that it shows up in the browser.
|
||||
"""
|
||||
|
||||
def __init__(self, loader, use_eager_loading=None):
|
||||
self.loader = loader
|
||||
self._app = None
|
||||
self._lock = Lock()
|
||||
self._bg_loading_exc = None
|
||||
|
||||
if use_eager_loading is None:
|
||||
use_eager_loading = os.environ.get("WERKZEUG_RUN_MAIN") != "true"
|
||||
|
||||
if use_eager_loading:
|
||||
self._load_unlocked()
|
||||
else:
|
||||
self._load_in_background()
|
||||
|
||||
def _load_in_background(self):
|
||||
def _load_app():
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
with self._lock:
|
||||
try:
|
||||
self._load_unlocked()
|
||||
except Exception as e:
|
||||
self._bg_loading_exc = e
|
||||
|
||||
t = Thread(target=_load_app, args=())
|
||||
t.start()
|
||||
|
||||
def _flush_bg_loading_exception(self):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
exc = self._bg_loading_exc
|
||||
|
||||
if exc is not None:
|
||||
self._bg_loading_exc = None
|
||||
raise exc
|
||||
|
||||
def _load_unlocked(self):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
self._app = rv = self.loader()
|
||||
self._bg_loading_exc = None
|
||||
return rv
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
if self._app is not None:
|
||||
return self._app(environ, start_response)
|
||||
self._flush_bg_loading_exception()
|
||||
with self._lock:
|
||||
if self._app is not None:
|
||||
rv = self._app
|
||||
else:
|
||||
rv = self._load_unlocked()
|
||||
return rv(environ, start_response)
|
||||
|
||||
|
||||
class ScriptInfo:
|
||||
"""Helper object to deal with Flask applications. This is usually not
|
||||
necessary to interface with as it's used internally in the dispatching
|
||||
to click. In future versions of Flask this object will most likely play
|
||||
a bigger role. Typically it's created automatically by the
|
||||
:class:`FlaskGroup` but you can also manually create it and pass it
|
||||
onwards as click object.
|
||||
"""
|
||||
|
||||
def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True):
|
||||
#: Optionally the import path for the Flask application.
|
||||
self.app_import_path = app_import_path or os.environ.get("FLASK_APP")
|
||||
#: Optionally a function that is passed the script info to create
|
||||
#: the instance of the application.
|
||||
self.create_app = create_app
|
||||
#: A dictionary with arbitrary data that can be associated with
|
||||
#: this script info.
|
||||
self.data = {}
|
||||
self.set_debug_flag = set_debug_flag
|
||||
self._loaded_app = None
|
||||
|
||||
def load_app(self):
|
||||
"""Loads the Flask app (if not yet loaded) and returns it. Calling
|
||||
this multiple times will just result in the already loaded app to
|
||||
be returned.
|
||||
"""
|
||||
__traceback_hide__ = True # noqa: F841
|
||||
|
||||
if self._loaded_app is not None:
|
||||
return self._loaded_app
|
||||
|
||||
if self.create_app is not None:
|
||||
app = call_factory(self, self.create_app)
|
||||
else:
|
||||
if self.app_import_path:
|
||||
path, name = (
|
||||
re.split(r":(?![\\/])", self.app_import_path, 1) + [None]
|
||||
)[:2]
|
||||
import_name = prepare_import(path)
|
||||
app = locate_app(self, import_name, name)
|
||||
else:
|
||||
for path in ("wsgi.py", "app.py"):
|
||||
import_name = prepare_import(path)
|
||||
app = locate_app(self, import_name, None, raise_if_not_found=False)
|
||||
|
||||
if app:
|
||||
break
|
||||
|
||||
if not app:
|
||||
raise NoAppException(
|
||||
"Could not locate a Flask application. You did not provide "
|
||||
'the "FLASK_APP" environment variable, and a "wsgi.py" or '
|
||||
'"app.py" module was not found in the current directory.'
|
||||
)
|
||||
|
||||
if self.set_debug_flag:
|
||||
# Update the app's debug flag through the descriptor so that
|
||||
# other values repopulate as well.
|
||||
app.debug = get_debug_flag()
|
||||
|
||||
self._loaded_app = app
|
||||
return app
|
||||
|
||||
|
||||
pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True)
|
||||
|
||||
|
||||
def with_appcontext(f):
|
||||
"""Wraps a callback so that it's guaranteed to be executed with the
|
||||
script's application context. If callbacks are registered directly
|
||||
to the ``app.cli`` object then they are wrapped with this function
|
||||
by default unless it's disabled.
|
||||
"""
|
||||
|
||||
@click.pass_context
|
||||
def decorator(__ctx, *args, **kwargs):
|
||||
with __ctx.ensure_object(ScriptInfo).load_app().app_context():
|
||||
return __ctx.invoke(f, *args, **kwargs)
|
||||
|
||||
return update_wrapper(decorator, f)
|
||||
|
||||
|
||||
class AppGroup(click.Group):
|
||||
"""This works similar to a regular click :class:`~click.Group` but it
|
||||
changes the behavior of the :meth:`command` decorator so that it
|
||||
automatically wraps the functions in :func:`with_appcontext`.
|
||||
|
||||
Not to be confused with :class:`FlaskGroup`.
|
||||
"""
|
||||
|
||||
def command(self, *args, **kwargs):
|
||||
"""This works exactly like the method of the same name on a regular
|
||||
:class:`click.Group` but it wraps callbacks in :func:`with_appcontext`
|
||||
unless it's disabled by passing ``with_appcontext=False``.
|
||||
"""
|
||||
wrap_for_ctx = kwargs.pop("with_appcontext", True)
|
||||
|
||||
def decorator(f):
|
||||
if wrap_for_ctx:
|
||||
f = with_appcontext(f)
|
||||
return click.Group.command(self, *args, **kwargs)(f)
|
||||
|
||||
return decorator
|
||||
|
||||
def group(self, *args, **kwargs):
|
||||
"""This works exactly like the method of the same name on a regular
|
||||
:class:`click.Group` but it defaults the group class to
|
||||
:class:`AppGroup`.
|
||||
"""
|
||||
kwargs.setdefault("cls", AppGroup)
|
||||
return click.Group.group(self, *args, **kwargs)
|
||||
|
||||
|
||||
class FlaskGroup(AppGroup):
|
||||
"""Special subclass of the :class:`AppGroup` group that supports
|
||||
loading more commands from the configured Flask app. Normally a
|
||||
developer does not have to interface with this class but there are
|
||||
some very advanced use cases for which it makes sense to create an
|
||||
instance of this. see :ref:`custom-scripts`.
|
||||
|
||||
:param add_default_commands: if this is True then the default run and
|
||||
shell commands will be added.
|
||||
:param add_version_option: adds the ``--version`` option.
|
||||
:param create_app: an optional callback that is passed the script info and
|
||||
returns the loaded app.
|
||||
:param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
|
||||
files to set environment variables. Will also change the working
|
||||
directory to the directory containing the first file found.
|
||||
:param set_debug_flag: Set the app's debug flag based on the active
|
||||
environment
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
If installed, python-dotenv will be used to load environment variables
|
||||
from :file:`.env` and :file:`.flaskenv` files.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
add_default_commands=True,
|
||||
create_app=None,
|
||||
add_version_option=True,
|
||||
load_dotenv=True,
|
||||
set_debug_flag=True,
|
||||
**extra,
|
||||
):
|
||||
params = list(extra.pop("params", None) or ())
|
||||
|
||||
if add_version_option:
|
||||
params.append(version_option)
|
||||
|
||||
AppGroup.__init__(self, params=params, **extra)
|
||||
self.create_app = create_app
|
||||
self.load_dotenv = load_dotenv
|
||||
self.set_debug_flag = set_debug_flag
|
||||
|
||||
if add_default_commands:
|
||||
self.add_command(run_command)
|
||||
self.add_command(shell_command)
|
||||
self.add_command(routes_command)
|
||||
|
||||
self._loaded_plugin_commands = False
|
||||
|
||||
def _load_plugin_commands(self):
|
||||
if self._loaded_plugin_commands:
|
||||
return
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
self._loaded_plugin_commands = True
|
||||
return
|
||||
|
||||
for ep in pkg_resources.iter_entry_points("flask.commands"):
|
||||
self.add_command(ep.load(), ep.name)
|
||||
self._loaded_plugin_commands = True
|
||||
|
||||
def get_command(self, ctx, name):
|
||||
self._load_plugin_commands()
|
||||
# Look up built-in and plugin commands, which should be
|
||||
# available even if the app fails to load.
|
||||
rv = super().get_command(ctx, name)
|
||||
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
info = ctx.ensure_object(ScriptInfo)
|
||||
|
||||
# Look up commands provided by the app, showing an error and
|
||||
# continuing if the app couldn't be loaded.
|
||||
try:
|
||||
return info.load_app().cli.get_command(ctx, name)
|
||||
except NoAppException as e:
|
||||
click.secho(f"Error: {e.format_message()}\n", err=True, fg="red")
|
||||
|
||||
def list_commands(self, ctx):
|
||||
self._load_plugin_commands()
|
||||
# Start with the built-in and plugin commands.
|
||||
rv = set(super().list_commands(ctx))
|
||||
info = ctx.ensure_object(ScriptInfo)
|
||||
|
||||
# Add commands provided by the app, showing an error and
|
||||
# continuing if the app couldn't be loaded.
|
||||
try:
|
||||
rv.update(info.load_app().cli.list_commands(ctx))
|
||||
except NoAppException as e:
|
||||
# When an app couldn't be loaded, show the error message
|
||||
# without the traceback.
|
||||
click.secho(f"Error: {e.format_message()}\n", err=True, fg="red")
|
||||
except Exception:
|
||||
# When any other errors occurred during loading, show the
|
||||
# full traceback.
|
||||
click.secho(f"{traceback.format_exc()}\n", err=True, fg="red")
|
||||
|
||||
return sorted(rv)
|
||||
|
||||
def main(self, *args, **kwargs):
|
||||
# Set a global flag that indicates that we were invoked from the
|
||||
# command line interface. This is detected by Flask.run to make the
|
||||
# call into a no-op. This is necessary to avoid ugly errors when the
|
||||
# script that is loaded here also attempts to start a server.
|
||||
os.environ["FLASK_RUN_FROM_CLI"] = "true"
|
||||
|
||||
if get_load_dotenv(self.load_dotenv):
|
||||
load_dotenv()
|
||||
|
||||
obj = kwargs.get("obj")
|
||||
|
||||
if obj is None:
|
||||
obj = ScriptInfo(
|
||||
create_app=self.create_app, set_debug_flag=self.set_debug_flag
|
||||
)
|
||||
|
||||
kwargs["obj"] = obj
|
||||
kwargs.setdefault("auto_envvar_prefix", "FLASK")
|
||||
return super().main(*args, **kwargs)
|
||||
|
||||
|
||||
def _path_is_ancestor(path, other):
|
||||
"""Take ``other`` and remove the length of ``path`` from it. Then join it
|
||||
to ``path``. If it is the original value, ``path`` is an ancestor of
|
||||
``other``."""
|
||||
return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other
|
||||
|
||||
|
||||
def load_dotenv(path=None):
|
||||
"""Load "dotenv" files in order of precedence to set environment variables.
|
||||
|
||||
If an env var is already set it is not overwritten, so earlier files in the
|
||||
list are preferred over later files.
|
||||
|
||||
This is a no-op if `python-dotenv`_ is not installed.
|
||||
|
||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||
|
||||
:param path: Load the file at this location instead of searching.
|
||||
:return: ``True`` if a file was loaded.
|
||||
|
||||
.. versionchanged:: 1.1.0
|
||||
Returns ``False`` when python-dotenv is not installed, or when
|
||||
the given path isn't a file.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
When loading the env files, set the default encoding to UTF-8.
|
||||
|
||||
.. versionadded:: 1.0
|
||||
"""
|
||||
if dotenv is None:
|
||||
if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"):
|
||||
click.secho(
|
||||
" * Tip: There are .env or .flaskenv files present."
|
||||
' Do "pip install python-dotenv" to use them.',
|
||||
fg="yellow",
|
||||
err=True,
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
# if the given path specifies the actual file then return True,
|
||||
# else False
|
||||
if path is not None:
|
||||
if os.path.isfile(path):
|
||||
return dotenv.load_dotenv(path, encoding="utf-8")
|
||||
|
||||
return False
|
||||
|
||||
new_dir = None
|
||||
|
||||
for name in (".env", ".flaskenv"):
|
||||
path = dotenv.find_dotenv(name, usecwd=True)
|
||||
|
||||
if not path:
|
||||
continue
|
||||
|
||||
if new_dir is None:
|
||||
new_dir = os.path.dirname(path)
|
||||
|
||||
dotenv.load_dotenv(path, encoding="utf-8")
|
||||
|
||||
return new_dir is not None # at least one file was located and loaded
|
||||
|
||||
|
||||
def show_server_banner(env, debug, app_import_path, eager_loading):
|
||||
"""Show extra startup messages the first time the server is run,
|
||||
ignoring the reloader.
|
||||
"""
|
||||
if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||
return
|
||||
|
||||
if app_import_path is not None:
|
||||
message = f" * Serving Flask app {app_import_path!r}"
|
||||
|
||||
if not eager_loading:
|
||||
message += " (lazy loading)"
|
||||
|
||||
click.echo(message)
|
||||
|
||||
click.echo(f" * Environment: {env}")
|
||||
|
||||
if env == "production":
|
||||
click.secho(
|
||||
" WARNING: This is a development server. Do not use it in"
|
||||
" a production deployment.",
|
||||
fg="red",
|
||||
)
|
||||
click.secho(" Use a production WSGI server instead.", dim=True)
|
||||
|
||||
if debug is not None:
|
||||
click.echo(f" * Debug mode: {'on' if debug else 'off'}")
|
||||
|
||||
|
||||
class CertParamType(click.ParamType):
|
||||
"""Click option type for the ``--cert`` option. Allows either an
|
||||
existing file, the string ``'adhoc'``, or an import for a
|
||||
:class:`~ssl.SSLContext` object.
|
||||
"""
|
||||
|
||||
name = "path"
|
||||
|
||||
def __init__(self):
|
||||
self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True)
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
if ssl is None:
|
||||
raise click.BadParameter(
|
||||
'Using "--cert" requires Python to be compiled with SSL support.',
|
||||
ctx,
|
||||
param,
|
||||
)
|
||||
|
||||
try:
|
||||
return self.path_type(value, param, ctx)
|
||||
except click.BadParameter:
|
||||
value = click.STRING(value, param, ctx).lower()
|
||||
|
||||
if value == "adhoc":
|
||||
try:
|
||||
import cryptography # noqa: F401
|
||||
except ImportError:
|
||||
raise click.BadParameter(
|
||||
"Using ad-hoc certificates requires the cryptography library.",
|
||||
ctx,
|
||||
param,
|
||||
) from None
|
||||
|
||||
return value
|
||||
|
||||
obj = import_string(value, silent=True)
|
||||
|
||||
if isinstance(obj, ssl.SSLContext):
|
||||
return obj
|
||||
|
||||
raise
|
||||
|
||||
|
||||
def _validate_key(ctx, param, value):
|
||||
"""The ``--key`` option must be specified when ``--cert`` is a file.
|
||||
Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed.
|
||||
"""
|
||||
cert = ctx.params.get("cert")
|
||||
is_adhoc = cert == "adhoc"
|
||||
is_context = ssl and isinstance(cert, ssl.SSLContext)
|
||||
|
||||
if value is not None:
|
||||
if is_adhoc:
|
||||
raise click.BadParameter(
|
||||
'When "--cert" is "adhoc", "--key" is not used.', ctx, param
|
||||
)
|
||||
|
||||
if is_context:
|
||||
raise click.BadParameter(
|
||||
'When "--cert" is an SSLContext object, "--key is not used.', ctx, param
|
||||
)
|
||||
|
||||
if not cert:
|
||||
raise click.BadParameter('"--cert" must also be specified.', ctx, param)
|
||||
|
||||
ctx.params["cert"] = cert, value
|
||||
|
||||
else:
|
||||
if cert and not (is_adhoc or is_context):
|
||||
raise click.BadParameter('Required when using "--cert".', ctx, param)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class SeparatedPathType(click.Path):
|
||||
"""Click option type that accepts a list of values separated by the
|
||||
OS's path separator (``:``, ``;`` on Windows). Each value is
|
||||
validated as a :class:`click.Path` type.
|
||||
"""
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
items = self.split_envvar_value(value)
|
||||
super_convert = super().convert
|
||||
return [super_convert(item, param, ctx) for item in items]
|
||||
|
||||
|
||||
@click.command("run", short_help="Run a development server.")
|
||||
@click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.")
|
||||
@click.option("--port", "-p", default=5000, help="The port to bind to.")
|
||||
@click.option(
|
||||
"--cert", type=CertParamType(), help="Specify a certificate file to use HTTPS."
|
||||
)
|
||||
@click.option(
|
||||
"--key",
|
||||
type=click.Path(exists=True, dir_okay=False, resolve_path=True),
|
||||
callback=_validate_key,
|
||||
expose_value=False,
|
||||
help="The key file to use when specifying a certificate.",
|
||||
)
|
||||
@click.option(
|
||||
"--reload/--no-reload",
|
||||
default=None,
|
||||
help="Enable or disable the reloader. By default the reloader "
|
||||
"is active if debug is enabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--debugger/--no-debugger",
|
||||
default=None,
|
||||
help="Enable or disable the debugger. By default the debugger "
|
||||
"is active if debug is enabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--eager-loading/--lazy-loading",
|
||||
default=None,
|
||||
help="Enable or disable eager loading. By default eager "
|
||||
"loading is enabled if the reloader is disabled.",
|
||||
)
|
||||
@click.option(
|
||||
"--with-threads/--without-threads",
|
||||
default=True,
|
||||
help="Enable or disable multithreading.",
|
||||
)
|
||||
@click.option(
|
||||
"--extra-files",
|
||||
default=None,
|
||||
type=SeparatedPathType(),
|
||||
help=(
|
||||
"Extra files that trigger a reload on change. Multiple paths"
|
||||
f" are separated by {os.path.pathsep!r}."
|
||||
),
|
||||
)
|
||||
@pass_script_info
|
||||
def run_command(
|
||||
info, host, port, reload, debugger, eager_loading, with_threads, cert, extra_files
|
||||
):
|
||||
"""Run a local development server.
|
||||
|
||||
This server is for development purposes only. It does not provide
|
||||
the stability, security, or performance of production WSGI servers.
|
||||
|
||||
The reloader and debugger are enabled by default if
|
||||
FLASK_ENV=development or FLASK_DEBUG=1.
|
||||
"""
|
||||
debug = get_debug_flag()
|
||||
|
||||
if reload is None:
|
||||
reload = debug
|
||||
|
||||
if debugger is None:
|
||||
debugger = debug
|
||||
|
||||
show_server_banner(get_env(), debug, info.app_import_path, eager_loading)
|
||||
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
|
||||
|
||||
from werkzeug.serving import run_simple
|
||||
|
||||
run_simple(
|
||||
host,
|
||||
port,
|
||||
app,
|
||||
use_reloader=reload,
|
||||
use_debugger=debugger,
|
||||
threaded=with_threads,
|
||||
ssl_context=cert,
|
||||
extra_files=extra_files,
|
||||
)
|
||||
|
||||
|
||||
@click.command("shell", short_help="Run a shell in the app context.")
|
||||
@with_appcontext
|
||||
def shell_command() -> None:
|
||||
"""Run an interactive Python shell in the context of a given
|
||||
Flask application. The application will populate the default
|
||||
namespace of this shell according to its configuration.
|
||||
|
||||
This is useful for executing small snippets of management code
|
||||
without having to manually configure the application.
|
||||
"""
|
||||
import code
|
||||
from .globals import _app_ctx_stack
|
||||
|
||||
app = _app_ctx_stack.top.app
|
||||
banner = (
|
||||
f"Python {sys.version} on {sys.platform}\n"
|
||||
f"App: {app.import_name} [{app.env}]\n"
|
||||
f"Instance: {app.instance_path}"
|
||||
)
|
||||
ctx: dict = {}
|
||||
|
||||
# Support the regular Python interpreter startup script if someone
|
||||
# is using it.
|
||||
startup = os.environ.get("PYTHONSTARTUP")
|
||||
if startup and os.path.isfile(startup):
|
||||
with open(startup) as f:
|
||||
eval(compile(f.read(), startup, "exec"), ctx)
|
||||
|
||||
ctx.update(app.make_shell_context())
|
||||
|
||||
# Site, customize, or startup script can set a hook to call when
|
||||
# entering interactive mode. The default one sets up readline with
|
||||
# tab and history completion.
|
||||
interactive_hook = getattr(sys, "__interactivehook__", None)
|
||||
|
||||
if interactive_hook is not None:
|
||||
try:
|
||||
import readline
|
||||
from rlcompleter import Completer
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
# rlcompleter uses __main__.__dict__ by default, which is
|
||||
# flask.__main__. Use the shell context instead.
|
||||
readline.set_completer(Completer(ctx).complete)
|
||||
|
||||
interactive_hook()
|
||||
|
||||
code.interact(banner=banner, local=ctx)
|
||||
|
||||
|
||||
@click.command("routes", short_help="Show the routes for the app.")
|
||||
@click.option(
|
||||
"--sort",
|
||||
"-s",
|
||||
type=click.Choice(("endpoint", "methods", "rule", "match")),
|
||||
default="endpoint",
|
||||
help=(
|
||||
'Method to sort routes by. "match" is the order that Flask will match '
|
||||
"routes when dispatching a request."
|
||||
),
|
||||
)
|
||||
@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
|
||||
@with_appcontext
|
||||
def routes_command(sort: str, all_methods: bool) -> None:
|
||||
"""Show all registered routes with endpoints and methods."""
|
||||
|
||||
rules = list(current_app.url_map.iter_rules())
|
||||
if not rules:
|
||||
click.echo("No routes were registered.")
|
||||
return
|
||||
|
||||
ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS"))
|
||||
|
||||
if sort in ("endpoint", "rule"):
|
||||
rules = sorted(rules, key=attrgetter(sort))
|
||||
elif sort == "methods":
|
||||
rules = sorted(rules, key=lambda rule: sorted(rule.methods)) # type: ignore
|
||||
|
||||
rule_methods = [
|
||||
", ".join(sorted(rule.methods - ignored_methods)) # type: ignore
|
||||
for rule in rules
|
||||
]
|
||||
|
||||
headers = ("Endpoint", "Methods", "Rule")
|
||||
widths = (
|
||||
max(len(rule.endpoint) for rule in rules),
|
||||
max(len(methods) for methods in rule_methods),
|
||||
max(len(rule.rule) for rule in rules),
|
||||
)
|
||||
widths = [max(len(h), w) for h, w in zip(headers, widths)]
|
||||
row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths)
|
||||
|
||||
click.echo(row.format(*headers).strip())
|
||||
click.echo(row.format(*("-" * width for width in widths)))
|
||||
|
||||
for rule, methods in zip(rules, rule_methods):
|
||||
click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())
|
||||
|
||||
|
||||
cli = FlaskGroup(
|
||||
help="""\
|
||||
A general utility script for Flask applications.
|
||||
|
||||
Provides commands from Flask, extensions, and the application. Loads the
|
||||
application defined in the FLASK_APP environment variable, or from a wsgi.py
|
||||
file. Setting the FLASK_ENV environment variable to 'development' will enable
|
||||
debug mode.
|
||||
|
||||
\b
|
||||
{prefix}{cmd} FLASK_APP=hello.py
|
||||
{prefix}{cmd} FLASK_ENV=development
|
||||
{prefix}flask run
|
||||
""".format(
|
||||
cmd="export" if os.name == "posix" else "set",
|
||||
prefix="$ " if os.name == "posix" else "> ",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if int(click.__version__[0]) < 8:
|
||||
warnings.warn(
|
||||
"Using the `flask` cli with Click 7 is deprecated and"
|
||||
" will not be supported starting with Flask 2.1."
|
||||
" Please upgrade to Click 8 as soon as possible.",
|
||||
DeprecationWarning,
|
||||
)
|
||||
# TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
|
||||
cli.main(args=sys.argv[1:])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
295
dist/ba_data/python-site-packages/flask/config.py
vendored
Normal file
295
dist/ba_data/python-site-packages/flask/config.py
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
import errno
|
||||
import os
|
||||
import types
|
||||
import typing as t
|
||||
|
||||
from werkzeug.utils import import_string
|
||||
|
||||
|
||||
class ConfigAttribute:
|
||||
"""Makes an attribute forward to the config"""
|
||||
|
||||
def __init__(self, name: str, get_converter: t.Optional[t.Callable] = None) -> None:
|
||||
self.__name__ = name
|
||||
self.get_converter = get_converter
|
||||
|
||||
def __get__(self, obj: t.Any, owner: t.Any = None) -> t.Any:
|
||||
if obj is None:
|
||||
return self
|
||||
rv = obj.config[self.__name__]
|
||||
if self.get_converter is not None:
|
||||
rv = self.get_converter(rv)
|
||||
return rv
|
||||
|
||||
def __set__(self, obj: t.Any, value: t.Any) -> None:
|
||||
obj.config[self.__name__] = value
|
||||
|
||||
|
||||
class Config(dict):
|
||||
"""Works exactly like a dict but provides ways to fill it from files
|
||||
or special dictionaries. There are two common patterns to populate the
|
||||
config.
|
||||
|
||||
Either you can fill the config from a config file::
|
||||
|
||||
app.config.from_pyfile('yourconfig.cfg')
|
||||
|
||||
Or alternatively you can define the configuration options in the
|
||||
module that calls :meth:`from_object` or provide an import path to
|
||||
a module that should be loaded. It is also possible to tell it to
|
||||
use the same module and with that provide the configuration values
|
||||
just before the call::
|
||||
|
||||
DEBUG = True
|
||||
SECRET_KEY = 'development key'
|
||||
app.config.from_object(__name__)
|
||||
|
||||
In both cases (loading from any Python file or loading from modules),
|
||||
only uppercase keys are added to the config. This makes it possible to use
|
||||
lowercase values in the config file for temporary values that are not added
|
||||
to the config or to define the config keys in the same file that implements
|
||||
the application.
|
||||
|
||||
Probably the most interesting way to load configurations is from an
|
||||
environment variable pointing to a file::
|
||||
|
||||
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
|
||||
|
||||
In this case before launching the application you have to set this
|
||||
environment variable to the file you want to use. On Linux and OS X
|
||||
use the export statement::
|
||||
|
||||
export YOURAPPLICATION_SETTINGS='/path/to/config/file'
|
||||
|
||||
On windows use `set` instead.
|
||||
|
||||
:param root_path: path to which files are read relative from. When the
|
||||
config object is created by the application, this is
|
||||
the application's :attr:`~flask.Flask.root_path`.
|
||||
:param defaults: an optional dictionary of default values
|
||||
"""
|
||||
|
||||
def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
|
||||
dict.__init__(self, defaults or {})
|
||||
self.root_path = root_path
|
||||
|
||||
def from_envvar(self, variable_name: str, silent: bool = False) -> bool:
|
||||
"""Loads a configuration from an environment variable pointing to
|
||||
a configuration file. This is basically just a shortcut with nicer
|
||||
error messages for this line of code::
|
||||
|
||||
app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
|
||||
|
||||
:param variable_name: name of the environment variable
|
||||
:param silent: set to ``True`` if you want silent failure for missing
|
||||
files.
|
||||
:return: ``True`` if the file was loaded successfully.
|
||||
"""
|
||||
rv = os.environ.get(variable_name)
|
||||
if not rv:
|
||||
if silent:
|
||||
return False
|
||||
raise RuntimeError(
|
||||
f"The environment variable {variable_name!r} is not set"
|
||||
" and as such configuration could not be loaded. Set"
|
||||
" this variable and make it point to a configuration"
|
||||
" file"
|
||||
)
|
||||
return self.from_pyfile(rv, silent=silent)
|
||||
|
||||
def from_pyfile(self, filename: str, silent: bool = False) -> bool:
|
||||
"""Updates the values in the config from a Python file. This function
|
||||
behaves as if the file was imported as module with the
|
||||
:meth:`from_object` function.
|
||||
|
||||
:param filename: the filename of the config. This can either be an
|
||||
absolute filename or a filename relative to the
|
||||
root path.
|
||||
:param silent: set to ``True`` if you want silent failure for missing
|
||||
files.
|
||||
:return: ``True`` if the file was loaded successfully.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
`silent` parameter.
|
||||
"""
|
||||
filename = os.path.join(self.root_path, filename)
|
||||
d = types.ModuleType("config")
|
||||
d.__file__ = filename
|
||||
try:
|
||||
with open(filename, mode="rb") as config_file:
|
||||
exec(compile(config_file.read(), filename, "exec"), d.__dict__)
|
||||
except OSError as e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
|
||||
return False
|
||||
e.strerror = f"Unable to load configuration file ({e.strerror})"
|
||||
raise
|
||||
self.from_object(d)
|
||||
return True
|
||||
|
||||
def from_object(self, obj: t.Union[object, str]) -> None:
|
||||
"""Updates the values from the given object. An object can be of one
|
||||
of the following two types:
|
||||
|
||||
- a string: in this case the object with that name will be imported
|
||||
- an actual object reference: that object is used directly
|
||||
|
||||
Objects are usually either modules or classes. :meth:`from_object`
|
||||
loads only the uppercase attributes of the module/class. A ``dict``
|
||||
object will not work with :meth:`from_object` because the keys of a
|
||||
``dict`` are not attributes of the ``dict`` class.
|
||||
|
||||
Example of module-based configuration::
|
||||
|
||||
app.config.from_object('yourapplication.default_config')
|
||||
from yourapplication import default_config
|
||||
app.config.from_object(default_config)
|
||||
|
||||
Nothing is done to the object before loading. If the object is a
|
||||
class and has ``@property`` attributes, it needs to be
|
||||
instantiated before being passed to this method.
|
||||
|
||||
You should not use this function to load the actual configuration but
|
||||
rather configuration defaults. The actual config should be loaded
|
||||
with :meth:`from_pyfile` and ideally from a location not within the
|
||||
package because the package might be installed system wide.
|
||||
|
||||
See :ref:`config-dev-prod` for an example of class-based configuration
|
||||
using :meth:`from_object`.
|
||||
|
||||
:param obj: an import name or object
|
||||
"""
|
||||
if isinstance(obj, str):
|
||||
obj = import_string(obj)
|
||||
for key in dir(obj):
|
||||
if key.isupper():
|
||||
self[key] = getattr(obj, key)
|
||||
|
||||
def from_file(
|
||||
self,
|
||||
filename: str,
|
||||
load: t.Callable[[t.IO[t.Any]], t.Mapping],
|
||||
silent: bool = False,
|
||||
) -> bool:
|
||||
"""Update the values in the config from a file that is loaded
|
||||
using the ``load`` parameter. The loaded data is passed to the
|
||||
:meth:`from_mapping` method.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import toml
|
||||
app.config.from_file("config.toml", load=toml.load)
|
||||
|
||||
:param filename: The path to the data file. This can be an
|
||||
absolute path or relative to the config root path.
|
||||
:param load: A callable that takes a file handle and returns a
|
||||
mapping of loaded data from the file.
|
||||
:type load: ``Callable[[Reader], Mapping]`` where ``Reader``
|
||||
implements a ``read`` method.
|
||||
:param silent: Ignore the file if it doesn't exist.
|
||||
:return: ``True`` if the file was loaded successfully.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
filename = os.path.join(self.root_path, filename)
|
||||
|
||||
try:
|
||||
with open(filename) as f:
|
||||
obj = load(f)
|
||||
except OSError as e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||
return False
|
||||
|
||||
e.strerror = f"Unable to load configuration file ({e.strerror})"
|
||||
raise
|
||||
|
||||
return self.from_mapping(obj)
|
||||
|
||||
def from_json(self, filename: str, silent: bool = False) -> bool:
|
||||
"""Update the values in the config from a JSON file. The loaded
|
||||
data is passed to the :meth:`from_mapping` method.
|
||||
|
||||
:param filename: The path to the JSON file. This can be an
|
||||
absolute path or relative to the config root path.
|
||||
:param silent: Ignore the file if it doesn't exist.
|
||||
:return: ``True`` if the file was loaded successfully.
|
||||
|
||||
.. deprecated:: 2.0.0
|
||||
Will be removed in Flask 2.1. Use :meth:`from_file` instead.
|
||||
This was removed early in 2.0.0, was added back in 2.0.1.
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
import warnings
|
||||
from . import json
|
||||
|
||||
warnings.warn(
|
||||
"'from_json' is deprecated and will be removed in Flask"
|
||||
" 2.1. Use 'from_file(path, json.load)' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return self.from_file(filename, json.load, silent=silent)
|
||||
|
||||
def from_mapping(
|
||||
self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
|
||||
) -> bool:
|
||||
"""Updates the config like :meth:`update` ignoring items with non-upper
|
||||
keys.
|
||||
:return: Always returns ``True``.
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
mappings: t.Dict[str, t.Any] = {}
|
||||
if mapping is not None:
|
||||
mappings.update(mapping)
|
||||
mappings.update(kwargs)
|
||||
for key, value in mappings.items():
|
||||
if key.isupper():
|
||||
self[key] = value
|
||||
return True
|
||||
|
||||
def get_namespace(
|
||||
self, namespace: str, lowercase: bool = True, trim_namespace: bool = True
|
||||
) -> t.Dict[str, t.Any]:
|
||||
"""Returns a dictionary containing a subset of configuration options
|
||||
that match the specified namespace/prefix. Example usage::
|
||||
|
||||
app.config['IMAGE_STORE_TYPE'] = 'fs'
|
||||
app.config['IMAGE_STORE_PATH'] = '/var/app/images'
|
||||
app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
|
||||
image_store_config = app.config.get_namespace('IMAGE_STORE_')
|
||||
|
||||
The resulting dictionary `image_store_config` would look like::
|
||||
|
||||
{
|
||||
'type': 'fs',
|
||||
'path': '/var/app/images',
|
||||
'base_url': 'http://img.website.com'
|
||||
}
|
||||
|
||||
This is often useful when configuration options map directly to
|
||||
keyword arguments in functions or class constructors.
|
||||
|
||||
:param namespace: a configuration namespace
|
||||
:param lowercase: a flag indicating if the keys of the resulting
|
||||
dictionary should be lowercase
|
||||
:param trim_namespace: a flag indicating if the keys of the resulting
|
||||
dictionary should not include the namespace
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
rv = {}
|
||||
for k, v in self.items():
|
||||
if not k.startswith(namespace):
|
||||
continue
|
||||
if trim_namespace:
|
||||
key = k[len(namespace) :]
|
||||
else:
|
||||
key = k
|
||||
if lowercase:
|
||||
key = key.lower()
|
||||
rv[key] = v
|
||||
return rv
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{type(self).__name__} {dict.__repr__(self)}>"
|
||||
489
dist/ba_data/python-site-packages/flask/ctx.py
vendored
Normal file
489
dist/ba_data/python-site-packages/flask/ctx.py
vendored
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
import sys
|
||||
import typing as t
|
||||
from functools import update_wrapper
|
||||
from types import TracebackType
|
||||
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from .globals import _app_ctx_stack
|
||||
from .globals import _request_ctx_stack
|
||||
from .signals import appcontext_popped
|
||||
from .signals import appcontext_pushed
|
||||
from .typing import AfterRequestCallable
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
from .sessions import SessionMixin
|
||||
from .wrappers import Request
|
||||
|
||||
|
||||
# a singleton sentinel value for parameter defaults
|
||||
_sentinel = object()
|
||||
|
||||
|
||||
class _AppCtxGlobals:
|
||||
"""A plain object. Used as a namespace for storing data during an
|
||||
application context.
|
||||
|
||||
Creating an app context automatically creates this object, which is
|
||||
made available as the :data:`g` proxy.
|
||||
|
||||
.. describe:: 'key' in g
|
||||
|
||||
Check whether an attribute is present.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. describe:: iter(g)
|
||||
|
||||
Return an iterator over the attribute names.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
|
||||
# Define attr methods to let mypy know this is a namespace object
|
||||
# that has arbitrary attributes.
|
||||
|
||||
def __getattr__(self, name: str) -> t.Any:
|
||||
try:
|
||||
return self.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name) from None
|
||||
|
||||
def __setattr__(self, name: str, value: t.Any) -> None:
|
||||
self.__dict__[name] = value
|
||||
|
||||
def __delattr__(self, name: str) -> None:
|
||||
try:
|
||||
del self.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name) from None
|
||||
|
||||
def get(self, name: str, default: t.Optional[t.Any] = None) -> t.Any:
|
||||
"""Get an attribute by name, or a default value. Like
|
||||
:meth:`dict.get`.
|
||||
|
||||
:param name: Name of attribute to get.
|
||||
:param default: Value to return if the attribute is not present.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
return self.__dict__.get(name, default)
|
||||
|
||||
def pop(self, name: str, default: t.Any = _sentinel) -> t.Any:
|
||||
"""Get and remove an attribute by name. Like :meth:`dict.pop`.
|
||||
|
||||
:param name: Name of attribute to pop.
|
||||
:param default: Value to return if the attribute is not present,
|
||||
instead of raising a ``KeyError``.
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
if default is _sentinel:
|
||||
return self.__dict__.pop(name)
|
||||
else:
|
||||
return self.__dict__.pop(name, default)
|
||||
|
||||
def setdefault(self, name: str, default: t.Any = None) -> t.Any:
|
||||
"""Get the value of an attribute if it is present, otherwise
|
||||
set and return a default value. Like :meth:`dict.setdefault`.
|
||||
|
||||
:param name: Name of attribute to get.
|
||||
:param default: Value to set and return if the attribute is not
|
||||
present.
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
return self.__dict__.setdefault(name, default)
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
return item in self.__dict__
|
||||
|
||||
def __iter__(self) -> t.Iterator[str]:
|
||||
return iter(self.__dict__)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
top = _app_ctx_stack.top
|
||||
if top is not None:
|
||||
return f"<flask.g of {top.app.name!r}>"
|
||||
return object.__repr__(self)
|
||||
|
||||
|
||||
def after_this_request(f: AfterRequestCallable) -> AfterRequestCallable:
|
||||
"""Executes a function after this request. This is useful to modify
|
||||
response objects. The function is passed the response object and has
|
||||
to return the same or a new one.
|
||||
|
||||
Example::
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@after_this_request
|
||||
def add_header(response):
|
||||
response.headers['X-Foo'] = 'Parachute'
|
||||
return response
|
||||
return 'Hello World!'
|
||||
|
||||
This is more useful if a function other than the view function wants to
|
||||
modify a response. For instance think of a decorator that wants to add
|
||||
some headers without converting the return value into a response object.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
top = _request_ctx_stack.top
|
||||
|
||||
if top is None:
|
||||
raise RuntimeError(
|
||||
"This decorator can only be used when a request context is"
|
||||
" active, such as within a view function."
|
||||
)
|
||||
|
||||
top._after_request_functions.append(f)
|
||||
return f
|
||||
|
||||
|
||||
def copy_current_request_context(f: t.Callable) -> t.Callable:
|
||||
"""A helper function that decorates a function to retain the current
|
||||
request context. This is useful when working with greenlets. The moment
|
||||
the function is decorated a copy of the request context is created and
|
||||
then pushed when the function is called. The current session is also
|
||||
included in the copied request context.
|
||||
|
||||
Example::
|
||||
|
||||
import gevent
|
||||
from flask import copy_current_request_context
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
@copy_current_request_context
|
||||
def do_some_work():
|
||||
# do some work here, it can access flask.request or
|
||||
# flask.session like you would otherwise in the view function.
|
||||
...
|
||||
gevent.spawn(do_some_work)
|
||||
return 'Regular response'
|
||||
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
top = _request_ctx_stack.top
|
||||
|
||||
if top is None:
|
||||
raise RuntimeError(
|
||||
"This decorator can only be used when a request context is"
|
||||
" active, such as within a view function."
|
||||
)
|
||||
|
||||
reqctx = top.copy()
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
with reqctx:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return update_wrapper(wrapper, f)
|
||||
|
||||
|
||||
def has_request_context() -> bool:
|
||||
"""If you have code that wants to test if a request context is there or
|
||||
not this function can be used. For instance, you may want to take advantage
|
||||
of request information if the request object is available, but fail
|
||||
silently if it is unavailable.
|
||||
|
||||
::
|
||||
|
||||
class User(db.Model):
|
||||
|
||||
def __init__(self, username, remote_addr=None):
|
||||
self.username = username
|
||||
if remote_addr is None and has_request_context():
|
||||
remote_addr = request.remote_addr
|
||||
self.remote_addr = remote_addr
|
||||
|
||||
Alternatively you can also just test any of the context bound objects
|
||||
(such as :class:`request` or :class:`g`) for truthness::
|
||||
|
||||
class User(db.Model):
|
||||
|
||||
def __init__(self, username, remote_addr=None):
|
||||
self.username = username
|
||||
if remote_addr is None and request:
|
||||
remote_addr = request.remote_addr
|
||||
self.remote_addr = remote_addr
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
return _request_ctx_stack.top is not None
|
||||
|
||||
|
||||
def has_app_context() -> bool:
|
||||
"""Works like :func:`has_request_context` but for the application
|
||||
context. You can also just do a boolean check on the
|
||||
:data:`current_app` object instead.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
return _app_ctx_stack.top is not None
|
||||
|
||||
|
||||
class AppContext:
|
||||
"""The application context binds an application object implicitly
|
||||
to the current thread or greenlet, similar to how the
|
||||
:class:`RequestContext` binds request information. The application
|
||||
context is also implicitly created if a request context is created
|
||||
but the application is not on top of the individual application
|
||||
context.
|
||||
"""
|
||||
|
||||
def __init__(self, app: "Flask") -> None:
|
||||
self.app = app
|
||||
self.url_adapter = app.create_url_adapter(None)
|
||||
self.g = app.app_ctx_globals_class()
|
||||
|
||||
# Like request context, app contexts can be pushed multiple times
|
||||
# but there a basic "refcount" is enough to track them.
|
||||
self._refcnt = 0
|
||||
|
||||
def push(self) -> None:
|
||||
"""Binds the app context to the current context."""
|
||||
self._refcnt += 1
|
||||
_app_ctx_stack.push(self)
|
||||
appcontext_pushed.send(self.app)
|
||||
|
||||
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
|
||||
"""Pops the app context."""
|
||||
try:
|
||||
self._refcnt -= 1
|
||||
if self._refcnt <= 0:
|
||||
if exc is _sentinel:
|
||||
exc = sys.exc_info()[1]
|
||||
self.app.do_teardown_appcontext(exc)
|
||||
finally:
|
||||
rv = _app_ctx_stack.pop()
|
||||
assert rv is self, f"Popped wrong app context. ({rv!r} instead of {self!r})"
|
||||
appcontext_popped.send(self.app)
|
||||
|
||||
def __enter__(self) -> "AppContext":
|
||||
self.push()
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||
) -> None:
|
||||
self.pop(exc_value)
|
||||
|
||||
|
||||
class RequestContext:
|
||||
"""The request context contains all request relevant information. It is
|
||||
created at the beginning of the request and pushed to the
|
||||
`_request_ctx_stack` and removed at the end of it. It will create the
|
||||
URL adapter and request object for the WSGI environment provided.
|
||||
|
||||
Do not attempt to use this class directly, instead use
|
||||
:meth:`~flask.Flask.test_request_context` and
|
||||
:meth:`~flask.Flask.request_context` to create this object.
|
||||
|
||||
When the request context is popped, it will evaluate all the
|
||||
functions registered on the application for teardown execution
|
||||
(:meth:`~flask.Flask.teardown_request`).
|
||||
|
||||
The request context is automatically popped at the end of the request
|
||||
for you. In debug mode the request context is kept around if
|
||||
exceptions happen so that interactive debuggers have a chance to
|
||||
introspect the data. With 0.4 this can also be forced for requests
|
||||
that did not fail and outside of ``DEBUG`` mode. By setting
|
||||
``'flask._preserve_context'`` to ``True`` on the WSGI environment the
|
||||
context will not pop itself at the end of the request. This is used by
|
||||
the :meth:`~flask.Flask.test_client` for example to implement the
|
||||
deferred cleanup functionality.
|
||||
|
||||
You might find this helpful for unittests where you need the
|
||||
information from the context local around for a little longer. Make
|
||||
sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
|
||||
that situation, otherwise your unittests will leak memory.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
app: "Flask",
|
||||
environ: dict,
|
||||
request: t.Optional["Request"] = None,
|
||||
session: t.Optional["SessionMixin"] = None,
|
||||
) -> None:
|
||||
self.app = app
|
||||
if request is None:
|
||||
request = app.request_class(environ)
|
||||
self.request = request
|
||||
self.url_adapter = None
|
||||
try:
|
||||
self.url_adapter = app.create_url_adapter(self.request)
|
||||
except HTTPException as e:
|
||||
self.request.routing_exception = e
|
||||
self.flashes = None
|
||||
self.session = session
|
||||
|
||||
# Request contexts can be pushed multiple times and interleaved with
|
||||
# other request contexts. Now only if the last level is popped we
|
||||
# get rid of them. Additionally if an application context is missing
|
||||
# one is created implicitly so for each level we add this information
|
||||
self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = []
|
||||
|
||||
# indicator if the context was preserved. Next time another context
|
||||
# is pushed the preserved context is popped.
|
||||
self.preserved = False
|
||||
|
||||
# remembers the exception for pop if there is one in case the context
|
||||
# preservation kicks in.
|
||||
self._preserved_exc = None
|
||||
|
||||
# Functions that should be executed after the request on the response
|
||||
# object. These will be called before the regular "after_request"
|
||||
# functions.
|
||||
self._after_request_functions: t.List[AfterRequestCallable] = []
|
||||
|
||||
@property
|
||||
def g(self) -> AppContext:
|
||||
return _app_ctx_stack.top.g
|
||||
|
||||
@g.setter
|
||||
def g(self, value: AppContext) -> None:
|
||||
_app_ctx_stack.top.g = value
|
||||
|
||||
def copy(self) -> "RequestContext":
|
||||
"""Creates a copy of this request context with the same request object.
|
||||
This can be used to move a request context to a different greenlet.
|
||||
Because the actual request object is the same this cannot be used to
|
||||
move a request context to a different thread unless access to the
|
||||
request object is locked.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
The current session object is used instead of reloading the original
|
||||
data. This prevents `flask.session` pointing to an out-of-date object.
|
||||
"""
|
||||
return self.__class__(
|
||||
self.app,
|
||||
environ=self.request.environ,
|
||||
request=self.request,
|
||||
session=self.session,
|
||||
)
|
||||
|
||||
def match_request(self) -> None:
|
||||
"""Can be overridden by a subclass to hook into the matching
|
||||
of the request.
|
||||
"""
|
||||
try:
|
||||
result = self.url_adapter.match(return_rule=True) # type: ignore
|
||||
self.request.url_rule, self.request.view_args = result # type: ignore
|
||||
except HTTPException as e:
|
||||
self.request.routing_exception = e
|
||||
|
||||
def push(self) -> None:
|
||||
"""Binds the request context to the current context."""
|
||||
# If an exception occurs in debug mode or if context preservation is
|
||||
# activated under exception situations exactly one context stays
|
||||
# on the stack. The rationale is that you want to access that
|
||||
# information under debug situations. However if someone forgets to
|
||||
# pop that context again we want to make sure that on the next push
|
||||
# it's invalidated, otherwise we run at risk that something leaks
|
||||
# memory. This is usually only a problem in test suite since this
|
||||
# functionality is not active in production environments.
|
||||
top = _request_ctx_stack.top
|
||||
if top is not None and top.preserved:
|
||||
top.pop(top._preserved_exc)
|
||||
|
||||
# Before we push the request context we have to ensure that there
|
||||
# is an application context.
|
||||
app_ctx = _app_ctx_stack.top
|
||||
if app_ctx is None or app_ctx.app != self.app:
|
||||
app_ctx = self.app.app_context()
|
||||
app_ctx.push()
|
||||
self._implicit_app_ctx_stack.append(app_ctx)
|
||||
else:
|
||||
self._implicit_app_ctx_stack.append(None)
|
||||
|
||||
_request_ctx_stack.push(self)
|
||||
|
||||
# Open the session at the moment that the request context is available.
|
||||
# This allows a custom open_session method to use the request context.
|
||||
# Only open a new session if this is the first time the request was
|
||||
# pushed, otherwise stream_with_context loses the session.
|
||||
if self.session is None:
|
||||
session_interface = self.app.session_interface
|
||||
self.session = session_interface.open_session(self.app, self.request)
|
||||
|
||||
if self.session is None:
|
||||
self.session = session_interface.make_null_session(self.app)
|
||||
|
||||
# Match the request URL after loading the session, so that the
|
||||
# session is available in custom URL converters.
|
||||
if self.url_adapter is not None:
|
||||
self.match_request()
|
||||
|
||||
def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None: # type: ignore
|
||||
"""Pops the request context and unbinds it by doing that. This will
|
||||
also trigger the execution of functions registered by the
|
||||
:meth:`~flask.Flask.teardown_request` decorator.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
Added the `exc` argument.
|
||||
"""
|
||||
app_ctx = self._implicit_app_ctx_stack.pop()
|
||||
clear_request = False
|
||||
|
||||
try:
|
||||
if not self._implicit_app_ctx_stack:
|
||||
self.preserved = False
|
||||
self._preserved_exc = None
|
||||
if exc is _sentinel:
|
||||
exc = sys.exc_info()[1]
|
||||
self.app.do_teardown_request(exc)
|
||||
|
||||
request_close = getattr(self.request, "close", None)
|
||||
if request_close is not None:
|
||||
request_close()
|
||||
clear_request = True
|
||||
finally:
|
||||
rv = _request_ctx_stack.pop()
|
||||
|
||||
# get rid of circular dependencies at the end of the request
|
||||
# so that we don't require the GC to be active.
|
||||
if clear_request:
|
||||
rv.request.environ["werkzeug.request"] = None
|
||||
|
||||
# Get rid of the app as well if necessary.
|
||||
if app_ctx is not None:
|
||||
app_ctx.pop(exc)
|
||||
|
||||
assert (
|
||||
rv is self
|
||||
), f"Popped wrong request context. ({rv!r} instead of {self!r})"
|
||||
|
||||
def auto_pop(self, exc: t.Optional[BaseException]) -> None:
|
||||
if self.request.environ.get("flask._preserve_context") or (
|
||||
exc is not None and self.app.preserve_context_on_exception
|
||||
):
|
||||
self.preserved = True
|
||||
self._preserved_exc = exc # type: ignore
|
||||
else:
|
||||
self.pop(exc)
|
||||
|
||||
def __enter__(self) -> "RequestContext":
|
||||
self.push()
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||
) -> None:
|
||||
# do not pop the request stack if we are in debug mode and an
|
||||
# exception happened. This will allow the debugger to still
|
||||
# access the request object in the interactive shell. Furthermore
|
||||
# the context can be force kept alive for the test client.
|
||||
# See flask.testing for how this works.
|
||||
self.auto_pop(exc_value)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<{type(self).__name__} {self.request.url!r}"
|
||||
f" [{self.request.method}] of {self.app.name}>"
|
||||
)
|
||||
172
dist/ba_data/python-site-packages/flask/debughelpers.py
vendored
Normal file
172
dist/ba_data/python-site-packages/flask/debughelpers.py
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
import os
|
||||
import typing as t
|
||||
from warnings import warn
|
||||
|
||||
from .app import Flask
|
||||
from .blueprints import Blueprint
|
||||
from .globals import _request_ctx_stack
|
||||
|
||||
|
||||
class UnexpectedUnicodeError(AssertionError, UnicodeError):
|
||||
"""Raised in places where we want some better error reporting for
|
||||
unexpected unicode or binary data.
|
||||
"""
|
||||
|
||||
|
||||
class DebugFilesKeyError(KeyError, AssertionError):
|
||||
"""Raised from request.files during debugging. The idea is that it can
|
||||
provide a better error message than just a generic KeyError/BadRequest.
|
||||
"""
|
||||
|
||||
def __init__(self, request, key):
|
||||
form_matches = request.form.getlist(key)
|
||||
buf = [
|
||||
f"You tried to access the file {key!r} in the request.files"
|
||||
" dictionary but it does not exist. The mimetype for the"
|
||||
f" request is {request.mimetype!r} instead of"
|
||||
" 'multipart/form-data' which means that no file contents"
|
||||
" were transmitted. To fix this error you should provide"
|
||||
' enctype="multipart/form-data" in your form.'
|
||||
]
|
||||
if form_matches:
|
||||
names = ", ".join(repr(x) for x in form_matches)
|
||||
buf.append(
|
||||
"\n\nThe browser instead transmitted some file names. "
|
||||
f"This was submitted: {names}"
|
||||
)
|
||||
self.msg = "".join(buf)
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
|
||||
class FormDataRoutingRedirect(AssertionError):
|
||||
"""This exception is raised by Flask in debug mode if it detects a
|
||||
redirect caused by the routing system when the request method is not
|
||||
GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
|
||||
"""
|
||||
|
||||
def __init__(self, request):
|
||||
exc = request.routing_exception
|
||||
buf = [
|
||||
f"A request was sent to this URL ({request.url}) but a"
|
||||
" redirect was issued automatically by the routing system"
|
||||
f" to {exc.new_url!r}."
|
||||
]
|
||||
|
||||
# In case just a slash was appended we can be extra helpful
|
||||
if f"{request.base_url}/" == exc.new_url.split("?")[0]:
|
||||
buf.append(
|
||||
" The URL was defined with a trailing slash so Flask"
|
||||
" will automatically redirect to the URL with the"
|
||||
" trailing slash if it was accessed without one."
|
||||
)
|
||||
|
||||
buf.append(
|
||||
" Make sure to directly send your"
|
||||
f" {request.method}-request to this URL since we can't make"
|
||||
" browsers or HTTP clients redirect with form data reliably"
|
||||
" or without user interaction."
|
||||
)
|
||||
buf.append("\n\nNote: this exception is only raised in debug mode")
|
||||
AssertionError.__init__(self, "".join(buf).encode("utf-8"))
|
||||
|
||||
|
||||
def attach_enctype_error_multidict(request):
|
||||
"""Since Flask 0.8 we're monkeypatching the files object in case a
|
||||
request is detected that does not use multipart form data but the files
|
||||
object is accessed.
|
||||
"""
|
||||
oldcls = request.files.__class__
|
||||
|
||||
class newcls(oldcls):
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return oldcls.__getitem__(self, key)
|
||||
except KeyError as e:
|
||||
if key not in request.form:
|
||||
raise
|
||||
|
||||
raise DebugFilesKeyError(request, key) from e
|
||||
|
||||
newcls.__name__ = oldcls.__name__
|
||||
newcls.__module__ = oldcls.__module__
|
||||
request.files.__class__ = newcls
|
||||
|
||||
|
||||
def _dump_loader_info(loader) -> t.Generator:
|
||||
yield f"class: {type(loader).__module__}.{type(loader).__name__}"
|
||||
for key, value in sorted(loader.__dict__.items()):
|
||||
if key.startswith("_"):
|
||||
continue
|
||||
if isinstance(value, (tuple, list)):
|
||||
if not all(isinstance(x, str) for x in value):
|
||||
continue
|
||||
yield f"{key}:"
|
||||
for item in value:
|
||||
yield f" - {item}"
|
||||
continue
|
||||
elif not isinstance(value, (str, int, float, bool)):
|
||||
continue
|
||||
yield f"{key}: {value!r}"
|
||||
|
||||
|
||||
def explain_template_loading_attempts(app: Flask, template, attempts) -> None:
|
||||
"""This should help developers understand what failed"""
|
||||
info = [f"Locating template {template!r}:"]
|
||||
total_found = 0
|
||||
blueprint = None
|
||||
reqctx = _request_ctx_stack.top
|
||||
if reqctx is not None and reqctx.request.blueprint is not None:
|
||||
blueprint = reqctx.request.blueprint
|
||||
|
||||
for idx, (loader, srcobj, triple) in enumerate(attempts):
|
||||
if isinstance(srcobj, Flask):
|
||||
src_info = f"application {srcobj.import_name!r}"
|
||||
elif isinstance(srcobj, Blueprint):
|
||||
src_info = f"blueprint {srcobj.name!r} ({srcobj.import_name})"
|
||||
else:
|
||||
src_info = repr(srcobj)
|
||||
|
||||
info.append(f"{idx + 1:5}: trying loader of {src_info}")
|
||||
|
||||
for line in _dump_loader_info(loader):
|
||||
info.append(f" {line}")
|
||||
|
||||
if triple is None:
|
||||
detail = "no match"
|
||||
else:
|
||||
detail = f"found ({triple[1] or '<string>'!r})"
|
||||
total_found += 1
|
||||
info.append(f" -> {detail}")
|
||||
|
||||
seems_fishy = False
|
||||
if total_found == 0:
|
||||
info.append("Error: the template could not be found.")
|
||||
seems_fishy = True
|
||||
elif total_found > 1:
|
||||
info.append("Warning: multiple loaders returned a match for the template.")
|
||||
seems_fishy = True
|
||||
|
||||
if blueprint is not None and seems_fishy:
|
||||
info.append(
|
||||
" The template was looked up from an endpoint that belongs"
|
||||
f" to the blueprint {blueprint!r}."
|
||||
)
|
||||
info.append(" Maybe you did not place a template in the right folder?")
|
||||
info.append(" See https://flask.palletsprojects.com/blueprints/#templates")
|
||||
|
||||
app.logger.info("\n".join(info))
|
||||
|
||||
|
||||
def explain_ignored_app_run() -> None:
|
||||
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
||||
warn(
|
||||
Warning(
|
||||
"Silently ignoring app.run() because the application is"
|
||||
" run from the flask command line executable. Consider"
|
||||
' putting app.run() behind an if __name__ == "__main__"'
|
||||
" guard to silence this warning."
|
||||
),
|
||||
stacklevel=3,
|
||||
)
|
||||
59
dist/ba_data/python-site-packages/flask/globals.py
vendored
Normal file
59
dist/ba_data/python-site-packages/flask/globals.py
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import typing as t
|
||||
from functools import partial
|
||||
|
||||
from werkzeug.local import LocalProxy
|
||||
from werkzeug.local import LocalStack
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
from .ctx import _AppCtxGlobals
|
||||
from .sessions import SessionMixin
|
||||
from .wrappers import Request
|
||||
|
||||
_request_ctx_err_msg = """\
|
||||
Working outside of request context.
|
||||
|
||||
This typically means that you attempted to use functionality that needed
|
||||
an active HTTP request. Consult the documentation on testing for
|
||||
information about how to avoid this problem.\
|
||||
"""
|
||||
_app_ctx_err_msg = """\
|
||||
Working outside of application context.
|
||||
|
||||
This typically means that you attempted to use functionality that needed
|
||||
to interface with the current application object in some way. To solve
|
||||
this, set up an application context with app.app_context(). See the
|
||||
documentation for more information.\
|
||||
"""
|
||||
|
||||
|
||||
def _lookup_req_object(name):
|
||||
top = _request_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError(_request_ctx_err_msg)
|
||||
return getattr(top, name)
|
||||
|
||||
|
||||
def _lookup_app_object(name):
|
||||
top = _app_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError(_app_ctx_err_msg)
|
||||
return getattr(top, name)
|
||||
|
||||
|
||||
def _find_app():
|
||||
top = _app_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError(_app_ctx_err_msg)
|
||||
return top.app
|
||||
|
||||
|
||||
# context locals
|
||||
_request_ctx_stack = LocalStack()
|
||||
_app_ctx_stack = LocalStack()
|
||||
current_app: "Flask" = LocalProxy(_find_app) # type: ignore
|
||||
request: "Request" = LocalProxy(partial(_lookup_req_object, "request")) # type: ignore
|
||||
session: "SessionMixin" = LocalProxy( # type: ignore
|
||||
partial(_lookup_req_object, "session")
|
||||
)
|
||||
g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g")) # type: ignore
|
||||
836
dist/ba_data/python-site-packages/flask/helpers.py
vendored
Normal file
836
dist/ba_data/python-site-packages/flask/helpers.py
vendored
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
import os
|
||||
import pkgutil
|
||||
import socket
|
||||
import sys
|
||||
import typing as t
|
||||
import warnings
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from functools import lru_cache
|
||||
from functools import update_wrapper
|
||||
from threading import RLock
|
||||
|
||||
import werkzeug.utils
|
||||
from werkzeug.exceptions import NotFound
|
||||
from werkzeug.routing import BuildError
|
||||
from werkzeug.urls import url_quote
|
||||
|
||||
from .globals import _app_ctx_stack
|
||||
from .globals import _request_ctx_stack
|
||||
from .globals import current_app
|
||||
from .globals import request
|
||||
from .globals import session
|
||||
from .signals import message_flashed
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .wrappers import Response
|
||||
|
||||
|
||||
def get_env() -> str:
|
||||
"""Get the environment the app is running in, indicated by the
|
||||
:envvar:`FLASK_ENV` environment variable. The default is
|
||||
``'production'``.
|
||||
"""
|
||||
return os.environ.get("FLASK_ENV") or "production"
|
||||
|
||||
|
||||
def get_debug_flag() -> bool:
|
||||
"""Get whether debug mode should be enabled for the app, indicated
|
||||
by the :envvar:`FLASK_DEBUG` environment variable. The default is
|
||||
``True`` if :func:`.get_env` returns ``'development'``, or ``False``
|
||||
otherwise.
|
||||
"""
|
||||
val = os.environ.get("FLASK_DEBUG")
|
||||
|
||||
if not val:
|
||||
return get_env() == "development"
|
||||
|
||||
return val.lower() not in ("0", "false", "no")
|
||||
|
||||
|
||||
def get_load_dotenv(default: bool = True) -> bool:
|
||||
"""Get whether the user has disabled loading dotenv files by setting
|
||||
:envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
|
||||
files.
|
||||
|
||||
:param default: What to return if the env var isn't set.
|
||||
"""
|
||||
val = os.environ.get("FLASK_SKIP_DOTENV")
|
||||
|
||||
if not val:
|
||||
return default
|
||||
|
||||
return val.lower() in ("0", "false", "no")
|
||||
|
||||
|
||||
def stream_with_context(
|
||||
generator_or_function: t.Union[
|
||||
t.Iterator[t.AnyStr], t.Callable[..., t.Iterator[t.AnyStr]]
|
||||
]
|
||||
) -> t.Iterator[t.AnyStr]:
|
||||
"""Request contexts disappear when the response is started on the server.
|
||||
This is done for efficiency reasons and to make it less likely to encounter
|
||||
memory leaks with badly written WSGI middlewares. The downside is that if
|
||||
you are using streamed responses, the generator cannot access request bound
|
||||
information any more.
|
||||
|
||||
This function however can help you keep the context around for longer::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
@stream_with_context
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(generate())
|
||||
|
||||
Alternatively it can also be used around a specific generator::
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(stream_with_context(generate()))
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
try:
|
||||
gen = iter(generator_or_function) # type: ignore
|
||||
except TypeError:
|
||||
|
||||
def decorator(*args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
gen = generator_or_function(*args, **kwargs) # type: ignore
|
||||
return stream_with_context(gen)
|
||||
|
||||
return update_wrapper(decorator, generator_or_function) # type: ignore
|
||||
|
||||
def generator() -> t.Generator:
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is None:
|
||||
raise RuntimeError(
|
||||
"Attempted to stream with context but "
|
||||
"there was no context in the first place to keep around."
|
||||
)
|
||||
with ctx:
|
||||
# Dummy sentinel. Has to be inside the context block or we're
|
||||
# not actually keeping the context around.
|
||||
yield None
|
||||
|
||||
# The try/finally is here so that if someone passes a WSGI level
|
||||
# iterator in we're still running the cleanup logic. Generators
|
||||
# don't need that because they are closed on their destruction
|
||||
# automatically.
|
||||
try:
|
||||
yield from gen
|
||||
finally:
|
||||
if hasattr(gen, "close"):
|
||||
gen.close() # type: ignore
|
||||
|
||||
# The trick is to start the generator. Then the code execution runs until
|
||||
# the first dummy None is yielded at which point the context was already
|
||||
# pushed. This item is discarded. Then when the iteration continues the
|
||||
# real generator is executed.
|
||||
wrapped_g = generator()
|
||||
next(wrapped_g)
|
||||
return wrapped_g
|
||||
|
||||
|
||||
def make_response(*args: t.Any) -> "Response":
|
||||
"""Sometimes it is necessary to set additional headers in a view. Because
|
||||
views do not have to return response objects but can return a value that
|
||||
is converted into a response object by Flask itself, it becomes tricky to
|
||||
add headers to it. This function can be called instead of using a return
|
||||
and you will get a response object which you can use to attach headers.
|
||||
|
||||
If view looked like this and you want to add a new header::
|
||||
|
||||
def index():
|
||||
return render_template('index.html', foo=42)
|
||||
|
||||
You can now do something like this::
|
||||
|
||||
def index():
|
||||
response = make_response(render_template('index.html', foo=42))
|
||||
response.headers['X-Parachutes'] = 'parachutes are cool'
|
||||
return response
|
||||
|
||||
This function accepts the very same arguments you can return from a
|
||||
view function. This for example creates a response with a 404 error
|
||||
code::
|
||||
|
||||
response = make_response(render_template('not_found.html'), 404)
|
||||
|
||||
The other use case of this function is to force the return value of a
|
||||
view function into a response which is helpful with view
|
||||
decorators::
|
||||
|
||||
response = make_response(view_function())
|
||||
response.headers['X-Parachutes'] = 'parachutes are cool'
|
||||
|
||||
Internally this function does the following things:
|
||||
|
||||
- if no arguments are passed, it creates a new response argument
|
||||
- if one argument is passed, :meth:`flask.Flask.make_response`
|
||||
is invoked with it.
|
||||
- if more than one argument is passed, the arguments are passed
|
||||
to the :meth:`flask.Flask.make_response` function as tuple.
|
||||
|
||||
.. versionadded:: 0.6
|
||||
"""
|
||||
if not args:
|
||||
return current_app.response_class()
|
||||
if len(args) == 1:
|
||||
args = args[0]
|
||||
return current_app.make_response(args)
|
||||
|
||||
|
||||
def url_for(endpoint: str, **values: t.Any) -> str:
|
||||
"""Generates a URL to the given endpoint with the method provided.
|
||||
|
||||
Variable arguments that are unknown to the target endpoint are appended
|
||||
to the generated URL as query arguments. If the value of a query argument
|
||||
is ``None``, the whole pair is skipped. In case blueprints are active
|
||||
you can shortcut references to the same blueprint by prefixing the
|
||||
local endpoint with a dot (``.``).
|
||||
|
||||
This will reference the index function local to the current blueprint::
|
||||
|
||||
url_for('.index')
|
||||
|
||||
See :ref:`url-building`.
|
||||
|
||||
Configuration values ``APPLICATION_ROOT`` and ``SERVER_NAME`` are only used when
|
||||
generating URLs outside of a request context.
|
||||
|
||||
To integrate applications, :class:`Flask` has a hook to intercept URL build
|
||||
errors through :attr:`Flask.url_build_error_handlers`. The `url_for`
|
||||
function results in a :exc:`~werkzeug.routing.BuildError` when the current
|
||||
app does not have a URL for the given endpoint and values. When it does, the
|
||||
:data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
|
||||
it is not ``None``, which can return a string to use as the result of
|
||||
`url_for` (instead of `url_for`'s default to raise the
|
||||
:exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
|
||||
An example::
|
||||
|
||||
def external_url_handler(error, endpoint, values):
|
||||
"Looks up an external URL when `url_for` cannot build a URL."
|
||||
# This is an example of hooking the build_error_handler.
|
||||
# Here, lookup_url is some utility function you've built
|
||||
# which looks up the endpoint in some external URL registry.
|
||||
url = lookup_url(endpoint, **values)
|
||||
if url is None:
|
||||
# External lookup did not have a URL.
|
||||
# Re-raise the BuildError, in context of original traceback.
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
if exc_value is error:
|
||||
raise exc_type(exc_value).with_traceback(tb)
|
||||
else:
|
||||
raise error
|
||||
# url_for will use this result, instead of raising BuildError.
|
||||
return url
|
||||
|
||||
app.url_build_error_handlers.append(external_url_handler)
|
||||
|
||||
Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
|
||||
`endpoint` and `values` are the arguments passed into `url_for`. Note
|
||||
that this is for building URLs outside the current application, and not for
|
||||
handling 404 NotFound errors.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
The `_scheme` parameter was added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
The `_anchor` and `_method` parameters were added.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
Calls :meth:`Flask.handle_build_error` on
|
||||
:exc:`~werkzeug.routing.BuildError`.
|
||||
|
||||
:param endpoint: the endpoint of the URL (name of the function)
|
||||
:param values: the variable arguments of the URL rule
|
||||
:param _external: if set to ``True``, an absolute URL is generated. Server
|
||||
address can be changed via ``SERVER_NAME`` configuration variable which
|
||||
falls back to the `Host` header, then to the IP and port of the request.
|
||||
:param _scheme: a string specifying the desired URL scheme. The `_external`
|
||||
parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
|
||||
behavior uses the same scheme as the current request, or
|
||||
:data:`PREFERRED_URL_SCHEME` if no request context is available.
|
||||
This also can be set to an empty string to build protocol-relative
|
||||
URLs.
|
||||
:param _anchor: if provided this is added as anchor to the URL.
|
||||
:param _method: if provided this explicitly specifies an HTTP method.
|
||||
"""
|
||||
appctx = _app_ctx_stack.top
|
||||
reqctx = _request_ctx_stack.top
|
||||
|
||||
if appctx is None:
|
||||
raise RuntimeError(
|
||||
"Attempted to generate a URL without the application context being"
|
||||
" pushed. This has to be executed when application context is"
|
||||
" available."
|
||||
)
|
||||
|
||||
# If request specific information is available we have some extra
|
||||
# features that support "relative" URLs.
|
||||
if reqctx is not None:
|
||||
url_adapter = reqctx.url_adapter
|
||||
blueprint_name = request.blueprint
|
||||
|
||||
if endpoint[:1] == ".":
|
||||
if blueprint_name is not None:
|
||||
endpoint = f"{blueprint_name}{endpoint}"
|
||||
else:
|
||||
endpoint = endpoint[1:]
|
||||
|
||||
external = values.pop("_external", False)
|
||||
|
||||
# Otherwise go with the url adapter from the appctx and make
|
||||
# the URLs external by default.
|
||||
else:
|
||||
url_adapter = appctx.url_adapter
|
||||
|
||||
if url_adapter is None:
|
||||
raise RuntimeError(
|
||||
"Application was not able to create a URL adapter for request"
|
||||
" independent URL generation. You might be able to fix this by"
|
||||
" setting the SERVER_NAME config variable."
|
||||
)
|
||||
|
||||
external = values.pop("_external", True)
|
||||
|
||||
anchor = values.pop("_anchor", None)
|
||||
method = values.pop("_method", None)
|
||||
scheme = values.pop("_scheme", None)
|
||||
appctx.app.inject_url_defaults(endpoint, values)
|
||||
|
||||
# This is not the best way to deal with this but currently the
|
||||
# underlying Werkzeug router does not support overriding the scheme on
|
||||
# a per build call basis.
|
||||
old_scheme = None
|
||||
if scheme is not None:
|
||||
if not external:
|
||||
raise ValueError("When specifying _scheme, _external must be True")
|
||||
old_scheme = url_adapter.url_scheme
|
||||
url_adapter.url_scheme = scheme
|
||||
|
||||
try:
|
||||
try:
|
||||
rv = url_adapter.build(
|
||||
endpoint, values, method=method, force_external=external
|
||||
)
|
||||
finally:
|
||||
if old_scheme is not None:
|
||||
url_adapter.url_scheme = old_scheme
|
||||
except BuildError as error:
|
||||
# We need to inject the values again so that the app callback can
|
||||
# deal with that sort of stuff.
|
||||
values["_external"] = external
|
||||
values["_anchor"] = anchor
|
||||
values["_method"] = method
|
||||
values["_scheme"] = scheme
|
||||
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||
|
||||
if anchor is not None:
|
||||
rv += f"#{url_quote(anchor)}"
|
||||
return rv
|
||||
|
||||
|
||||
def get_template_attribute(template_name: str, attribute: str) -> t.Any:
|
||||
"""Loads a macro (or variable) a template exports. This can be used to
|
||||
invoke a macro from within Python code. If you for example have a
|
||||
template named :file:`_cider.html` with the following contents:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% macro hello(name) %}Hello {{ name }}!{% endmacro %}
|
||||
|
||||
You can access this from Python code like this::
|
||||
|
||||
hello = get_template_attribute('_cider.html', 'hello')
|
||||
return hello('World')
|
||||
|
||||
.. versionadded:: 0.2
|
||||
|
||||
:param template_name: the name of the template
|
||||
:param attribute: the name of the variable of macro to access
|
||||
"""
|
||||
return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
|
||||
|
||||
|
||||
def flash(message: str, category: str = "message") -> None:
|
||||
"""Flashes a message to the next request. In order to remove the
|
||||
flashed message from the session and to display it to the user,
|
||||
the template has to call :func:`get_flashed_messages`.
|
||||
|
||||
.. versionchanged:: 0.3
|
||||
`category` parameter added.
|
||||
|
||||
:param message: the message to be flashed.
|
||||
:param category: the category for the message. The following values
|
||||
are recommended: ``'message'`` for any kind of message,
|
||||
``'error'`` for errors, ``'info'`` for information
|
||||
messages and ``'warning'`` for warnings. However any
|
||||
kind of string can be used as category.
|
||||
"""
|
||||
# Original implementation:
|
||||
#
|
||||
# session.setdefault('_flashes', []).append((category, message))
|
||||
#
|
||||
# This assumed that changes made to mutable structures in the session are
|
||||
# always in sync with the session object, which is not true for session
|
||||
# implementations that use external storage for keeping their keys/values.
|
||||
flashes = session.get("_flashes", [])
|
||||
flashes.append((category, message))
|
||||
session["_flashes"] = flashes
|
||||
message_flashed.send(
|
||||
current_app._get_current_object(), # type: ignore
|
||||
message=message,
|
||||
category=category,
|
||||
)
|
||||
|
||||
|
||||
def get_flashed_messages(
|
||||
with_categories: bool = False, category_filter: t.Iterable[str] = ()
|
||||
) -> t.Union[t.List[str], t.List[t.Tuple[str, str]]]:
|
||||
"""Pulls all flashed messages from the session and returns them.
|
||||
Further calls in the same request to the function will return
|
||||
the same messages. By default just the messages are returned,
|
||||
but when `with_categories` is set to ``True``, the return value will
|
||||
be a list of tuples in the form ``(category, message)`` instead.
|
||||
|
||||
Filter the flashed messages to one or more categories by providing those
|
||||
categories in `category_filter`. This allows rendering categories in
|
||||
separate html blocks. The `with_categories` and `category_filter`
|
||||
arguments are distinct:
|
||||
|
||||
* `with_categories` controls whether categories are returned with message
|
||||
text (``True`` gives a tuple, where ``False`` gives just the message text).
|
||||
* `category_filter` filters the messages down to only those matching the
|
||||
provided categories.
|
||||
|
||||
See :doc:`/patterns/flashing` for examples.
|
||||
|
||||
.. versionchanged:: 0.3
|
||||
`with_categories` parameter added.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
`category_filter` parameter added.
|
||||
|
||||
:param with_categories: set to ``True`` to also receive categories.
|
||||
:param category_filter: filter of categories to limit return values. Only
|
||||
categories in the list will be returned.
|
||||
"""
|
||||
flashes = _request_ctx_stack.top.flashes
|
||||
if flashes is None:
|
||||
_request_ctx_stack.top.flashes = flashes = (
|
||||
session.pop("_flashes") if "_flashes" in session else []
|
||||
)
|
||||
if category_filter:
|
||||
flashes = list(filter(lambda f: f[0] in category_filter, flashes))
|
||||
if not with_categories:
|
||||
return [x[1] for x in flashes]
|
||||
return flashes
|
||||
|
||||
|
||||
def _prepare_send_file_kwargs(
|
||||
download_name: t.Optional[str] = None,
|
||||
attachment_filename: t.Optional[str] = None,
|
||||
etag: t.Optional[t.Union[bool, str]] = None,
|
||||
add_etags: t.Optional[t.Union[bool]] = None,
|
||||
max_age: t.Optional[
|
||||
t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
|
||||
] = None,
|
||||
cache_timeout: t.Optional[int] = None,
|
||||
**kwargs: t.Any,
|
||||
) -> t.Dict[str, t.Any]:
|
||||
if attachment_filename is not None:
|
||||
warnings.warn(
|
||||
"The 'attachment_filename' parameter has been renamed to"
|
||||
" 'download_name'. The old name will be removed in Flask"
|
||||
" 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
download_name = attachment_filename
|
||||
|
||||
if cache_timeout is not None:
|
||||
warnings.warn(
|
||||
"The 'cache_timeout' parameter has been renamed to"
|
||||
" 'max_age'. The old name will be removed in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
max_age = cache_timeout
|
||||
|
||||
if add_etags is not None:
|
||||
warnings.warn(
|
||||
"The 'add_etags' parameter has been renamed to 'etag'. The"
|
||||
" old name will be removed in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
etag = add_etags
|
||||
|
||||
if max_age is None:
|
||||
max_age = current_app.get_send_file_max_age
|
||||
|
||||
kwargs.update(
|
||||
environ=request.environ,
|
||||
download_name=download_name,
|
||||
etag=etag,
|
||||
max_age=max_age,
|
||||
use_x_sendfile=current_app.use_x_sendfile,
|
||||
response_class=current_app.response_class,
|
||||
_root_path=current_app.root_path, # type: ignore
|
||||
)
|
||||
return kwargs
|
||||
|
||||
|
||||
def send_file(
|
||||
path_or_file: t.Union[os.PathLike, str, t.BinaryIO],
|
||||
mimetype: t.Optional[str] = None,
|
||||
as_attachment: bool = False,
|
||||
download_name: t.Optional[str] = None,
|
||||
attachment_filename: t.Optional[str] = None,
|
||||
conditional: bool = True,
|
||||
etag: t.Union[bool, str] = True,
|
||||
add_etags: t.Optional[bool] = None,
|
||||
last_modified: t.Optional[t.Union[datetime, int, float]] = None,
|
||||
max_age: t.Optional[
|
||||
t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
|
||||
] = None,
|
||||
cache_timeout: t.Optional[int] = None,
|
||||
):
|
||||
"""Send the contents of a file to the client.
|
||||
|
||||
The first argument can be a file path or a file-like object. Paths
|
||||
are preferred in most cases because Werkzeug can manage the file and
|
||||
get extra information from the path. Passing a file-like object
|
||||
requires that the file is opened in binary mode, and is mostly
|
||||
useful when building a file in memory with :class:`io.BytesIO`.
|
||||
|
||||
Never pass file paths provided by a user. The path is assumed to be
|
||||
trusted, so a user could craft a path to access a file you didn't
|
||||
intend. Use :func:`send_from_directory` to safely serve
|
||||
user-requested paths from within a directory.
|
||||
|
||||
If the WSGI server sets a ``file_wrapper`` in ``environ``, it is
|
||||
used, otherwise Werkzeug's built-in wrapper is used. Alternatively,
|
||||
if the HTTP server supports ``X-Sendfile``, configuring Flask with
|
||||
``USE_X_SENDFILE = True`` will tell the server to send the given
|
||||
path, which is much more efficient than reading it in Python.
|
||||
|
||||
:param path_or_file: The path to the file to send, relative to the
|
||||
current working directory if a relative path is given.
|
||||
Alternatively, a file-like object opened in binary mode. Make
|
||||
sure the file pointer is seeked to the start of the data.
|
||||
:param mimetype: The MIME type to send for the file. If not
|
||||
provided, it will try to detect it from the file name.
|
||||
:param as_attachment: Indicate to a browser that it should offer to
|
||||
save the file instead of displaying it.
|
||||
:param download_name: The default name browsers will use when saving
|
||||
the file. Defaults to the passed file name.
|
||||
:param conditional: Enable conditional and range responses based on
|
||||
request headers. Requires passing a file path and ``environ``.
|
||||
:param etag: Calculate an ETag for the file, which requires passing
|
||||
a file path. Can also be a string to use instead.
|
||||
:param last_modified: The last modified time to send for the file,
|
||||
in seconds. If not provided, it will try to detect it from the
|
||||
file path.
|
||||
:param max_age: How long the client should cache the file, in
|
||||
seconds. If set, ``Cache-Control`` will be ``public``, otherwise
|
||||
it will be ``no-cache`` to prefer conditional caching.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``download_name`` replaces the ``attachment_filename``
|
||||
parameter. If ``as_attachment=False``, it is passed with
|
||||
``Content-Disposition: inline`` instead.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``max_age`` replaces the ``cache_timeout`` parameter.
|
||||
``conditional`` is enabled and ``max_age`` is not set by
|
||||
default.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``etag`` replaces the ``add_etags`` parameter. It can be a
|
||||
string to use instead of generating one.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Passing a file-like object that inherits from
|
||||
:class:`~io.TextIOBase` will raise a :exc:`ValueError` rather
|
||||
than sending an empty file.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
Moved the implementation to Werkzeug. This is now a wrapper to
|
||||
pass some Flask-specific arguments.
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
``filename`` may be a :class:`~os.PathLike` object.
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
Passing a :class:`~io.BytesIO` object supports range requests.
|
||||
|
||||
.. versionchanged:: 1.0.3
|
||||
Filenames are encoded with ASCII instead of Latin-1 for broader
|
||||
compatibility with WSGI servers.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
UTF-8 filenames as specified in :rfc:`2231` are supported.
|
||||
|
||||
.. versionchanged:: 0.12
|
||||
The filename is no longer automatically inferred from file
|
||||
objects. If you want to use automatic MIME and etag support,
|
||||
pass a filename via ``filename_or_fp`` or
|
||||
``attachment_filename``.
|
||||
|
||||
.. versionchanged:: 0.12
|
||||
``attachment_filename`` is preferred over ``filename`` for MIME
|
||||
detection.
|
||||
|
||||
.. versionchanged:: 0.9
|
||||
``cache_timeout`` defaults to
|
||||
:meth:`Flask.get_send_file_max_age`.
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
MIME guessing and etag support for file-like objects was
|
||||
deprecated because it was unreliable. Pass a filename if you are
|
||||
able to, otherwise attach an etag yourself.
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
The ``add_etags``, ``cache_timeout`` and ``conditional``
|
||||
parameters were added. The default behavior is to add etags.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
"""
|
||||
return werkzeug.utils.send_file(
|
||||
**_prepare_send_file_kwargs(
|
||||
path_or_file=path_or_file,
|
||||
environ=request.environ,
|
||||
mimetype=mimetype,
|
||||
as_attachment=as_attachment,
|
||||
download_name=download_name,
|
||||
attachment_filename=attachment_filename,
|
||||
conditional=conditional,
|
||||
etag=etag,
|
||||
add_etags=add_etags,
|
||||
last_modified=last_modified,
|
||||
max_age=max_age,
|
||||
cache_timeout=cache_timeout,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def safe_join(directory: str, *pathnames: str) -> str:
|
||||
"""Safely join zero or more untrusted path components to a base
|
||||
directory to avoid escaping the base directory.
|
||||
|
||||
:param directory: The trusted base directory.
|
||||
:param pathnames: The untrusted path components relative to the
|
||||
base directory.
|
||||
:return: A safe path, otherwise ``None``.
|
||||
"""
|
||||
warnings.warn(
|
||||
"'flask.helpers.safe_join' is deprecated and will be removed in"
|
||||
" Flask 2.1. Use 'werkzeug.utils.safe_join' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
path = werkzeug.utils.safe_join(directory, *pathnames)
|
||||
|
||||
if path is None:
|
||||
raise NotFound()
|
||||
|
||||
return path
|
||||
|
||||
|
||||
def send_from_directory(
|
||||
directory: t.Union[os.PathLike, str],
|
||||
path: t.Union[os.PathLike, str],
|
||||
filename: t.Optional[str] = None,
|
||||
**kwargs: t.Any,
|
||||
) -> "Response":
|
||||
"""Send a file from within a directory using :func:`send_file`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/uploads/<path:name>")
|
||||
def download_file(name):
|
||||
return send_from_directory(
|
||||
app.config['UPLOAD_FOLDER'], name, as_attachment=True
|
||||
)
|
||||
|
||||
This is a secure way to serve files from a folder, such as static
|
||||
files or uploads. Uses :func:`~werkzeug.security.safe_join` to
|
||||
ensure the path coming from the client is not maliciously crafted to
|
||||
point outside the specified directory.
|
||||
|
||||
If the final path does not point to an existing regular file,
|
||||
raises a 404 :exc:`~werkzeug.exceptions.NotFound` error.
|
||||
|
||||
:param directory: The directory that ``path`` must be located under.
|
||||
:param path: The path to the file to send, relative to
|
||||
``directory``.
|
||||
:param kwargs: Arguments to pass to :func:`send_file`.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``path`` replaces the ``filename`` parameter.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
Moved the implementation to Werkzeug. This is now a wrapper to
|
||||
pass some Flask-specific arguments.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if filename is not None:
|
||||
warnings.warn(
|
||||
"The 'filename' parameter has been renamed to 'path'. The"
|
||||
" old name will be removed in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
path = filename
|
||||
|
||||
return werkzeug.utils.send_from_directory( # type: ignore
|
||||
directory, path, **_prepare_send_file_kwargs(**kwargs)
|
||||
)
|
||||
|
||||
|
||||
def get_root_path(import_name: str) -> str:
|
||||
"""Find the root path of a package, or the path that contains a
|
||||
module. If it cannot be found, returns the current working
|
||||
directory.
|
||||
|
||||
Not to be confused with the value returned by :func:`find_package`.
|
||||
|
||||
:meta private:
|
||||
"""
|
||||
# Module already imported and has a file attribute. Use that first.
|
||||
mod = sys.modules.get(import_name)
|
||||
|
||||
if mod is not None and hasattr(mod, "__file__") and mod.__file__ is not None:
|
||||
return os.path.dirname(os.path.abspath(mod.__file__))
|
||||
|
||||
# Next attempt: check the loader.
|
||||
loader = pkgutil.get_loader(import_name)
|
||||
|
||||
# Loader does not exist or we're referring to an unloaded main
|
||||
# module or a main module without path (interactive sessions), go
|
||||
# with the current working directory.
|
||||
if loader is None or import_name == "__main__":
|
||||
return os.getcwd()
|
||||
|
||||
if hasattr(loader, "get_filename"):
|
||||
filepath = loader.get_filename(import_name) # type: ignore
|
||||
else:
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
mod = sys.modules[import_name]
|
||||
filepath = getattr(mod, "__file__", None)
|
||||
|
||||
# If we don't have a file path it might be because it is a
|
||||
# namespace package. In this case pick the root path from the
|
||||
# first module that is contained in the package.
|
||||
if filepath is None:
|
||||
raise RuntimeError(
|
||||
"No root path can be found for the provided module"
|
||||
f" {import_name!r}. This can happen because the module"
|
||||
" came from an import hook that does not provide file"
|
||||
" name information or because it's a namespace package."
|
||||
" In this case the root path needs to be explicitly"
|
||||
" provided."
|
||||
)
|
||||
|
||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||
return os.path.dirname(os.path.abspath(filepath))
|
||||
|
||||
|
||||
class locked_cached_property(werkzeug.utils.cached_property):
|
||||
"""A :func:`property` that is only evaluated once. Like
|
||||
:class:`werkzeug.utils.cached_property` except access uses a lock
|
||||
for thread safety.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Inherits from Werkzeug's ``cached_property`` (and ``property``).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
fget: t.Callable[[t.Any], t.Any],
|
||||
name: t.Optional[str] = None,
|
||||
doc: t.Optional[str] = None,
|
||||
) -> None:
|
||||
super().__init__(fget, name=name, doc=doc)
|
||||
self.lock = RLock()
|
||||
|
||||
def __get__(self, obj: object, type: type = None) -> t.Any: # type: ignore
|
||||
if obj is None:
|
||||
return self
|
||||
|
||||
with self.lock:
|
||||
return super().__get__(obj, type=type)
|
||||
|
||||
def __set__(self, obj: object, value: t.Any) -> None:
|
||||
with self.lock:
|
||||
super().__set__(obj, value)
|
||||
|
||||
def __delete__(self, obj: object) -> None:
|
||||
with self.lock:
|
||||
super().__delete__(obj)
|
||||
|
||||
|
||||
def total_seconds(td: timedelta) -> int:
|
||||
"""Returns the total seconds from a timedelta object.
|
||||
|
||||
:param timedelta td: the timedelta to be converted in seconds
|
||||
|
||||
:returns: number of seconds
|
||||
:rtype: int
|
||||
|
||||
.. deprecated:: 2.0
|
||||
Will be removed in Flask 2.1. Use
|
||||
:meth:`timedelta.total_seconds` instead.
|
||||
"""
|
||||
warnings.warn(
|
||||
"'total_seconds' is deprecated and will be removed in Flask"
|
||||
" 2.1. Use 'timedelta.total_seconds' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return td.days * 60 * 60 * 24 + td.seconds
|
||||
|
||||
|
||||
def is_ip(value: str) -> bool:
|
||||
"""Determine if the given string is an IP address.
|
||||
|
||||
:param value: value to check
|
||||
:type value: str
|
||||
|
||||
:return: True if string is an IP address
|
||||
:rtype: bool
|
||||
"""
|
||||
for family in (socket.AF_INET, socket.AF_INET6):
|
||||
try:
|
||||
socket.inet_pton(family, value)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def _split_blueprint_path(name: str) -> t.List[str]:
|
||||
out: t.List[str] = [name]
|
||||
|
||||
if "." in name:
|
||||
out.extend(_split_blueprint_path(name.rpartition(".")[0]))
|
||||
|
||||
return out
|
||||
363
dist/ba_data/python-site-packages/flask/json/__init__.py
vendored
Normal file
363
dist/ba_data/python-site-packages/flask/json/__init__.py
vendored
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
import decimal
|
||||
import io
|
||||
import json as _json
|
||||
import typing as t
|
||||
import uuid
|
||||
import warnings
|
||||
from datetime import date
|
||||
|
||||
from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
|
||||
from werkzeug.http import http_date
|
||||
|
||||
from ..globals import current_app
|
||||
from ..globals import request
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from ..app import Flask
|
||||
from ..wrappers import Response
|
||||
|
||||
try:
|
||||
import dataclasses
|
||||
except ImportError:
|
||||
# Python < 3.7
|
||||
dataclasses = None # type: ignore
|
||||
|
||||
|
||||
class JSONEncoder(_json.JSONEncoder):
|
||||
"""The default JSON encoder. Handles extra types compared to the
|
||||
built-in :class:`json.JSONEncoder`.
|
||||
|
||||
- :class:`datetime.datetime` and :class:`datetime.date` are
|
||||
serialized to :rfc:`822` strings. This is the same as the HTTP
|
||||
date format.
|
||||
- :class:`uuid.UUID` is serialized to a string.
|
||||
- :class:`dataclasses.dataclass` is passed to
|
||||
:func:`dataclasses.asdict`.
|
||||
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
|
||||
method) will call the ``__html__`` method to get a string.
|
||||
|
||||
Assign a subclass of this to :attr:`flask.Flask.json_encoder` or
|
||||
:attr:`flask.Blueprint.json_encoder` to override the default.
|
||||
"""
|
||||
|
||||
def default(self, o: t.Any) -> t.Any:
|
||||
"""Convert ``o`` to a JSON serializable type. See
|
||||
:meth:`json.JSONEncoder.default`. Python does not support
|
||||
overriding how basic types like ``str`` or ``list`` are
|
||||
serialized, they are handled before this method.
|
||||
"""
|
||||
if isinstance(o, date):
|
||||
return http_date(o)
|
||||
if isinstance(o, (decimal.Decimal, uuid.UUID)):
|
||||
return str(o)
|
||||
if dataclasses and dataclasses.is_dataclass(o):
|
||||
return dataclasses.asdict(o)
|
||||
if hasattr(o, "__html__"):
|
||||
return str(o.__html__())
|
||||
return super().default(o)
|
||||
|
||||
|
||||
class JSONDecoder(_json.JSONDecoder):
|
||||
"""The default JSON decoder.
|
||||
|
||||
This does not change any behavior from the built-in
|
||||
:class:`json.JSONDecoder`.
|
||||
|
||||
Assign a subclass of this to :attr:`flask.Flask.json_decoder` or
|
||||
:attr:`flask.Blueprint.json_decoder` to override the default.
|
||||
"""
|
||||
|
||||
|
||||
def _dump_arg_defaults(
|
||||
kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
|
||||
) -> None:
|
||||
"""Inject default arguments for dump functions."""
|
||||
if app is None:
|
||||
app = current_app
|
||||
|
||||
if app:
|
||||
cls = app.json_encoder
|
||||
bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
|
||||
if bp is not None and bp.json_encoder is not None:
|
||||
cls = bp.json_encoder
|
||||
|
||||
# Only set a custom encoder if it has custom behavior. This is
|
||||
# faster on PyPy.
|
||||
if cls is not _json.JSONEncoder:
|
||||
kwargs.setdefault("cls", cls)
|
||||
|
||||
kwargs.setdefault("cls", cls)
|
||||
kwargs.setdefault("ensure_ascii", app.config["JSON_AS_ASCII"])
|
||||
kwargs.setdefault("sort_keys", app.config["JSON_SORT_KEYS"])
|
||||
else:
|
||||
kwargs.setdefault("sort_keys", True)
|
||||
kwargs.setdefault("cls", JSONEncoder)
|
||||
|
||||
|
||||
def _load_arg_defaults(
|
||||
kwargs: t.Dict[str, t.Any], app: t.Optional["Flask"] = None
|
||||
) -> None:
|
||||
"""Inject default arguments for load functions."""
|
||||
if app is None:
|
||||
app = current_app
|
||||
|
||||
if app:
|
||||
cls = app.json_decoder
|
||||
bp = app.blueprints.get(request.blueprint) if request else None # type: ignore
|
||||
if bp is not None and bp.json_decoder is not None:
|
||||
cls = bp.json_decoder
|
||||
|
||||
# Only set a custom decoder if it has custom behavior. This is
|
||||
# faster on PyPy.
|
||||
if cls not in {JSONDecoder, _json.JSONDecoder}:
|
||||
kwargs.setdefault("cls", cls)
|
||||
|
||||
|
||||
def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str:
|
||||
"""Serialize an object to a string of JSON.
|
||||
|
||||
Takes the same arguments as the built-in :func:`json.dumps`, with
|
||||
some defaults from application configuration.
|
||||
|
||||
:param obj: Object to serialize to JSON.
|
||||
:param app: Use this app's config instead of the active app context
|
||||
or defaults.
|
||||
:param kwargs: Extra arguments passed to :func:`json.dumps`.
|
||||
|
||||
.. versionchanged:: 2.0.2
|
||||
:class:`decimal.Decimal` is supported by converting to a string.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``encoding`` is deprecated and will be removed in Flask 2.1.
|
||||
|
||||
.. versionchanged:: 1.0.3
|
||||
``app`` can be passed directly, rather than requiring an app
|
||||
context for configuration.
|
||||
"""
|
||||
_dump_arg_defaults(kwargs, app=app)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
rv = _json.dumps(obj, **kwargs)
|
||||
|
||||
if encoding is not None:
|
||||
warnings.warn(
|
||||
"'encoding' is deprecated and will be removed in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if isinstance(rv, str):
|
||||
return rv.encode(encoding) # type: ignore
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
def dump(
|
||||
obj: t.Any, fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any
|
||||
) -> None:
|
||||
"""Serialize an object to JSON written to a file object.
|
||||
|
||||
Takes the same arguments as the built-in :func:`json.dump`, with
|
||||
some defaults from application configuration.
|
||||
|
||||
:param obj: Object to serialize to JSON.
|
||||
:param fp: File object to write JSON to.
|
||||
:param app: Use this app's config instead of the active app context
|
||||
or defaults.
|
||||
:param kwargs: Extra arguments passed to :func:`json.dump`.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Writing to a binary file, and the ``encoding`` argument, is
|
||||
deprecated and will be removed in Flask 2.1.
|
||||
"""
|
||||
_dump_arg_defaults(kwargs, app=app)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
show_warning = encoding is not None
|
||||
|
||||
try:
|
||||
fp.write("")
|
||||
except TypeError:
|
||||
show_warning = True
|
||||
fp = io.TextIOWrapper(fp, encoding or "utf-8") # type: ignore
|
||||
|
||||
if show_warning:
|
||||
warnings.warn(
|
||||
"Writing to a binary file, and the 'encoding' argument, is"
|
||||
" deprecated and will be removed in Flask 2.1.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
_json.dump(obj, fp, **kwargs)
|
||||
|
||||
|
||||
def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
|
||||
"""Deserialize an object from a string of JSON.
|
||||
|
||||
Takes the same arguments as the built-in :func:`json.loads`, with
|
||||
some defaults from application configuration.
|
||||
|
||||
:param s: JSON string to deserialize.
|
||||
:param app: Use this app's config instead of the active app context
|
||||
or defaults.
|
||||
:param kwargs: Extra arguments passed to :func:`json.loads`.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``encoding`` is deprecated and will be removed in Flask 2.1. The
|
||||
data must be a string or UTF-8 bytes.
|
||||
|
||||
.. versionchanged:: 1.0.3
|
||||
``app`` can be passed directly, rather than requiring an app
|
||||
context for configuration.
|
||||
"""
|
||||
_load_arg_defaults(kwargs, app=app)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
|
||||
if encoding is not None:
|
||||
warnings.warn(
|
||||
"'encoding' is deprecated and will be removed in Flask 2.1."
|
||||
" The data must be a string or UTF-8 bytes.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if isinstance(s, bytes):
|
||||
s = s.decode(encoding)
|
||||
|
||||
return _json.loads(s, **kwargs)
|
||||
|
||||
|
||||
def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
|
||||
"""Deserialize an object from JSON read from a file object.
|
||||
|
||||
Takes the same arguments as the built-in :func:`json.load`, with
|
||||
some defaults from application configuration.
|
||||
|
||||
:param fp: File object to read JSON from.
|
||||
:param app: Use this app's config instead of the active app context
|
||||
or defaults.
|
||||
:param kwargs: Extra arguments passed to :func:`json.load`.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
``encoding`` is deprecated and will be removed in Flask 2.1. The
|
||||
file must be text mode, or binary mode with UTF-8 bytes.
|
||||
"""
|
||||
_load_arg_defaults(kwargs, app=app)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
|
||||
if encoding is not None:
|
||||
warnings.warn(
|
||||
"'encoding' is deprecated and will be removed in Flask 2.1."
|
||||
" The file must be text mode, or binary mode with UTF-8"
|
||||
" bytes.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if isinstance(fp.read(0), bytes):
|
||||
fp = io.TextIOWrapper(fp, encoding) # type: ignore
|
||||
|
||||
return _json.load(fp, **kwargs)
|
||||
|
||||
|
||||
def htmlsafe_dumps(obj: t.Any, **kwargs: t.Any) -> str:
|
||||
"""Serialize an object to a string of JSON with :func:`dumps`, then
|
||||
replace HTML-unsafe characters with Unicode escapes and mark the
|
||||
result safe with :class:`~markupsafe.Markup`.
|
||||
|
||||
This is available in templates as the ``|tojson`` filter.
|
||||
|
||||
The returned string is safe to render in HTML documents and
|
||||
``<script>`` tags. The exception is in HTML attributes that are
|
||||
double quoted; either use single quotes or the ``|forceescape``
|
||||
filter.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Uses :func:`jinja2.utils.htmlsafe_json_dumps`. The returned
|
||||
value is marked safe by wrapping in :class:`~markupsafe.Markup`.
|
||||
|
||||
.. versionchanged:: 0.10
|
||||
Single quotes are escaped, making this safe to use in HTML,
|
||||
``<script>`` tags, and single-quoted attributes without further
|
||||
escaping.
|
||||
"""
|
||||
return _jinja_htmlsafe_dumps(obj, dumps=dumps, **kwargs)
|
||||
|
||||
|
||||
def htmlsafe_dump(obj: t.Any, fp: t.IO[str], **kwargs: t.Any) -> None:
|
||||
"""Serialize an object to JSON written to a file object, replacing
|
||||
HTML-unsafe characters with Unicode escapes. See
|
||||
:func:`htmlsafe_dumps` and :func:`dumps`.
|
||||
"""
|
||||
fp.write(htmlsafe_dumps(obj, **kwargs))
|
||||
|
||||
|
||||
def jsonify(*args: t.Any, **kwargs: t.Any) -> "Response":
|
||||
"""Serialize data to JSON and wrap it in a :class:`~flask.Response`
|
||||
with the :mimetype:`application/json` mimetype.
|
||||
|
||||
Uses :func:`dumps` to serialize the data, but ``args`` and
|
||||
``kwargs`` are treated as data rather than arguments to
|
||||
:func:`json.dumps`.
|
||||
|
||||
1. Single argument: Treated as a single value.
|
||||
2. Multiple arguments: Treated as a list of values.
|
||||
``jsonify(1, 2, 3)`` is the same as ``jsonify([1, 2, 3])``.
|
||||
3. Keyword arguments: Treated as a dict of values.
|
||||
``jsonify(data=data, errors=errors)`` is the same as
|
||||
``jsonify({"data": data, "errors": errors})``.
|
||||
4. Passing both arguments and keyword arguments is not allowed as
|
||||
it's not clear what should happen.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@app.route("/users/me")
|
||||
def get_current_user():
|
||||
return jsonify(
|
||||
username=g.user.username,
|
||||
email=g.user.email,
|
||||
id=g.user.id,
|
||||
)
|
||||
|
||||
Will return a JSON response like this:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"username": "admin",
|
||||
"email": "admin@localhost",
|
||||
"id": 42
|
||||
}
|
||||
|
||||
The default output omits indents and spaces after separators. In
|
||||
debug mode or if :data:`JSONIFY_PRETTYPRINT_REGULAR` is ``True``,
|
||||
the output will be formatted to be easier to read.
|
||||
|
||||
.. versionchanged:: 2.0.2
|
||||
:class:`decimal.Decimal` is supported by converting to a string.
|
||||
|
||||
.. versionchanged:: 0.11
|
||||
Added support for serializing top-level arrays. This introduces
|
||||
a security risk in ancient browsers. See :ref:`security-json`.
|
||||
|
||||
.. versionadded:: 0.2
|
||||
"""
|
||||
indent = None
|
||||
separators = (",", ":")
|
||||
|
||||
if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
|
||||
indent = 2
|
||||
separators = (", ", ": ")
|
||||
|
||||
if args and kwargs:
|
||||
raise TypeError("jsonify() behavior undefined when passed both args and kwargs")
|
||||
elif len(args) == 1: # single args are passed directly to dumps()
|
||||
data = args[0]
|
||||
else:
|
||||
data = args or kwargs
|
||||
|
||||
return current_app.response_class(
|
||||
f"{dumps(data, indent=indent, separators=separators)}\n",
|
||||
mimetype=current_app.config["JSONIFY_MIMETYPE"],
|
||||
)
|
||||
BIN
dist/ba_data/python-site-packages/flask/json/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/json/__pycache__/__init__.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
BIN
dist/ba_data/python-site-packages/flask/json/__pycache__/tag.cpython-310.opt-1.pyc
vendored
Normal file
BIN
dist/ba_data/python-site-packages/flask/json/__pycache__/tag.cpython-310.opt-1.pyc
vendored
Normal file
Binary file not shown.
312
dist/ba_data/python-site-packages/flask/json/tag.py
vendored
Normal file
312
dist/ba_data/python-site-packages/flask/json/tag.py
vendored
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
"""
|
||||
Tagged JSON
|
||||
~~~~~~~~~~~
|
||||
|
||||
A compact representation for lossless serialization of non-standard JSON
|
||||
types. :class:`~flask.sessions.SecureCookieSessionInterface` uses this
|
||||
to serialize the session data, but it may be useful in other places. It
|
||||
can be extended to support other types.
|
||||
|
||||
.. autoclass:: TaggedJSONSerializer
|
||||
:members:
|
||||
|
||||
.. autoclass:: JSONTag
|
||||
:members:
|
||||
|
||||
Let's see an example that adds support for
|
||||
:class:`~collections.OrderedDict`. Dicts don't have an order in JSON, so
|
||||
to handle this we will dump the items as a list of ``[key, value]``
|
||||
pairs. Subclass :class:`JSONTag` and give it the new key ``' od'`` to
|
||||
identify the type. The session serializer processes dicts first, so
|
||||
insert the new tag at the front of the order since ``OrderedDict`` must
|
||||
be processed before ``dict``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask.json.tag import JSONTag
|
||||
|
||||
class TagOrderedDict(JSONTag):
|
||||
__slots__ = ('serializer',)
|
||||
key = ' od'
|
||||
|
||||
def check(self, value):
|
||||
return isinstance(value, OrderedDict)
|
||||
|
||||
def to_json(self, value):
|
||||
return [[k, self.serializer.tag(v)] for k, v in iteritems(value)]
|
||||
|
||||
def to_python(self, value):
|
||||
return OrderedDict(value)
|
||||
|
||||
app.session_interface.serializer.register(TagOrderedDict, index=0)
|
||||
"""
|
||||
import typing as t
|
||||
from base64 import b64decode
|
||||
from base64 import b64encode
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from markupsafe import Markup
|
||||
from werkzeug.http import http_date
|
||||
from werkzeug.http import parse_date
|
||||
|
||||
from ..json import dumps
|
||||
from ..json import loads
|
||||
|
||||
|
||||
class JSONTag:
|
||||
"""Base class for defining type tags for :class:`TaggedJSONSerializer`."""
|
||||
|
||||
__slots__ = ("serializer",)
|
||||
|
||||
#: The tag to mark the serialized object with. If ``None``, this tag is
|
||||
#: only used as an intermediate step during tagging.
|
||||
key: t.Optional[str] = None
|
||||
|
||||
def __init__(self, serializer: "TaggedJSONSerializer") -> None:
|
||||
"""Create a tagger for the given serializer."""
|
||||
self.serializer = serializer
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
"""Check if the given value should be tagged by this tag."""
|
||||
raise NotImplementedError
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
"""Convert the Python object to an object that is a valid JSON type.
|
||||
The tag will be added later."""
|
||||
raise NotImplementedError
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
"""Convert the JSON representation back to the correct type. The tag
|
||||
will already be removed."""
|
||||
raise NotImplementedError
|
||||
|
||||
def tag(self, value: t.Any) -> t.Any:
|
||||
"""Convert the value to a valid JSON type and add the tag structure
|
||||
around it."""
|
||||
return {self.key: self.to_json(value)}
|
||||
|
||||
|
||||
class TagDict(JSONTag):
|
||||
"""Tag for 1-item dicts whose only key matches a registered tag.
|
||||
|
||||
Internally, the dict key is suffixed with `__`, and the suffix is removed
|
||||
when deserializing.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
key = " di"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return (
|
||||
isinstance(value, dict)
|
||||
and len(value) == 1
|
||||
and next(iter(value)) in self.serializer.tags
|
||||
)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
key = next(iter(value))
|
||||
return {f"{key}__": self.serializer.tag(value[key])}
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
key = next(iter(value))
|
||||
return {key[:-2]: value[key]}
|
||||
|
||||
|
||||
class PassDict(JSONTag):
|
||||
__slots__ = ()
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, dict)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
# JSON objects may only have string keys, so don't bother tagging the
|
||||
# key here.
|
||||
return {k: self.serializer.tag(v) for k, v in value.items()}
|
||||
|
||||
tag = to_json
|
||||
|
||||
|
||||
class TagTuple(JSONTag):
|
||||
__slots__ = ()
|
||||
key = " t"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, tuple)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return [self.serializer.tag(item) for item in value]
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
return tuple(value)
|
||||
|
||||
|
||||
class PassList(JSONTag):
|
||||
__slots__ = ()
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, list)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return [self.serializer.tag(item) for item in value]
|
||||
|
||||
tag = to_json
|
||||
|
||||
|
||||
class TagBytes(JSONTag):
|
||||
__slots__ = ()
|
||||
key = " b"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, bytes)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return b64encode(value).decode("ascii")
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
return b64decode(value)
|
||||
|
||||
|
||||
class TagMarkup(JSONTag):
|
||||
"""Serialize anything matching the :class:`~markupsafe.Markup` API by
|
||||
having a ``__html__`` method to the result of that method. Always
|
||||
deserializes to an instance of :class:`~markupsafe.Markup`."""
|
||||
|
||||
__slots__ = ()
|
||||
key = " m"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return callable(getattr(value, "__html__", None))
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return str(value.__html__())
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
return Markup(value)
|
||||
|
||||
|
||||
class TagUUID(JSONTag):
|
||||
__slots__ = ()
|
||||
key = " u"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, UUID)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return value.hex
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
return UUID(value)
|
||||
|
||||
|
||||
class TagDateTime(JSONTag):
|
||||
__slots__ = ()
|
||||
key = " d"
|
||||
|
||||
def check(self, value: t.Any) -> bool:
|
||||
return isinstance(value, datetime)
|
||||
|
||||
def to_json(self, value: t.Any) -> t.Any:
|
||||
return http_date(value)
|
||||
|
||||
def to_python(self, value: t.Any) -> t.Any:
|
||||
return parse_date(value)
|
||||
|
||||
|
||||
class TaggedJSONSerializer:
|
||||
"""Serializer that uses a tag system to compactly represent objects that
|
||||
are not JSON types. Passed as the intermediate serializer to
|
||||
:class:`itsdangerous.Serializer`.
|
||||
|
||||
The following extra types are supported:
|
||||
|
||||
* :class:`dict`
|
||||
* :class:`tuple`
|
||||
* :class:`bytes`
|
||||
* :class:`~markupsafe.Markup`
|
||||
* :class:`~uuid.UUID`
|
||||
* :class:`~datetime.datetime`
|
||||
"""
|
||||
|
||||
__slots__ = ("tags", "order")
|
||||
|
||||
#: Tag classes to bind when creating the serializer. Other tags can be
|
||||
#: added later using :meth:`~register`.
|
||||
default_tags = [
|
||||
TagDict,
|
||||
PassDict,
|
||||
TagTuple,
|
||||
PassList,
|
||||
TagBytes,
|
||||
TagMarkup,
|
||||
TagUUID,
|
||||
TagDateTime,
|
||||
]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tags: t.Dict[str, JSONTag] = {}
|
||||
self.order: t.List[JSONTag] = []
|
||||
|
||||
for cls in self.default_tags:
|
||||
self.register(cls)
|
||||
|
||||
def register(
|
||||
self,
|
||||
tag_class: t.Type[JSONTag],
|
||||
force: bool = False,
|
||||
index: t.Optional[int] = None,
|
||||
) -> None:
|
||||
"""Register a new tag with this serializer.
|
||||
|
||||
:param tag_class: tag class to register. Will be instantiated with this
|
||||
serializer instance.
|
||||
:param force: overwrite an existing tag. If false (default), a
|
||||
:exc:`KeyError` is raised.
|
||||
:param index: index to insert the new tag in the tag order. Useful when
|
||||
the new tag is a special case of an existing tag. If ``None``
|
||||
(default), the tag is appended to the end of the order.
|
||||
|
||||
:raise KeyError: if the tag key is already registered and ``force`` is
|
||||
not true.
|
||||
"""
|
||||
tag = tag_class(self)
|
||||
key = tag.key
|
||||
|
||||
if key is not None:
|
||||
if not force and key in self.tags:
|
||||
raise KeyError(f"Tag '{key}' is already registered.")
|
||||
|
||||
self.tags[key] = tag
|
||||
|
||||
if index is None:
|
||||
self.order.append(tag)
|
||||
else:
|
||||
self.order.insert(index, tag)
|
||||
|
||||
def tag(self, value: t.Any) -> t.Dict[str, t.Any]:
|
||||
"""Convert a value to a tagged representation if necessary."""
|
||||
for tag in self.order:
|
||||
if tag.check(value):
|
||||
return tag.tag(value)
|
||||
|
||||
return value
|
||||
|
||||
def untag(self, value: t.Dict[str, t.Any]) -> t.Any:
|
||||
"""Convert a tagged representation back to the original type."""
|
||||
if len(value) != 1:
|
||||
return value
|
||||
|
||||
key = next(iter(value))
|
||||
|
||||
if key not in self.tags:
|
||||
return value
|
||||
|
||||
return self.tags[key].to_python(value[key])
|
||||
|
||||
def dumps(self, value: t.Any) -> str:
|
||||
"""Tag the value and dump it to a compact JSON string."""
|
||||
return dumps(self.tag(value), separators=(",", ":"))
|
||||
|
||||
def loads(self, value: str) -> t.Any:
|
||||
"""Load data from a JSON string and deserialized any tagged objects."""
|
||||
return loads(value, object_hook=self.untag)
|
||||
74
dist/ba_data/python-site-packages/flask/logging.py
vendored
Normal file
74
dist/ba_data/python-site-packages/flask/logging.py
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import logging
|
||||
import sys
|
||||
import typing as t
|
||||
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from .globals import request
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
|
||||
|
||||
@LocalProxy
|
||||
def wsgi_errors_stream() -> t.TextIO:
|
||||
"""Find the most appropriate error stream for the application. If a request
|
||||
is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``.
|
||||
|
||||
If you configure your own :class:`logging.StreamHandler`, you may want to
|
||||
use this for the stream. If you are using file or dict configuration and
|
||||
can't import this directly, you can refer to it as
|
||||
``ext://flask.logging.wsgi_errors_stream``.
|
||||
"""
|
||||
return request.environ["wsgi.errors"] if request else sys.stderr
|
||||
|
||||
|
||||
def has_level_handler(logger: logging.Logger) -> bool:
|
||||
"""Check if there is a handler in the logging chain that will handle the
|
||||
given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`.
|
||||
"""
|
||||
level = logger.getEffectiveLevel()
|
||||
current = logger
|
||||
|
||||
while current:
|
||||
if any(handler.level <= level for handler in current.handlers):
|
||||
return True
|
||||
|
||||
if not current.propagate:
|
||||
break
|
||||
|
||||
current = current.parent # type: ignore
|
||||
|
||||
return False
|
||||
|
||||
|
||||
#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
|
||||
#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
|
||||
default_handler = logging.StreamHandler(wsgi_errors_stream) # type: ignore
|
||||
default_handler.setFormatter(
|
||||
logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
|
||||
)
|
||||
|
||||
|
||||
def create_logger(app: "Flask") -> logging.Logger:
|
||||
"""Get the Flask app's logger and configure it if needed.
|
||||
|
||||
The logger name will be the same as
|
||||
:attr:`app.import_name <flask.Flask.name>`.
|
||||
|
||||
When :attr:`~flask.Flask.debug` is enabled, set the logger level to
|
||||
:data:`logging.DEBUG` if it is not set.
|
||||
|
||||
If there is no handler for the logger's effective level, add a
|
||||
:class:`~logging.StreamHandler` for
|
||||
:func:`~flask.logging.wsgi_errors_stream` with a basic format.
|
||||
"""
|
||||
logger = logging.getLogger(app.name)
|
||||
|
||||
if app.debug and not logger.level:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
if not has_level_handler(logger):
|
||||
logger.addHandler(default_handler)
|
||||
|
||||
return logger
|
||||
0
dist/ba_data/python-site-packages/flask/py.typed
vendored
Normal file
0
dist/ba_data/python-site-packages/flask/py.typed
vendored
Normal file
875
dist/ba_data/python-site-packages/flask/scaffold.py
vendored
Normal file
875
dist/ba_data/python-site-packages/flask/scaffold.py
vendored
Normal file
|
|
@ -0,0 +1,875 @@
|
|||
import importlib.util
|
||||
import os
|
||||
import pkgutil
|
||||
import sys
|
||||
import typing as t
|
||||
from collections import defaultdict
|
||||
from functools import update_wrapper
|
||||
from json import JSONDecoder
|
||||
from json import JSONEncoder
|
||||
|
||||
from jinja2 import FileSystemLoader
|
||||
from werkzeug.exceptions import default_exceptions
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from .cli import AppGroup
|
||||
from .globals import current_app
|
||||
from .helpers import get_root_path
|
||||
from .helpers import locked_cached_property
|
||||
from .helpers import send_from_directory
|
||||
from .templating import _default_template_ctx_processor
|
||||
from .typing import AfterRequestCallable
|
||||
from .typing import AppOrBlueprintKey
|
||||
from .typing import BeforeRequestCallable
|
||||
from .typing import GenericException
|
||||
from .typing import TeardownCallable
|
||||
from .typing import TemplateContextProcessorCallable
|
||||
from .typing import URLDefaultCallable
|
||||
from .typing import URLValuePreprocessorCallable
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .wrappers import Response
|
||||
from .typing import ErrorHandlerCallable
|
||||
|
||||
# a singleton sentinel value for parameter defaults
|
||||
_sentinel = object()
|
||||
|
||||
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||
|
||||
|
||||
def setupmethod(f: F) -> F:
|
||||
"""Wraps a method so that it performs a check in debug mode if the
|
||||
first request was already handled.
|
||||
"""
|
||||
|
||||
def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
if self._is_setup_finished():
|
||||
raise AssertionError(
|
||||
"A setup function was called after the first request "
|
||||
"was handled. This usually indicates a bug in the"
|
||||
" application where a module was not imported and"
|
||||
" decorators or other functionality was called too"
|
||||
" late.\nTo fix this make sure to import all your view"
|
||||
" modules, database models, and everything related at a"
|
||||
" central place before the application starts serving"
|
||||
" requests."
|
||||
)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return t.cast(F, update_wrapper(wrapper_func, f))
|
||||
|
||||
|
||||
class Scaffold:
|
||||
"""Common behavior shared between :class:`~flask.Flask` and
|
||||
:class:`~flask.blueprints.Blueprint`.
|
||||
|
||||
:param import_name: The import name of the module where this object
|
||||
is defined. Usually :attr:`__name__` should be used.
|
||||
:param static_folder: Path to a folder of static files to serve.
|
||||
If this is set, a static route will be added.
|
||||
:param static_url_path: URL prefix for the static route.
|
||||
:param template_folder: Path to a folder containing template files.
|
||||
for rendering. If this is set, a Jinja loader will be added.
|
||||
:param root_path: The path that static, template, and resource files
|
||||
are relative to. Typically not set, it is discovered based on
|
||||
the ``import_name``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
|
||||
name: str
|
||||
_static_folder: t.Optional[str] = None
|
||||
_static_url_path: t.Optional[str] = None
|
||||
|
||||
#: JSON encoder class used by :func:`flask.json.dumps`. If a
|
||||
#: blueprint sets this, it will be used instead of the app's value.
|
||||
json_encoder: t.Optional[t.Type[JSONEncoder]] = None
|
||||
|
||||
#: JSON decoder class used by :func:`flask.json.loads`. If a
|
||||
#: blueprint sets this, it will be used instead of the app's value.
|
||||
json_decoder: t.Optional[t.Type[JSONDecoder]] = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
import_name: str,
|
||||
static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
|
||||
static_url_path: t.Optional[str] = None,
|
||||
template_folder: t.Optional[str] = None,
|
||||
root_path: t.Optional[str] = None,
|
||||
):
|
||||
#: The name of the package or module that this object belongs
|
||||
#: to. Do not change this once it is set by the constructor.
|
||||
self.import_name = import_name
|
||||
|
||||
self.static_folder = static_folder # type: ignore
|
||||
self.static_url_path = static_url_path
|
||||
|
||||
#: The path to the templates folder, relative to
|
||||
#: :attr:`root_path`, to add to the template loader. ``None`` if
|
||||
#: templates should not be added.
|
||||
self.template_folder = template_folder
|
||||
|
||||
if root_path is None:
|
||||
root_path = get_root_path(self.import_name)
|
||||
|
||||
#: Absolute path to the package on the filesystem. Used to look
|
||||
#: up resources contained in the package.
|
||||
self.root_path = root_path
|
||||
|
||||
#: The Click command group for registering CLI commands for this
|
||||
#: object. The commands are available from the ``flask`` command
|
||||
#: once the application has been discovered and blueprints have
|
||||
#: been registered.
|
||||
self.cli = AppGroup()
|
||||
|
||||
#: A dictionary mapping endpoint names to view functions.
|
||||
#:
|
||||
#: To register a view function, use the :meth:`route` decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.view_functions: t.Dict[str, t.Callable] = {}
|
||||
|
||||
#: A data structure of registered error handlers, in the format
|
||||
#: ``{scope: {code: {class: handler}}}```. The ``scope`` key is
|
||||
#: the name of a blueprint the handlers are active for, or
|
||||
#: ``None`` for all requests. The ``code`` key is the HTTP
|
||||
#: status code for ``HTTPException``, or ``None`` for
|
||||
#: other exceptions. The innermost dictionary maps exception
|
||||
#: classes to handler functions.
|
||||
#:
|
||||
#: To register an error handler, use the :meth:`errorhandler`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.error_handler_spec: t.Dict[
|
||||
AppOrBlueprintKey,
|
||||
t.Dict[
|
||||
t.Optional[int],
|
||||
t.Dict[t.Type[Exception], "ErrorHandlerCallable[Exception]"],
|
||||
],
|
||||
] = defaultdict(lambda: defaultdict(dict))
|
||||
|
||||
#: A data structure of functions to call at the beginning of
|
||||
#: each request, in the format ``{scope: [functions]}``. The
|
||||
#: ``scope`` key is the name of a blueprint the functions are
|
||||
#: active for, or ``None`` for all requests.
|
||||
#:
|
||||
#: To register a function, use the :meth:`before_request`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.before_request_funcs: t.Dict[
|
||||
AppOrBlueprintKey, t.List[BeforeRequestCallable]
|
||||
] = defaultdict(list)
|
||||
|
||||
#: A data structure of functions to call at the end of each
|
||||
#: request, in the format ``{scope: [functions]}``. The
|
||||
#: ``scope`` key is the name of a blueprint the functions are
|
||||
#: active for, or ``None`` for all requests.
|
||||
#:
|
||||
#: To register a function, use the :meth:`after_request`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.after_request_funcs: t.Dict[
|
||||
AppOrBlueprintKey, t.List[AfterRequestCallable]
|
||||
] = defaultdict(list)
|
||||
|
||||
#: A data structure of functions to call at the end of each
|
||||
#: request even if an exception is raised, in the format
|
||||
#: ``{scope: [functions]}``. The ``scope`` key is the name of a
|
||||
#: blueprint the functions are active for, or ``None`` for all
|
||||
#: requests.
|
||||
#:
|
||||
#: To register a function, use the :meth:`teardown_request`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.teardown_request_funcs: t.Dict[
|
||||
AppOrBlueprintKey, t.List[TeardownCallable]
|
||||
] = defaultdict(list)
|
||||
|
||||
#: A data structure of functions to call to pass extra context
|
||||
#: values when rendering templates, in the format
|
||||
#: ``{scope: [functions]}``. The ``scope`` key is the name of a
|
||||
#: blueprint the functions are active for, or ``None`` for all
|
||||
#: requests.
|
||||
#:
|
||||
#: To register a function, use the :meth:`context_processor`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.template_context_processors: t.Dict[
|
||||
AppOrBlueprintKey, t.List[TemplateContextProcessorCallable]
|
||||
] = defaultdict(list, {None: [_default_template_ctx_processor]})
|
||||
|
||||
#: A data structure of functions to call to modify the keyword
|
||||
#: arguments passed to the view function, in the format
|
||||
#: ``{scope: [functions]}``. The ``scope`` key is the name of a
|
||||
#: blueprint the functions are active for, or ``None`` for all
|
||||
#: requests.
|
||||
#:
|
||||
#: To register a function, use the
|
||||
#: :meth:`url_value_preprocessor` decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.url_value_preprocessors: t.Dict[
|
||||
AppOrBlueprintKey,
|
||||
t.List[URLValuePreprocessorCallable],
|
||||
] = defaultdict(list)
|
||||
|
||||
#: A data structure of functions to call to modify the keyword
|
||||
#: arguments when generating URLs, in the format
|
||||
#: ``{scope: [functions]}``. The ``scope`` key is the name of a
|
||||
#: blueprint the functions are active for, or ``None`` for all
|
||||
#: requests.
|
||||
#:
|
||||
#: To register a function, use the :meth:`url_defaults`
|
||||
#: decorator.
|
||||
#:
|
||||
#: This data structure is internal. It should not be modified
|
||||
#: directly and its format may change at any time.
|
||||
self.url_default_functions: t.Dict[
|
||||
AppOrBlueprintKey, t.List[URLDefaultCallable]
|
||||
] = defaultdict(list)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<{type(self).__name__} {self.name!r}>"
|
||||
|
||||
def _is_setup_finished(self) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def static_folder(self) -> t.Optional[str]:
|
||||
"""The absolute path to the configured static folder. ``None``
|
||||
if no static folder is set.
|
||||
"""
|
||||
if self._static_folder is not None:
|
||||
return os.path.join(self.root_path, self._static_folder)
|
||||
else:
|
||||
return None
|
||||
|
||||
@static_folder.setter
|
||||
def static_folder(self, value: t.Optional[t.Union[str, os.PathLike]]) -> None:
|
||||
if value is not None:
|
||||
value = os.fspath(value).rstrip(r"\/")
|
||||
|
||||
self._static_folder = value
|
||||
|
||||
@property
|
||||
def has_static_folder(self) -> bool:
|
||||
"""``True`` if :attr:`static_folder` is set.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
return self.static_folder is not None
|
||||
|
||||
@property
|
||||
def static_url_path(self) -> t.Optional[str]:
|
||||
"""The URL prefix that the static route will be accessible from.
|
||||
|
||||
If it was not configured during init, it is derived from
|
||||
:attr:`static_folder`.
|
||||
"""
|
||||
if self._static_url_path is not None:
|
||||
return self._static_url_path
|
||||
|
||||
if self.static_folder is not None:
|
||||
basename = os.path.basename(self.static_folder)
|
||||
return f"/{basename}".rstrip("/")
|
||||
|
||||
return None
|
||||
|
||||
@static_url_path.setter
|
||||
def static_url_path(self, value: t.Optional[str]) -> None:
|
||||
if value is not None:
|
||||
value = value.rstrip("/")
|
||||
|
||||
self._static_url_path = value
|
||||
|
||||
def get_send_file_max_age(self, filename: t.Optional[str]) -> t.Optional[int]:
|
||||
"""Used by :func:`send_file` to determine the ``max_age`` cache
|
||||
value for a given file path if it wasn't passed.
|
||||
|
||||
By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from
|
||||
the configuration of :data:`~flask.current_app`. This defaults
|
||||
to ``None``, which tells the browser to use conditional requests
|
||||
instead of a timed cache, which is usually preferable.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
The default configuration is ``None`` instead of 12 hours.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
"""
|
||||
value = current_app.send_file_max_age_default
|
||||
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
return int(value.total_seconds())
|
||||
|
||||
def send_static_file(self, filename: str) -> "Response":
|
||||
"""The view function used to serve files from
|
||||
:attr:`static_folder`. A route is automatically registered for
|
||||
this view at :attr:`static_url_path` if :attr:`static_folder` is
|
||||
set.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if not self.has_static_folder:
|
||||
raise RuntimeError("'static_folder' must be set to serve static_files.")
|
||||
|
||||
# send_file only knows to call get_send_file_max_age on the app,
|
||||
# call it here so it works for blueprints too.
|
||||
max_age = self.get_send_file_max_age(filename)
|
||||
return send_from_directory(
|
||||
t.cast(str, self.static_folder), filename, max_age=max_age
|
||||
)
|
||||
|
||||
@locked_cached_property
|
||||
def jinja_loader(self) -> t.Optional[FileSystemLoader]:
|
||||
"""The Jinja loader for this object's templates. By default this
|
||||
is a class :class:`jinja2.loaders.FileSystemLoader` to
|
||||
:attr:`template_folder` if it is set.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
if self.template_folder is not None:
|
||||
return FileSystemLoader(os.path.join(self.root_path, self.template_folder))
|
||||
else:
|
||||
return None
|
||||
|
||||
def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
|
||||
"""Open a resource file relative to :attr:`root_path` for
|
||||
reading.
|
||||
|
||||
For example, if the file ``schema.sql`` is next to the file
|
||||
``app.py`` where the ``Flask`` app is defined, it can be opened
|
||||
with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with app.open_resource("schema.sql") as f:
|
||||
conn.executescript(f.read())
|
||||
|
||||
:param resource: Path to the resource relative to
|
||||
:attr:`root_path`.
|
||||
:param mode: Open the file in this mode. Only reading is
|
||||
supported, valid values are "r" (or "rt") and "rb".
|
||||
"""
|
||||
if mode not in {"r", "rt", "rb"}:
|
||||
raise ValueError("Resources can only be opened for reading.")
|
||||
|
||||
return open(os.path.join(self.root_path, resource), mode)
|
||||
|
||||
def _method_route(self, method: str, rule: str, options: dict) -> t.Callable:
|
||||
if "methods" in options:
|
||||
raise TypeError("Use the 'route' decorator to use the 'methods' argument.")
|
||||
|
||||
return self.route(rule, methods=[method], **options)
|
||||
|
||||
def get(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Shortcut for :meth:`route` with ``methods=["GET"]``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._method_route("GET", rule, options)
|
||||
|
||||
def post(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Shortcut for :meth:`route` with ``methods=["POST"]``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._method_route("POST", rule, options)
|
||||
|
||||
def put(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Shortcut for :meth:`route` with ``methods=["PUT"]``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._method_route("PUT", rule, options)
|
||||
|
||||
def delete(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Shortcut for :meth:`route` with ``methods=["DELETE"]``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._method_route("DELETE", rule, options)
|
||||
|
||||
def patch(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Shortcut for :meth:`route` with ``methods=["PATCH"]``.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return self._method_route("PATCH", rule, options)
|
||||
|
||||
def route(self, rule: str, **options: t.Any) -> t.Callable:
|
||||
"""Decorate a view function to register it with the given URL
|
||||
rule and options. Calls :meth:`add_url_rule`, which has more
|
||||
details about the implementation.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return "Hello, World!"
|
||||
|
||||
See :ref:`url-route-registrations`.
|
||||
|
||||
The endpoint name for the route defaults to the name of the view
|
||||
function if the ``endpoint`` parameter isn't passed.
|
||||
|
||||
The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and
|
||||
``OPTIONS`` are added automatically.
|
||||
|
||||
:param rule: The URL rule string.
|
||||
:param options: Extra options passed to the
|
||||
:class:`~werkzeug.routing.Rule` object.
|
||||
"""
|
||||
|
||||
def decorator(f: t.Callable) -> t.Callable:
|
||||
endpoint = options.pop("endpoint", None)
|
||||
self.add_url_rule(rule, endpoint, f, **options)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
def add_url_rule(
|
||||
self,
|
||||
rule: str,
|
||||
endpoint: t.Optional[str] = None,
|
||||
view_func: t.Optional[t.Callable] = None,
|
||||
provide_automatic_options: t.Optional[bool] = None,
|
||||
**options: t.Any,
|
||||
) -> None:
|
||||
"""Register a rule for routing incoming requests and building
|
||||
URLs. The :meth:`route` decorator is a shortcut to call this
|
||||
with the ``view_func`` argument. These are equivalent:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
...
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def index():
|
||||
...
|
||||
|
||||
app.add_url_rule("/", view_func=index)
|
||||
|
||||
See :ref:`url-route-registrations`.
|
||||
|
||||
The endpoint name for the route defaults to the name of the view
|
||||
function if the ``endpoint`` parameter isn't passed. An error
|
||||
will be raised if a function has already been registered for the
|
||||
endpoint.
|
||||
|
||||
The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is
|
||||
always added automatically, and ``OPTIONS`` is added
|
||||
automatically by default.
|
||||
|
||||
``view_func`` does not necessarily need to be passed, but if the
|
||||
rule should participate in routing an endpoint name must be
|
||||
associated with a view function at some point with the
|
||||
:meth:`endpoint` decorator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_url_rule("/", endpoint="index")
|
||||
|
||||
@app.endpoint("index")
|
||||
def index():
|
||||
...
|
||||
|
||||
If ``view_func`` has a ``required_methods`` attribute, those
|
||||
methods are added to the passed and automatic methods. If it
|
||||
has a ``provide_automatic_methods`` attribute, it is used as the
|
||||
default if the parameter is not passed.
|
||||
|
||||
:param rule: The URL rule string.
|
||||
:param endpoint: The endpoint name to associate with the rule
|
||||
and view function. Used when routing and building URLs.
|
||||
Defaults to ``view_func.__name__``.
|
||||
:param view_func: The view function to associate with the
|
||||
endpoint name.
|
||||
:param provide_automatic_options: Add the ``OPTIONS`` method and
|
||||
respond to ``OPTIONS`` requests automatically.
|
||||
:param options: Extra options passed to the
|
||||
:class:`~werkzeug.routing.Rule` object.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def endpoint(self, endpoint: str) -> t.Callable:
|
||||
"""Decorate a view function to register it for the given
|
||||
endpoint. Used if a rule is added without a ``view_func`` with
|
||||
:meth:`add_url_rule`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_url_rule("/ex", endpoint="example")
|
||||
|
||||
@app.endpoint("example")
|
||||
def example():
|
||||
...
|
||||
|
||||
:param endpoint: The endpoint name to associate with the view
|
||||
function.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.view_functions[endpoint] = f
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
def before_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
|
||||
"""Register a function to run before each request.
|
||||
|
||||
For example, this can be used to open a database connection, or
|
||||
to load the logged in user from the session.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.before_request
|
||||
def load_user():
|
||||
if "user_id" in session:
|
||||
g.user = db.session.get(session["user_id"])
|
||||
|
||||
The function will be called without any arguments. If it returns
|
||||
a non-``None`` value, the value is handled as if it was the
|
||||
return value from the view, and further request handling is
|
||||
stopped.
|
||||
"""
|
||||
self.before_request_funcs.setdefault(None, []).append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def after_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
|
||||
"""Register a function to run after each request to this object.
|
||||
|
||||
The function is called with the response object, and must return
|
||||
a response object. This allows the functions to modify or
|
||||
replace the response before it is sent.
|
||||
|
||||
If a function raises an exception, any remaining
|
||||
``after_request`` functions will not be called. Therefore, this
|
||||
should not be used for actions that must execute, such as to
|
||||
close resources. Use :meth:`teardown_request` for that.
|
||||
"""
|
||||
self.after_request_funcs.setdefault(None, []).append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def teardown_request(self, f: TeardownCallable) -> TeardownCallable:
|
||||
"""Register a function to be run at the end of each request,
|
||||
regardless of whether there was an exception or not. These functions
|
||||
are executed when the request context is popped, even if not an
|
||||
actual request was performed.
|
||||
|
||||
Example::
|
||||
|
||||
ctx = app.test_request_context()
|
||||
ctx.push()
|
||||
...
|
||||
ctx.pop()
|
||||
|
||||
When ``ctx.pop()`` is executed in the above example, the teardown
|
||||
functions are called just before the request context moves from the
|
||||
stack of active contexts. This becomes relevant if you are using
|
||||
such constructs in tests.
|
||||
|
||||
Teardown functions must avoid raising exceptions, since they . If they
|
||||
execute code that might fail they
|
||||
will have to surround the execution of these code by try/except
|
||||
statements and log occurring errors.
|
||||
|
||||
When a teardown function was called because of an exception it will
|
||||
be passed an error object.
|
||||
|
||||
The return values of teardown functions are ignored.
|
||||
|
||||
.. admonition:: Debug Note
|
||||
|
||||
In debug mode Flask will not tear down a request on an exception
|
||||
immediately. Instead it will keep it alive so that the interactive
|
||||
debugger can still access it. This behavior can be controlled
|
||||
by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable.
|
||||
"""
|
||||
self.teardown_request_funcs.setdefault(None, []).append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def context_processor(
|
||||
self, f: TemplateContextProcessorCallable
|
||||
) -> TemplateContextProcessorCallable:
|
||||
"""Registers a template context processor function."""
|
||||
self.template_context_processors[None].append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def url_value_preprocessor(
|
||||
self, f: URLValuePreprocessorCallable
|
||||
) -> URLValuePreprocessorCallable:
|
||||
"""Register a URL value preprocessor function for all view
|
||||
functions in the application. These functions will be called before the
|
||||
:meth:`before_request` functions.
|
||||
|
||||
The function can modify the values captured from the matched url before
|
||||
they are passed to the view. For example, this can be used to pop a
|
||||
common language code value and place it in ``g`` rather than pass it to
|
||||
every view.
|
||||
|
||||
The function is passed the endpoint name and values dict. The return
|
||||
value is ignored.
|
||||
"""
|
||||
self.url_value_preprocessors[None].append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable:
|
||||
"""Callback function for URL defaults for all view functions of the
|
||||
application. It's called with the endpoint and values and should
|
||||
update the values passed in place.
|
||||
"""
|
||||
self.url_default_functions[None].append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def errorhandler(
|
||||
self, code_or_exception: t.Union[t.Type[GenericException], int]
|
||||
) -> t.Callable[
|
||||
["ErrorHandlerCallable[GenericException]"],
|
||||
"ErrorHandlerCallable[GenericException]",
|
||||
]:
|
||||
"""Register a function to handle errors by code or exception class.
|
||||
|
||||
A decorator that is used to register a function given an
|
||||
error code. Example::
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(error):
|
||||
return 'This page does not exist', 404
|
||||
|
||||
You can also register handlers for arbitrary exceptions::
|
||||
|
||||
@app.errorhandler(DatabaseError)
|
||||
def special_exception_handler(error):
|
||||
return 'Database connection failed', 500
|
||||
|
||||
.. versionadded:: 0.7
|
||||
Use :meth:`register_error_handler` instead of modifying
|
||||
:attr:`error_handler_spec` directly, for application wide error
|
||||
handlers.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
One can now additionally also register custom exception types
|
||||
that do not necessarily have to be a subclass of the
|
||||
:class:`~werkzeug.exceptions.HTTPException` class.
|
||||
|
||||
:param code_or_exception: the code as integer for the handler, or
|
||||
an arbitrary exception
|
||||
"""
|
||||
|
||||
def decorator(
|
||||
f: "ErrorHandlerCallable[GenericException]",
|
||||
) -> "ErrorHandlerCallable[GenericException]":
|
||||
self.register_error_handler(code_or_exception, f)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
def register_error_handler(
|
||||
self,
|
||||
code_or_exception: t.Union[t.Type[GenericException], int],
|
||||
f: "ErrorHandlerCallable[GenericException]",
|
||||
) -> None:
|
||||
"""Alternative error attach function to the :meth:`errorhandler`
|
||||
decorator that is more straightforward to use for non decorator
|
||||
usage.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
||||
raise ValueError(
|
||||
"Tried to register a handler for an exception instance"
|
||||
f" {code_or_exception!r}. Handlers can only be"
|
||||
" registered for exception classes or HTTP error codes."
|
||||
)
|
||||
|
||||
try:
|
||||
exc_class, code = self._get_exc_class_and_code(code_or_exception)
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
f"'{code_or_exception}' is not a recognized HTTP error"
|
||||
" code. Use a subclass of HTTPException with that code"
|
||||
" instead."
|
||||
) from None
|
||||
|
||||
self.error_handler_spec[None][code][exc_class] = t.cast(
|
||||
"ErrorHandlerCallable[Exception]", f
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _get_exc_class_and_code(
|
||||
exc_class_or_code: t.Union[t.Type[Exception], int]
|
||||
) -> t.Tuple[t.Type[Exception], t.Optional[int]]:
|
||||
"""Get the exception class being handled. For HTTP status codes
|
||||
or ``HTTPException`` subclasses, return both the exception and
|
||||
status code.
|
||||
|
||||
:param exc_class_or_code: Any exception class, or an HTTP status
|
||||
code as an integer.
|
||||
"""
|
||||
exc_class: t.Type[Exception]
|
||||
if isinstance(exc_class_or_code, int):
|
||||
exc_class = default_exceptions[exc_class_or_code]
|
||||
else:
|
||||
exc_class = exc_class_or_code
|
||||
|
||||
assert issubclass(
|
||||
exc_class, Exception
|
||||
), "Custom exceptions must be subclasses of Exception."
|
||||
|
||||
if issubclass(exc_class, HTTPException):
|
||||
return exc_class, exc_class.code
|
||||
else:
|
||||
return exc_class, None
|
||||
|
||||
|
||||
def _endpoint_from_view_func(view_func: t.Callable) -> str:
|
||||
"""Internal helper that returns the default endpoint for a given
|
||||
function. This always is the function name.
|
||||
"""
|
||||
assert view_func is not None, "expected view func if endpoint is not provided."
|
||||
return view_func.__name__
|
||||
|
||||
|
||||
def _matching_loader_thinks_module_is_package(loader, mod_name):
|
||||
"""Attempt to figure out if the given name is a package or a module.
|
||||
|
||||
:param: loader: The loader that handled the name.
|
||||
:param mod_name: The name of the package or module.
|
||||
"""
|
||||
# Use loader.is_package if it's available.
|
||||
if hasattr(loader, "is_package"):
|
||||
return loader.is_package(mod_name)
|
||||
|
||||
cls = type(loader)
|
||||
|
||||
# NamespaceLoader doesn't implement is_package, but all names it
|
||||
# loads must be packages.
|
||||
if cls.__module__ == "_frozen_importlib" and cls.__name__ == "NamespaceLoader":
|
||||
return True
|
||||
|
||||
# Otherwise we need to fail with an error that explains what went
|
||||
# wrong.
|
||||
raise AttributeError(
|
||||
f"'{cls.__name__}.is_package()' must be implemented for PEP 302"
|
||||
f" import hooks."
|
||||
)
|
||||
|
||||
|
||||
def _find_package_path(root_mod_name):
|
||||
"""Find the path that contains the package or module."""
|
||||
try:
|
||||
spec = importlib.util.find_spec(root_mod_name)
|
||||
|
||||
if spec is None:
|
||||
raise ValueError("not found")
|
||||
# ImportError: the machinery told us it does not exist
|
||||
# ValueError:
|
||||
# - the module name was invalid
|
||||
# - the module name is __main__
|
||||
# - *we* raised `ValueError` due to `spec` being `None`
|
||||
except (ImportError, ValueError):
|
||||
pass # handled below
|
||||
else:
|
||||
# namespace package
|
||||
if spec.origin in {"namespace", None}:
|
||||
return os.path.dirname(next(iter(spec.submodule_search_locations)))
|
||||
# a package (with __init__.py)
|
||||
elif spec.submodule_search_locations:
|
||||
return os.path.dirname(os.path.dirname(spec.origin))
|
||||
# just a normal module
|
||||
else:
|
||||
return os.path.dirname(spec.origin)
|
||||
|
||||
# we were unable to find the `package_path` using PEP 451 loaders
|
||||
loader = pkgutil.get_loader(root_mod_name)
|
||||
|
||||
if loader is None or root_mod_name == "__main__":
|
||||
# import name is not found, or interactive/main module
|
||||
return os.getcwd()
|
||||
|
||||
if hasattr(loader, "get_filename"):
|
||||
filename = loader.get_filename(root_mod_name)
|
||||
elif hasattr(loader, "archive"):
|
||||
# zipimporter's loader.archive points to the .egg or .zip file.
|
||||
filename = loader.archive
|
||||
else:
|
||||
# At least one loader is missing both get_filename and archive:
|
||||
# Google App Engine's HardenedModulesHook, use __file__.
|
||||
filename = importlib.import_module(root_mod_name).__file__
|
||||
|
||||
package_path = os.path.abspath(os.path.dirname(filename))
|
||||
|
||||
# If the imported name is a package, filename is currently pointing
|
||||
# to the root of the package, need to get the current directory.
|
||||
if _matching_loader_thinks_module_is_package(loader, root_mod_name):
|
||||
package_path = os.path.dirname(package_path)
|
||||
|
||||
return package_path
|
||||
|
||||
|
||||
def find_package(import_name: str):
|
||||
"""Find the prefix that a package is installed under, and the path
|
||||
that it would be imported from.
|
||||
|
||||
The prefix is the directory containing the standard directory
|
||||
hierarchy (lib, bin, etc.). If the package is not installed to the
|
||||
system (:attr:`sys.prefix`) or a virtualenv (``site-packages``),
|
||||
``None`` is returned.
|
||||
|
||||
The path is the entry in :attr:`sys.path` that contains the package
|
||||
for import. If the package is not installed, it's assumed that the
|
||||
package was imported from the current working directory.
|
||||
"""
|
||||
root_mod_name, _, _ = import_name.partition(".")
|
||||
package_path = _find_package_path(root_mod_name)
|
||||
py_prefix = os.path.abspath(sys.prefix)
|
||||
|
||||
# installed to the system
|
||||
if package_path.startswith(py_prefix):
|
||||
return py_prefix, package_path
|
||||
|
||||
site_parent, site_folder = os.path.split(package_path)
|
||||
|
||||
# installed to a virtualenv
|
||||
if site_folder.lower() == "site-packages":
|
||||
parent, folder = os.path.split(site_parent)
|
||||
|
||||
# Windows (prefix/lib/site-packages)
|
||||
if folder.lower() == "lib":
|
||||
return parent, package_path
|
||||
|
||||
# Unix (prefix/lib/pythonX.Y/site-packages)
|
||||
if os.path.basename(parent).lower() == "lib":
|
||||
return os.path.dirname(parent), package_path
|
||||
|
||||
# something else (prefix/site-packages)
|
||||
return site_parent, package_path
|
||||
|
||||
# not installed
|
||||
return None, package_path
|
||||
416
dist/ba_data/python-site-packages/flask/sessions.py
vendored
Normal file
416
dist/ba_data/python-site-packages/flask/sessions.py
vendored
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
import hashlib
|
||||
import typing as t
|
||||
import warnings
|
||||
from collections.abc import MutableMapping
|
||||
from datetime import datetime
|
||||
|
||||
from itsdangerous import BadSignature
|
||||
from itsdangerous import URLSafeTimedSerializer
|
||||
from werkzeug.datastructures import CallbackDict
|
||||
|
||||
from .helpers import is_ip
|
||||
from .json.tag import TaggedJSONSerializer
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
import typing_extensions as te
|
||||
from .app import Flask
|
||||
from .wrappers import Request, Response
|
||||
|
||||
|
||||
class SessionMixin(MutableMapping):
|
||||
"""Expands a basic dictionary with session attributes."""
|
||||
|
||||
@property
|
||||
def permanent(self) -> bool:
|
||||
"""This reflects the ``'_permanent'`` key in the dict."""
|
||||
return self.get("_permanent", False)
|
||||
|
||||
@permanent.setter
|
||||
def permanent(self, value: bool) -> None:
|
||||
self["_permanent"] = bool(value)
|
||||
|
||||
#: Some implementations can detect whether a session is newly
|
||||
#: created, but that is not guaranteed. Use with caution. The mixin
|
||||
# default is hard-coded ``False``.
|
||||
new = False
|
||||
|
||||
#: Some implementations can detect changes to the session and set
|
||||
#: this when that happens. The mixin default is hard coded to
|
||||
#: ``True``.
|
||||
modified = True
|
||||
|
||||
#: Some implementations can detect when session data is read or
|
||||
#: written and set this when that happens. The mixin default is hard
|
||||
#: coded to ``True``.
|
||||
accessed = True
|
||||
|
||||
|
||||
class SecureCookieSession(CallbackDict, SessionMixin):
|
||||
"""Base class for sessions based on signed cookies.
|
||||
|
||||
This session backend will set the :attr:`modified` and
|
||||
:attr:`accessed` attributes. It cannot reliably track whether a
|
||||
session is new (vs. empty), so :attr:`new` remains hard coded to
|
||||
``False``.
|
||||
"""
|
||||
|
||||
#: When data is changed, this is set to ``True``. Only the session
|
||||
#: dictionary itself is tracked; if the session contains mutable
|
||||
#: data (for example a nested dict) then this must be set to
|
||||
#: ``True`` manually when modifying that data. The session cookie
|
||||
#: will only be written to the response if this is ``True``.
|
||||
modified = False
|
||||
|
||||
#: When data is read or written, this is set to ``True``. Used by
|
||||
# :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie``
|
||||
#: header, which allows caching proxies to cache different pages for
|
||||
#: different users.
|
||||
accessed = False
|
||||
|
||||
def __init__(self, initial: t.Any = None) -> None:
|
||||
def on_update(self) -> None:
|
||||
self.modified = True
|
||||
self.accessed = True
|
||||
|
||||
super().__init__(initial, on_update)
|
||||
|
||||
def __getitem__(self, key: str) -> t.Any:
|
||||
self.accessed = True
|
||||
return super().__getitem__(key)
|
||||
|
||||
def get(self, key: str, default: t.Any = None) -> t.Any:
|
||||
self.accessed = True
|
||||
return super().get(key, default)
|
||||
|
||||
def setdefault(self, key: str, default: t.Any = None) -> t.Any:
|
||||
self.accessed = True
|
||||
return super().setdefault(key, default)
|
||||
|
||||
|
||||
class NullSession(SecureCookieSession):
|
||||
"""Class used to generate nicer error messages if sessions are not
|
||||
available. Will still allow read-only access to the empty session
|
||||
but fail on setting.
|
||||
"""
|
||||
|
||||
def _fail(self, *args: t.Any, **kwargs: t.Any) -> "te.NoReturn":
|
||||
raise RuntimeError(
|
||||
"The session is unavailable because no secret "
|
||||
"key was set. Set the secret_key on the "
|
||||
"application to something unique and secret."
|
||||
)
|
||||
|
||||
__setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail # type: ignore # noqa: B950
|
||||
del _fail
|
||||
|
||||
|
||||
class SessionInterface:
|
||||
"""The basic interface you have to implement in order to replace the
|
||||
default session interface which uses werkzeug's securecookie
|
||||
implementation. The only methods you have to implement are
|
||||
:meth:`open_session` and :meth:`save_session`, the others have
|
||||
useful defaults which you don't need to change.
|
||||
|
||||
The session object returned by the :meth:`open_session` method has to
|
||||
provide a dictionary like interface plus the properties and methods
|
||||
from the :class:`SessionMixin`. We recommend just subclassing a dict
|
||||
and adding that mixin::
|
||||
|
||||
class Session(dict, SessionMixin):
|
||||
pass
|
||||
|
||||
If :meth:`open_session` returns ``None`` Flask will call into
|
||||
:meth:`make_null_session` to create a session that acts as replacement
|
||||
if the session support cannot work because some requirement is not
|
||||
fulfilled. The default :class:`NullSession` class that is created
|
||||
will complain that the secret key was not set.
|
||||
|
||||
To replace the session interface on an application all you have to do
|
||||
is to assign :attr:`flask.Flask.session_interface`::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.session_interface = MySessionInterface()
|
||||
|
||||
Multiple requests with the same session may be sent and handled
|
||||
concurrently. When implementing a new session interface, consider
|
||||
whether reads or writes to the backing store must be synchronized.
|
||||
There is no guarantee on the order in which the session for each
|
||||
request is opened or saved, it will occur in the order that requests
|
||||
begin and end processing.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
|
||||
#: :meth:`make_null_session` will look here for the class that should
|
||||
#: be created when a null session is requested. Likewise the
|
||||
#: :meth:`is_null_session` method will perform a typecheck against
|
||||
#: this type.
|
||||
null_session_class = NullSession
|
||||
|
||||
#: A flag that indicates if the session interface is pickle based.
|
||||
#: This can be used by Flask extensions to make a decision in regards
|
||||
#: to how to deal with the session object.
|
||||
#:
|
||||
#: .. versionadded:: 0.10
|
||||
pickle_based = False
|
||||
|
||||
def make_null_session(self, app: "Flask") -> NullSession:
|
||||
"""Creates a null session which acts as a replacement object if the
|
||||
real session support could not be loaded due to a configuration
|
||||
error. This mainly aids the user experience because the job of the
|
||||
null session is to still support lookup without complaining but
|
||||
modifications are answered with a helpful error message of what
|
||||
failed.
|
||||
|
||||
This creates an instance of :attr:`null_session_class` by default.
|
||||
"""
|
||||
return self.null_session_class()
|
||||
|
||||
def is_null_session(self, obj: object) -> bool:
|
||||
"""Checks if a given object is a null session. Null sessions are
|
||||
not asked to be saved.
|
||||
|
||||
This checks if the object is an instance of :attr:`null_session_class`
|
||||
by default.
|
||||
"""
|
||||
return isinstance(obj, self.null_session_class)
|
||||
|
||||
def get_cookie_name(self, app: "Flask") -> str:
|
||||
"""Returns the name of the session cookie.
|
||||
|
||||
Uses ``app.session_cookie_name`` which is set to ``SESSION_COOKIE_NAME``
|
||||
"""
|
||||
return app.session_cookie_name
|
||||
|
||||
def get_cookie_domain(self, app: "Flask") -> t.Optional[str]:
|
||||
"""Returns the domain that should be set for the session cookie.
|
||||
|
||||
Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise
|
||||
falls back to detecting the domain based on ``SERVER_NAME``.
|
||||
|
||||
Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is
|
||||
updated to avoid re-running the logic.
|
||||
"""
|
||||
|
||||
rv = app.config["SESSION_COOKIE_DOMAIN"]
|
||||
|
||||
# set explicitly, or cached from SERVER_NAME detection
|
||||
# if False, return None
|
||||
if rv is not None:
|
||||
return rv if rv else None
|
||||
|
||||
rv = app.config["SERVER_NAME"]
|
||||
|
||||
# server name not set, cache False to return none next time
|
||||
if not rv:
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = False
|
||||
return None
|
||||
|
||||
# chop off the port which is usually not supported by browsers
|
||||
# remove any leading '.' since we'll add that later
|
||||
rv = rv.rsplit(":", 1)[0].lstrip(".")
|
||||
|
||||
if "." not in rv:
|
||||
# Chrome doesn't allow names without a '.'. This should only
|
||||
# come up with localhost. Hack around this by not setting
|
||||
# the name, and show a warning.
|
||||
warnings.warn(
|
||||
f"{rv!r} is not a valid cookie domain, it must contain"
|
||||
" a '.'. Add an entry to your hosts file, for example"
|
||||
f" '{rv}.localdomain', and use that instead."
|
||||
)
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = False
|
||||
return None
|
||||
|
||||
ip = is_ip(rv)
|
||||
|
||||
if ip:
|
||||
warnings.warn(
|
||||
"The session cookie domain is an IP address. This may not work"
|
||||
" as intended in some browsers. Add an entry to your hosts"
|
||||
' file, for example "localhost.localdomain", and use that'
|
||||
" instead."
|
||||
)
|
||||
|
||||
# if this is not an ip and app is mounted at the root, allow subdomain
|
||||
# matching by adding a '.' prefix
|
||||
if self.get_cookie_path(app) == "/" and not ip:
|
||||
rv = f".{rv}"
|
||||
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
||||
return rv
|
||||
|
||||
def get_cookie_path(self, app: "Flask") -> str:
|
||||
"""Returns the path for which the cookie should be valid. The
|
||||
default implementation uses the value from the ``SESSION_COOKIE_PATH``
|
||||
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
|
||||
uses ``/`` if it's ``None``.
|
||||
"""
|
||||
return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]
|
||||
|
||||
def get_cookie_httponly(self, app: "Flask") -> bool:
|
||||
"""Returns True if the session cookie should be httponly. This
|
||||
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
|
||||
config var.
|
||||
"""
|
||||
return app.config["SESSION_COOKIE_HTTPONLY"]
|
||||
|
||||
def get_cookie_secure(self, app: "Flask") -> bool:
|
||||
"""Returns True if the cookie should be secure. This currently
|
||||
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
|
||||
"""
|
||||
return app.config["SESSION_COOKIE_SECURE"]
|
||||
|
||||
def get_cookie_samesite(self, app: "Flask") -> str:
|
||||
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
|
||||
``SameSite`` attribute. This currently just returns the value of
|
||||
the :data:`SESSION_COOKIE_SAMESITE` setting.
|
||||
"""
|
||||
return app.config["SESSION_COOKIE_SAMESITE"]
|
||||
|
||||
def get_expiration_time(
|
||||
self, app: "Flask", session: SessionMixin
|
||||
) -> t.Optional[datetime]:
|
||||
"""A helper method that returns an expiration date for the session
|
||||
or ``None`` if the session is linked to the browser session. The
|
||||
default implementation returns now + the permanent session
|
||||
lifetime configured on the application.
|
||||
"""
|
||||
if session.permanent:
|
||||
return datetime.utcnow() + app.permanent_session_lifetime
|
||||
return None
|
||||
|
||||
def should_set_cookie(self, app: "Flask", session: SessionMixin) -> bool:
|
||||
"""Used by session backends to determine if a ``Set-Cookie`` header
|
||||
should be set for this session cookie for this response. If the session
|
||||
has been modified, the cookie is set. If the session is permanent and
|
||||
the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is
|
||||
always set.
|
||||
|
||||
This check is usually skipped if the session was deleted.
|
||||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
|
||||
return session.modified or (
|
||||
session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
|
||||
)
|
||||
|
||||
def open_session(
|
||||
self, app: "Flask", request: "Request"
|
||||
) -> t.Optional[SessionMixin]:
|
||||
"""This is called at the beginning of each request, after
|
||||
pushing the request context, before matching the URL.
|
||||
|
||||
This must return an object which implements a dictionary-like
|
||||
interface as well as the :class:`SessionMixin` interface.
|
||||
|
||||
This will return ``None`` to indicate that loading failed in
|
||||
some way that is not immediately an error. The request
|
||||
context will fall back to using :meth:`make_null_session`
|
||||
in this case.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def save_session(
|
||||
self, app: "Flask", session: SessionMixin, response: "Response"
|
||||
) -> None:
|
||||
"""This is called at the end of each request, after generating
|
||||
a response, before removing the request context. It is skipped
|
||||
if :meth:`is_null_session` returns ``True``.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
session_json_serializer = TaggedJSONSerializer()
|
||||
|
||||
|
||||
class SecureCookieSessionInterface(SessionInterface):
|
||||
"""The default session interface that stores sessions in signed cookies
|
||||
through the :mod:`itsdangerous` module.
|
||||
"""
|
||||
|
||||
#: the salt that should be applied on top of the secret key for the
|
||||
#: signing of cookie based sessions.
|
||||
salt = "cookie-session"
|
||||
#: the hash function to use for the signature. The default is sha1
|
||||
digest_method = staticmethod(hashlib.sha1)
|
||||
#: the name of the itsdangerous supported key derivation. The default
|
||||
#: is hmac.
|
||||
key_derivation = "hmac"
|
||||
#: A python serializer for the payload. The default is a compact
|
||||
#: JSON derived serializer with support for some extra Python types
|
||||
#: such as datetime objects or tuples.
|
||||
serializer = session_json_serializer
|
||||
session_class = SecureCookieSession
|
||||
|
||||
def get_signing_serializer(
|
||||
self, app: "Flask"
|
||||
) -> t.Optional[URLSafeTimedSerializer]:
|
||||
if not app.secret_key:
|
||||
return None
|
||||
signer_kwargs = dict(
|
||||
key_derivation=self.key_derivation, digest_method=self.digest_method
|
||||
)
|
||||
return URLSafeTimedSerializer(
|
||||
app.secret_key,
|
||||
salt=self.salt,
|
||||
serializer=self.serializer,
|
||||
signer_kwargs=signer_kwargs,
|
||||
)
|
||||
|
||||
def open_session(
|
||||
self, app: "Flask", request: "Request"
|
||||
) -> t.Optional[SecureCookieSession]:
|
||||
s = self.get_signing_serializer(app)
|
||||
if s is None:
|
||||
return None
|
||||
val = request.cookies.get(self.get_cookie_name(app))
|
||||
if not val:
|
||||
return self.session_class()
|
||||
max_age = int(app.permanent_session_lifetime.total_seconds())
|
||||
try:
|
||||
data = s.loads(val, max_age=max_age)
|
||||
return self.session_class(data)
|
||||
except BadSignature:
|
||||
return self.session_class()
|
||||
|
||||
def save_session(
|
||||
self, app: "Flask", session: SessionMixin, response: "Response"
|
||||
) -> None:
|
||||
name = self.get_cookie_name(app)
|
||||
domain = self.get_cookie_domain(app)
|
||||
path = self.get_cookie_path(app)
|
||||
secure = self.get_cookie_secure(app)
|
||||
samesite = self.get_cookie_samesite(app)
|
||||
|
||||
# If the session is modified to be empty, remove the cookie.
|
||||
# If the session is empty, return without setting the cookie.
|
||||
if not session:
|
||||
if session.modified:
|
||||
response.delete_cookie(
|
||||
name, domain=domain, path=path, secure=secure, samesite=samesite
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
# Add a "Vary: Cookie" header if the session was accessed at all.
|
||||
if session.accessed:
|
||||
response.vary.add("Cookie")
|
||||
|
||||
if not self.should_set_cookie(app, session):
|
||||
return
|
||||
|
||||
httponly = self.get_cookie_httponly(app)
|
||||
expires = self.get_expiration_time(app, session)
|
||||
val = self.get_signing_serializer(app).dumps(dict(session)) # type: ignore
|
||||
response.set_cookie(
|
||||
name,
|
||||
val, # type: ignore
|
||||
expires=expires,
|
||||
httponly=httponly,
|
||||
domain=domain,
|
||||
path=path,
|
||||
secure=secure,
|
||||
samesite=samesite,
|
||||
)
|
||||
56
dist/ba_data/python-site-packages/flask/signals.py
vendored
Normal file
56
dist/ba_data/python-site-packages/flask/signals.py
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import typing as t
|
||||
|
||||
try:
|
||||
from blinker import Namespace
|
||||
|
||||
signals_available = True
|
||||
except ImportError:
|
||||
signals_available = False
|
||||
|
||||
class Namespace: # type: ignore
|
||||
def signal(self, name: str, doc: t.Optional[str] = None) -> "_FakeSignal":
|
||||
return _FakeSignal(name, doc)
|
||||
|
||||
class _FakeSignal:
|
||||
"""If blinker is unavailable, create a fake class with the same
|
||||
interface that allows sending of signals but will fail with an
|
||||
error on anything else. Instead of doing anything on send, it
|
||||
will just ignore the arguments and do nothing instead.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, doc: t.Optional[str] = None) -> None:
|
||||
self.name = name
|
||||
self.__doc__ = doc
|
||||
|
||||
def send(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
pass
|
||||
|
||||
def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
||||
raise RuntimeError(
|
||||
"Signalling support is unavailable because the blinker"
|
||||
" library is not installed."
|
||||
) from None
|
||||
|
||||
connect = connect_via = connected_to = temporarily_connected_to = _fail
|
||||
disconnect = _fail
|
||||
has_receivers_for = receivers_for = _fail
|
||||
del _fail
|
||||
|
||||
|
||||
# The namespace for code signals. If you are not Flask code, do
|
||||
# not put signals in here. Create your own namespace instead.
|
||||
_signals = Namespace()
|
||||
|
||||
|
||||
# Core signals. For usage examples grep the source code or consult
|
||||
# the API documentation in docs/api.rst as well as docs/signals.rst
|
||||
template_rendered = _signals.signal("template-rendered")
|
||||
before_render_template = _signals.signal("before-render-template")
|
||||
request_started = _signals.signal("request-started")
|
||||
request_finished = _signals.signal("request-finished")
|
||||
request_tearing_down = _signals.signal("request-tearing-down")
|
||||
got_request_exception = _signals.signal("got-request-exception")
|
||||
appcontext_tearing_down = _signals.signal("appcontext-tearing-down")
|
||||
appcontext_pushed = _signals.signal("appcontext-pushed")
|
||||
appcontext_popped = _signals.signal("appcontext-popped")
|
||||
message_flashed = _signals.signal("message-flashed")
|
||||
165
dist/ba_data/python-site-packages/flask/templating.py
vendored
Normal file
165
dist/ba_data/python-site-packages/flask/templating.py
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import typing as t
|
||||
|
||||
from jinja2 import BaseLoader
|
||||
from jinja2 import Environment as BaseEnvironment
|
||||
from jinja2 import Template
|
||||
from jinja2 import TemplateNotFound
|
||||
|
||||
from .globals import _app_ctx_stack
|
||||
from .globals import _request_ctx_stack
|
||||
from .signals import before_render_template
|
||||
from .signals import template_rendered
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from .app import Flask
|
||||
from .scaffold import Scaffold
|
||||
|
||||
|
||||
def _default_template_ctx_processor() -> t.Dict[str, t.Any]:
|
||||
"""Default template context processor. Injects `request`,
|
||||
`session` and `g`.
|
||||
"""
|
||||
reqctx = _request_ctx_stack.top
|
||||
appctx = _app_ctx_stack.top
|
||||
rv = {}
|
||||
if appctx is not None:
|
||||
rv["g"] = appctx.g
|
||||
if reqctx is not None:
|
||||
rv["request"] = reqctx.request
|
||||
rv["session"] = reqctx.session
|
||||
return rv
|
||||
|
||||
|
||||
class Environment(BaseEnvironment):
|
||||
"""Works like a regular Jinja2 environment but has some additional
|
||||
knowledge of how Flask's blueprint works so that it can prepend the
|
||||
name of the blueprint to referenced templates if necessary.
|
||||
"""
|
||||
|
||||
def __init__(self, app: "Flask", **options: t.Any) -> None:
|
||||
if "loader" not in options:
|
||||
options["loader"] = app.create_global_jinja_loader()
|
||||
BaseEnvironment.__init__(self, **options)
|
||||
self.app = app
|
||||
|
||||
|
||||
class DispatchingJinjaLoader(BaseLoader):
|
||||
"""A loader that looks for templates in the application and all
|
||||
the blueprint folders.
|
||||
"""
|
||||
|
||||
def __init__(self, app: "Flask") -> None:
|
||||
self.app = app
|
||||
|
||||
def get_source( # type: ignore
|
||||
self, environment: Environment, template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
|
||||
if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
|
||||
return self._get_source_explained(environment, template)
|
||||
return self._get_source_fast(environment, template)
|
||||
|
||||
def _get_source_explained(
|
||||
self, environment: Environment, template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
|
||||
attempts = []
|
||||
rv: t.Optional[t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]]
|
||||
trv: t.Optional[
|
||||
t.Tuple[str, t.Optional[str], t.Optional[t.Callable[[], bool]]]
|
||||
] = None
|
||||
|
||||
for srcobj, loader in self._iter_loaders(template):
|
||||
try:
|
||||
rv = loader.get_source(environment, template)
|
||||
if trv is None:
|
||||
trv = rv
|
||||
except TemplateNotFound:
|
||||
rv = None
|
||||
attempts.append((loader, srcobj, rv))
|
||||
|
||||
from .debughelpers import explain_template_loading_attempts
|
||||
|
||||
explain_template_loading_attempts(self.app, template, attempts)
|
||||
|
||||
if trv is not None:
|
||||
return trv
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def _get_source_fast(
|
||||
self, environment: Environment, template: str
|
||||
) -> t.Tuple[str, t.Optional[str], t.Optional[t.Callable]]:
|
||||
for _srcobj, loader in self._iter_loaders(template):
|
||||
try:
|
||||
return loader.get_source(environment, template)
|
||||
except TemplateNotFound:
|
||||
continue
|
||||
raise TemplateNotFound(template)
|
||||
|
||||
def _iter_loaders(
|
||||
self, template: str
|
||||
) -> t.Generator[t.Tuple["Scaffold", BaseLoader], None, None]:
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
yield self.app, loader
|
||||
|
||||
for blueprint in self.app.iter_blueprints():
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
yield blueprint, loader
|
||||
|
||||
def list_templates(self) -> t.List[str]:
|
||||
result = set()
|
||||
loader = self.app.jinja_loader
|
||||
if loader is not None:
|
||||
result.update(loader.list_templates())
|
||||
|
||||
for blueprint in self.app.iter_blueprints():
|
||||
loader = blueprint.jinja_loader
|
||||
if loader is not None:
|
||||
for template in loader.list_templates():
|
||||
result.add(template)
|
||||
|
||||
return list(result)
|
||||
|
||||
|
||||
def _render(template: Template, context: dict, app: "Flask") -> str:
|
||||
"""Renders the template and fires the signal"""
|
||||
|
||||
before_render_template.send(app, template=template, context=context)
|
||||
rv = template.render(context)
|
||||
template_rendered.send(app, template=template, context=context)
|
||||
return rv
|
||||
|
||||
|
||||
def render_template(
|
||||
template_name_or_list: t.Union[str, t.List[str]], **context: t.Any
|
||||
) -> str:
|
||||
"""Renders a template from the template folder with the given
|
||||
context.
|
||||
|
||||
:param template_name_or_list: the name of the template to be
|
||||
rendered, or an iterable with template names
|
||||
the first one existing will be rendered
|
||||
:param context: the variables that should be available in the
|
||||
context of the template.
|
||||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(
|
||||
ctx.app.jinja_env.get_or_select_template(template_name_or_list),
|
||||
context,
|
||||
ctx.app,
|
||||
)
|
||||
|
||||
|
||||
def render_template_string(source: str, **context: t.Any) -> str:
|
||||
"""Renders a template from the given template source string
|
||||
with the given context. Template variables will be autoescaped.
|
||||
|
||||
:param source: the source code of the template to be
|
||||
rendered
|
||||
:param context: the variables that should be available in the
|
||||
context of the template.
|
||||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(ctx.app.jinja_env.from_string(source), context, ctx.app)
|
||||
298
dist/ba_data/python-site-packages/flask/testing.py
vendored
Normal file
298
dist/ba_data/python-site-packages/flask/testing.py
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
import typing as t
|
||||
from contextlib import contextmanager
|
||||
from copy import copy
|
||||
from types import TracebackType
|
||||
|
||||
import werkzeug.test
|
||||
from click.testing import CliRunner
|
||||
from werkzeug.test import Client
|
||||
from werkzeug.urls import url_parse
|
||||
from werkzeug.wrappers import Request as BaseRequest
|
||||
|
||||
from .cli import ScriptInfo
|
||||
from .globals import _request_ctx_stack
|
||||
from .json import dumps as json_dumps
|
||||
from .sessions import SessionMixin
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from werkzeug.test import TestResponse
|
||||
|
||||
from .app import Flask
|
||||
|
||||
|
||||
class EnvironBuilder(werkzeug.test.EnvironBuilder):
|
||||
"""An :class:`~werkzeug.test.EnvironBuilder`, that takes defaults from the
|
||||
application.
|
||||
|
||||
:param app: The Flask application to configure the environment from.
|
||||
:param path: URL path being requested.
|
||||
:param base_url: Base URL where the app is being served, which
|
||||
``path`` is relative to. If not given, built from
|
||||
:data:`PREFERRED_URL_SCHEME`, ``subdomain``,
|
||||
:data:`SERVER_NAME`, and :data:`APPLICATION_ROOT`.
|
||||
:param subdomain: Subdomain name to append to :data:`SERVER_NAME`.
|
||||
:param url_scheme: Scheme to use instead of
|
||||
:data:`PREFERRED_URL_SCHEME`.
|
||||
:param json: If given, this is serialized as JSON and passed as
|
||||
``data``. Also defaults ``content_type`` to
|
||||
``application/json``.
|
||||
:param args: other positional arguments passed to
|
||||
:class:`~werkzeug.test.EnvironBuilder`.
|
||||
:param kwargs: other keyword arguments passed to
|
||||
:class:`~werkzeug.test.EnvironBuilder`.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
app: "Flask",
|
||||
path: str = "/",
|
||||
base_url: t.Optional[str] = None,
|
||||
subdomain: t.Optional[str] = None,
|
||||
url_scheme: t.Optional[str] = None,
|
||||
*args: t.Any,
|
||||
**kwargs: t.Any,
|
||||
) -> None:
|
||||
assert not (base_url or subdomain or url_scheme) or (
|
||||
base_url is not None
|
||||
) != bool(
|
||||
subdomain or url_scheme
|
||||
), 'Cannot pass "subdomain" or "url_scheme" with "base_url".'
|
||||
|
||||
if base_url is None:
|
||||
http_host = app.config.get("SERVER_NAME") or "localhost"
|
||||
app_root = app.config["APPLICATION_ROOT"]
|
||||
|
||||
if subdomain:
|
||||
http_host = f"{subdomain}.{http_host}"
|
||||
|
||||
if url_scheme is None:
|
||||
url_scheme = app.config["PREFERRED_URL_SCHEME"]
|
||||
|
||||
url = url_parse(path)
|
||||
base_url = (
|
||||
f"{url.scheme or url_scheme}://{url.netloc or http_host}"
|
||||
f"/{app_root.lstrip('/')}"
|
||||
)
|
||||
path = url.path
|
||||
|
||||
if url.query:
|
||||
sep = b"?" if isinstance(url.query, bytes) else "?"
|
||||
path += sep + url.query
|
||||
|
||||
self.app = app
|
||||
super().__init__(path, base_url, *args, **kwargs)
|
||||
|
||||
def json_dumps(self, obj: t.Any, **kwargs: t.Any) -> str: # type: ignore
|
||||
"""Serialize ``obj`` to a JSON-formatted string.
|
||||
|
||||
The serialization will be configured according to the config associated
|
||||
with this EnvironBuilder's ``app``.
|
||||
"""
|
||||
kwargs.setdefault("app", self.app)
|
||||
return json_dumps(obj, **kwargs)
|
||||
|
||||
|
||||
class FlaskClient(Client):
|
||||
"""Works like a regular Werkzeug test client but has some knowledge about
|
||||
how Flask works to defer the cleanup of the request context stack to the
|
||||
end of a ``with`` body when used in a ``with`` statement. For general
|
||||
information about how to use this class refer to
|
||||
:class:`werkzeug.test.Client`.
|
||||
|
||||
.. versionchanged:: 0.12
|
||||
`app.test_client()` includes preset default environment, which can be
|
||||
set after instantiation of the `app.test_client()` object in
|
||||
`client.environ_base`.
|
||||
|
||||
Basic usage is outlined in the :doc:`/testing` chapter.
|
||||
"""
|
||||
|
||||
application: "Flask"
|
||||
preserve_context = False
|
||||
|
||||
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.environ_base = {
|
||||
"REMOTE_ADDR": "127.0.0.1",
|
||||
"HTTP_USER_AGENT": f"werkzeug/{werkzeug.__version__}",
|
||||
}
|
||||
|
||||
@contextmanager
|
||||
def session_transaction(
|
||||
self, *args: t.Any, **kwargs: t.Any
|
||||
) -> t.Generator[SessionMixin, None, None]:
|
||||
"""When used in combination with a ``with`` statement this opens a
|
||||
session transaction. This can be used to modify the session that
|
||||
the test client uses. Once the ``with`` block is left the session is
|
||||
stored back.
|
||||
|
||||
::
|
||||
|
||||
with client.session_transaction() as session:
|
||||
session['value'] = 42
|
||||
|
||||
Internally this is implemented by going through a temporary test
|
||||
request context and since session handling could depend on
|
||||
request variables this function accepts the same arguments as
|
||||
:meth:`~flask.Flask.test_request_context` which are directly
|
||||
passed through.
|
||||
"""
|
||||
if self.cookie_jar is None:
|
||||
raise RuntimeError(
|
||||
"Session transactions only make sense with cookies enabled."
|
||||
)
|
||||
app = self.application
|
||||
environ_overrides = kwargs.setdefault("environ_overrides", {})
|
||||
self.cookie_jar.inject_wsgi(environ_overrides)
|
||||
outer_reqctx = _request_ctx_stack.top
|
||||
with app.test_request_context(*args, **kwargs) as c:
|
||||
session_interface = app.session_interface
|
||||
sess = session_interface.open_session(app, c.request)
|
||||
if sess is None:
|
||||
raise RuntimeError(
|
||||
"Session backend did not open a session. Check the configuration"
|
||||
)
|
||||
|
||||
# Since we have to open a new request context for the session
|
||||
# handling we want to make sure that we hide out own context
|
||||
# from the caller. By pushing the original request context
|
||||
# (or None) on top of this and popping it we get exactly that
|
||||
# behavior. It's important to not use the push and pop
|
||||
# methods of the actual request context object since that would
|
||||
# mean that cleanup handlers are called
|
||||
_request_ctx_stack.push(outer_reqctx)
|
||||
try:
|
||||
yield sess
|
||||
finally:
|
||||
_request_ctx_stack.pop()
|
||||
|
||||
resp = app.response_class()
|
||||
if not session_interface.is_null_session(sess):
|
||||
session_interface.save_session(app, sess, resp)
|
||||
headers = resp.get_wsgi_headers(c.request.environ)
|
||||
self.cookie_jar.extract_wsgi(c.request.environ, headers)
|
||||
|
||||
def open(
|
||||
self,
|
||||
*args: t.Any,
|
||||
buffered: bool = False,
|
||||
follow_redirects: bool = False,
|
||||
**kwargs: t.Any,
|
||||
) -> "TestResponse":
|
||||
as_tuple = kwargs.pop("as_tuple", None)
|
||||
|
||||
# Same logic as super.open, but apply environ_base and preserve_context.
|
||||
request = None
|
||||
|
||||
def copy_environ(other):
|
||||
return {
|
||||
**self.environ_base,
|
||||
**other,
|
||||
"flask._preserve_context": self.preserve_context,
|
||||
}
|
||||
|
||||
if not kwargs and len(args) == 1:
|
||||
arg = args[0]
|
||||
|
||||
if isinstance(arg, werkzeug.test.EnvironBuilder):
|
||||
builder = copy(arg)
|
||||
builder.environ_base = copy_environ(builder.environ_base or {})
|
||||
request = builder.get_request()
|
||||
elif isinstance(arg, dict):
|
||||
request = EnvironBuilder.from_environ(
|
||||
arg, app=self.application, environ_base=copy_environ({})
|
||||
).get_request()
|
||||
elif isinstance(arg, BaseRequest):
|
||||
request = copy(arg)
|
||||
request.environ = copy_environ(request.environ)
|
||||
|
||||
if request is None:
|
||||
kwargs["environ_base"] = copy_environ(kwargs.get("environ_base", {}))
|
||||
builder = EnvironBuilder(self.application, *args, **kwargs)
|
||||
|
||||
try:
|
||||
request = builder.get_request()
|
||||
finally:
|
||||
builder.close()
|
||||
|
||||
if as_tuple is not None:
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"'as_tuple' is deprecated and will be removed in"
|
||||
" Werkzeug 2.1 and Flask 2.1. Use"
|
||||
" 'response.request.environ' instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
return super().open(
|
||||
request,
|
||||
as_tuple=as_tuple,
|
||||
buffered=buffered,
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
else:
|
||||
return super().open(
|
||||
request,
|
||||
buffered=buffered,
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
|
||||
def __enter__(self) -> "FlaskClient":
|
||||
if self.preserve_context:
|
||||
raise RuntimeError("Cannot nest client invocations")
|
||||
self.preserve_context = True
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self, exc_type: type, exc_value: BaseException, tb: TracebackType
|
||||
) -> None:
|
||||
self.preserve_context = False
|
||||
|
||||
# Normally the request context is preserved until the next
|
||||
# request in the same thread comes. When the client exits we
|
||||
# want to clean up earlier. Pop request contexts until the stack
|
||||
# is empty or a non-preserved one is found.
|
||||
while True:
|
||||
top = _request_ctx_stack.top
|
||||
|
||||
if top is not None and top.preserved:
|
||||
top.pop()
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
class FlaskCliRunner(CliRunner):
|
||||
"""A :class:`~click.testing.CliRunner` for testing a Flask app's
|
||||
CLI commands. Typically created using
|
||||
:meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
|
||||
"""
|
||||
|
||||
def __init__(self, app: "Flask", **kwargs: t.Any) -> None:
|
||||
self.app = app
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def invoke( # type: ignore
|
||||
self, cli: t.Any = None, args: t.Any = None, **kwargs: t.Any
|
||||
) -> t.Any:
|
||||
"""Invokes a CLI command in an isolated environment. See
|
||||
:meth:`CliRunner.invoke <click.testing.CliRunner.invoke>` for
|
||||
full method documentation. See :ref:`testing-cli` for examples.
|
||||
|
||||
If the ``obj`` argument is not given, passes an instance of
|
||||
:class:`~flask.cli.ScriptInfo` that knows how to load the Flask
|
||||
app being tested.
|
||||
|
||||
:param cli: Command object to invoke. Default is the app's
|
||||
:attr:`~flask.app.Flask.cli` group.
|
||||
:param args: List of strings to invoke the command with.
|
||||
|
||||
:return: a :class:`~click.testing.Result` object.
|
||||
"""
|
||||
if cli is None:
|
||||
cli = self.app.cli # type: ignore
|
||||
|
||||
if "obj" not in kwargs:
|
||||
kwargs["obj"] = ScriptInfo(create_app=lambda: self.app)
|
||||
|
||||
return super().invoke(cli, args, **kwargs)
|
||||
49
dist/ba_data/python-site-packages/flask/typing.py
vendored
Normal file
49
dist/ba_data/python-site-packages/flask/typing.py
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import typing as t
|
||||
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
from _typeshed.wsgi import WSGIApplication # noqa: F401
|
||||
from werkzeug.datastructures import Headers # noqa: F401
|
||||
from .wrappers import Response # noqa: F401
|
||||
|
||||
# The possible types that are directly convertible or are a Response object.
|
||||
ResponseValue = t.Union[
|
||||
"Response",
|
||||
t.AnyStr,
|
||||
t.Dict[str, t.Any], # any jsonify-able dict
|
||||
t.Generator[t.AnyStr, None, None],
|
||||
]
|
||||
StatusCode = int
|
||||
|
||||
# the possible types for an individual HTTP header
|
||||
HeaderName = str
|
||||
HeaderValue = t.Union[str, t.List[str], t.Tuple[str, ...]]
|
||||
|
||||
# the possible types for HTTP headers
|
||||
HeadersValue = t.Union[
|
||||
"Headers", t.Dict[HeaderName, HeaderValue], t.List[t.Tuple[HeaderName, HeaderValue]]
|
||||
]
|
||||
|
||||
# The possible types returned by a route function.
|
||||
ResponseReturnValue = t.Union[
|
||||
ResponseValue,
|
||||
t.Tuple[ResponseValue, HeadersValue],
|
||||
t.Tuple[ResponseValue, StatusCode],
|
||||
t.Tuple[ResponseValue, StatusCode, HeadersValue],
|
||||
"WSGIApplication",
|
||||
]
|
||||
|
||||
GenericException = t.TypeVar("GenericException", bound=Exception, contravariant=True)
|
||||
|
||||
AppOrBlueprintKey = t.Optional[str] # The App key is None, whereas blueprints are named
|
||||
AfterRequestCallable = t.Callable[["Response"], "Response"]
|
||||
BeforeFirstRequestCallable = t.Callable[[], None]
|
||||
BeforeRequestCallable = t.Callable[[], t.Optional[ResponseReturnValue]]
|
||||
TeardownCallable = t.Callable[[t.Optional[BaseException]], None]
|
||||
TemplateContextProcessorCallable = t.Callable[[], t.Dict[str, t.Any]]
|
||||
TemplateFilterCallable = t.Callable[..., t.Any]
|
||||
TemplateGlobalCallable = t.Callable[..., t.Any]
|
||||
TemplateTestCallable = t.Callable[..., bool]
|
||||
URLDefaultCallable = t.Callable[[str, dict], None]
|
||||
URLValuePreprocessorCallable = t.Callable[[t.Optional[str], t.Optional[dict]], None]
|
||||
ErrorHandlerCallable = t.Callable[[GenericException], ResponseReturnValue]
|
||||
158
dist/ba_data/python-site-packages/flask/views.py
vendored
Normal file
158
dist/ba_data/python-site-packages/flask/views.py
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import typing as t
|
||||
|
||||
from .globals import current_app
|
||||
from .globals import request
|
||||
from .typing import ResponseReturnValue
|
||||
|
||||
|
||||
http_method_funcs = frozenset(
|
||||
["get", "post", "head", "options", "delete", "put", "trace", "patch"]
|
||||
)
|
||||
|
||||
|
||||
class View:
|
||||
"""Alternative way to use view functions. A subclass has to implement
|
||||
:meth:`dispatch_request` which is called with the view arguments from
|
||||
the URL routing system. If :attr:`methods` is provided the methods
|
||||
do not have to be passed to the :meth:`~flask.Flask.add_url_rule`
|
||||
method explicitly::
|
||||
|
||||
class MyView(View):
|
||||
methods = ['GET']
|
||||
|
||||
def dispatch_request(self, name):
|
||||
return f"Hello {name}!"
|
||||
|
||||
app.add_url_rule('/hello/<name>', view_func=MyView.as_view('myview'))
|
||||
|
||||
When you want to decorate a pluggable view you will have to either do that
|
||||
when the view function is created (by wrapping the return value of
|
||||
:meth:`as_view`) or you can use the :attr:`decorators` attribute::
|
||||
|
||||
class SecretView(View):
|
||||
methods = ['GET']
|
||||
decorators = [superuser_required]
|
||||
|
||||
def dispatch_request(self):
|
||||
...
|
||||
|
||||
The decorators stored in the decorators list are applied one after another
|
||||
when the view function is created. Note that you can *not* use the class
|
||||
based decorators since those would decorate the view class and not the
|
||||
generated view function!
|
||||
"""
|
||||
|
||||
#: A list of methods this view can handle.
|
||||
methods: t.Optional[t.List[str]] = None
|
||||
|
||||
#: Setting this disables or force-enables the automatic options handling.
|
||||
provide_automatic_options: t.Optional[bool] = None
|
||||
|
||||
#: The canonical way to decorate class-based views is to decorate the
|
||||
#: return value of as_view(). However since this moves parts of the
|
||||
#: logic from the class declaration to the place where it's hooked
|
||||
#: into the routing system.
|
||||
#:
|
||||
#: You can place one or more decorators in this list and whenever the
|
||||
#: view function is created the result is automatically decorated.
|
||||
#:
|
||||
#: .. versionadded:: 0.8
|
||||
decorators: t.List[t.Callable] = []
|
||||
|
||||
def dispatch_request(self) -> ResponseReturnValue:
|
||||
"""Subclasses have to override this method to implement the
|
||||
actual view function code. This method is called with all
|
||||
the arguments from the URL rule.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def as_view(
|
||||
cls, name: str, *class_args: t.Any, **class_kwargs: t.Any
|
||||
) -> t.Callable:
|
||||
"""Converts the class into an actual view function that can be used
|
||||
with the routing system. Internally this generates a function on the
|
||||
fly which will instantiate the :class:`View` on each request and call
|
||||
the :meth:`dispatch_request` method on it.
|
||||
|
||||
The arguments passed to :meth:`as_view` are forwarded to the
|
||||
constructor of the class.
|
||||
"""
|
||||
|
||||
def view(*args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||
self = view.view_class(*class_args, **class_kwargs) # type: ignore
|
||||
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
|
||||
|
||||
if cls.decorators:
|
||||
view.__name__ = name
|
||||
view.__module__ = cls.__module__
|
||||
for decorator in cls.decorators:
|
||||
view = decorator(view)
|
||||
|
||||
# We attach the view class to the view function for two reasons:
|
||||
# first of all it allows us to easily figure out what class-based
|
||||
# view this thing came from, secondly it's also used for instantiating
|
||||
# the view class so you can actually replace it with something else
|
||||
# for testing purposes and debugging.
|
||||
view.view_class = cls # type: ignore
|
||||
view.__name__ = name
|
||||
view.__doc__ = cls.__doc__
|
||||
view.__module__ = cls.__module__
|
||||
view.methods = cls.methods # type: ignore
|
||||
view.provide_automatic_options = cls.provide_automatic_options # type: ignore
|
||||
return view
|
||||
|
||||
|
||||
class MethodViewType(type):
|
||||
"""Metaclass for :class:`MethodView` that determines what methods the view
|
||||
defines.
|
||||
"""
|
||||
|
||||
def __init__(cls, name, bases, d):
|
||||
super().__init__(name, bases, d)
|
||||
|
||||
if "methods" not in d:
|
||||
methods = set()
|
||||
|
||||
for base in bases:
|
||||
if getattr(base, "methods", None):
|
||||
methods.update(base.methods)
|
||||
|
||||
for key in http_method_funcs:
|
||||
if hasattr(cls, key):
|
||||
methods.add(key.upper())
|
||||
|
||||
# If we have no method at all in there we don't want to add a
|
||||
# method list. This is for instance the case for the base class
|
||||
# or another subclass of a base method view that does not introduce
|
||||
# new methods.
|
||||
if methods:
|
||||
cls.methods = methods
|
||||
|
||||
|
||||
class MethodView(View, metaclass=MethodViewType):
|
||||
"""A class-based view that dispatches request methods to the corresponding
|
||||
class methods. For example, if you implement a ``get`` method, it will be
|
||||
used to handle ``GET`` requests. ::
|
||||
|
||||
class CounterAPI(MethodView):
|
||||
def get(self):
|
||||
return session.get('counter', 0)
|
||||
|
||||
def post(self):
|
||||
session['counter'] = session.get('counter', 0) + 1
|
||||
return 'OK'
|
||||
|
||||
app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter'))
|
||||
"""
|
||||
|
||||
def dispatch_request(self, *args: t.Any, **kwargs: t.Any) -> ResponseReturnValue:
|
||||
meth = getattr(self, request.method.lower(), None)
|
||||
|
||||
# If the request method is HEAD and we don't have a handler for it
|
||||
# retry with GET.
|
||||
if meth is None and request.method == "HEAD":
|
||||
meth = getattr(self, "get", None)
|
||||
|
||||
assert meth is not None, f"Unimplemented method {request.method!r}"
|
||||
return current_app.ensure_sync(meth)(*args, **kwargs)
|
||||
167
dist/ba_data/python-site-packages/flask/wrappers.py
vendored
Normal file
167
dist/ba_data/python-site-packages/flask/wrappers.py
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import typing as t
|
||||
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.wrappers import Request as RequestBase
|
||||
from werkzeug.wrappers import Response as ResponseBase
|
||||
|
||||
from . import json
|
||||
from .globals import current_app
|
||||
from .helpers import _split_blueprint_path
|
||||
|
||||
if t.TYPE_CHECKING:
|
||||
import typing_extensions as te
|
||||
from werkzeug.routing import Rule
|
||||
|
||||
|
||||
class Request(RequestBase):
|
||||
"""The request object used by default in Flask. Remembers the
|
||||
matched endpoint and view arguments.
|
||||
|
||||
It is what ends up as :class:`~flask.request`. If you want to replace
|
||||
the request object used you can subclass this and set
|
||||
:attr:`~flask.Flask.request_class` to your subclass.
|
||||
|
||||
The request object is a :class:`~werkzeug.wrappers.Request` subclass and
|
||||
provides all of the attributes Werkzeug defines plus a few Flask
|
||||
specific ones.
|
||||
"""
|
||||
|
||||
json_module = json
|
||||
|
||||
#: The internal URL rule that matched the request. This can be
|
||||
#: useful to inspect which methods are allowed for the URL from
|
||||
#: a before/after handler (``request.url_rule.methods``) etc.
|
||||
#: Though if the request's method was invalid for the URL rule,
|
||||
#: the valid list is available in ``routing_exception.valid_methods``
|
||||
#: instead (an attribute of the Werkzeug exception
|
||||
#: :exc:`~werkzeug.exceptions.MethodNotAllowed`)
|
||||
#: because the request was never internally bound.
|
||||
#:
|
||||
#: .. versionadded:: 0.6
|
||||
url_rule: t.Optional["Rule"] = None
|
||||
|
||||
#: A dict of view arguments that matched the request. If an exception
|
||||
#: happened when matching, this will be ``None``.
|
||||
view_args: t.Optional[t.Dict[str, t.Any]] = None
|
||||
|
||||
#: If matching the URL failed, this is the exception that will be
|
||||
#: raised / was raised as part of the request handling. This is
|
||||
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
|
||||
#: something similar.
|
||||
routing_exception: t.Optional[Exception] = None
|
||||
|
||||
@property
|
||||
def max_content_length(self) -> t.Optional[int]: # type: ignore
|
||||
"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
|
||||
if current_app:
|
||||
return current_app.config["MAX_CONTENT_LENGTH"]
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def endpoint(self) -> t.Optional[str]:
|
||||
"""The endpoint that matched the request URL.
|
||||
|
||||
This will be ``None`` if matching failed or has not been
|
||||
performed yet.
|
||||
|
||||
This in combination with :attr:`view_args` can be used to
|
||||
reconstruct the same URL or a modified URL.
|
||||
"""
|
||||
if self.url_rule is not None:
|
||||
return self.url_rule.endpoint
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def blueprint(self) -> t.Optional[str]:
|
||||
"""The registered name of the current blueprint.
|
||||
|
||||
This will be ``None`` if the endpoint is not part of a
|
||||
blueprint, or if URL matching failed or has not been performed
|
||||
yet.
|
||||
|
||||
This does not necessarily match the name the blueprint was
|
||||
created with. It may have been nested, or registered with a
|
||||
different name.
|
||||
"""
|
||||
endpoint = self.endpoint
|
||||
|
||||
if endpoint is not None and "." in endpoint:
|
||||
return endpoint.rpartition(".")[0]
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def blueprints(self) -> t.List[str]:
|
||||
"""The registered names of the current blueprint upwards through
|
||||
parent blueprints.
|
||||
|
||||
This will be an empty list if there is no current blueprint, or
|
||||
if URL matching failed.
|
||||
|
||||
.. versionadded:: 2.0.1
|
||||
"""
|
||||
name = self.blueprint
|
||||
|
||||
if name is None:
|
||||
return []
|
||||
|
||||
return _split_blueprint_path(name)
|
||||
|
||||
def _load_form_data(self) -> None:
|
||||
RequestBase._load_form_data(self)
|
||||
|
||||
# In debug mode we're replacing the files multidict with an ad-hoc
|
||||
# subclass that raises a different error for key errors.
|
||||
if (
|
||||
current_app
|
||||
and current_app.debug
|
||||
and self.mimetype != "multipart/form-data"
|
||||
and not self.files
|
||||
):
|
||||
from .debughelpers import attach_enctype_error_multidict
|
||||
|
||||
attach_enctype_error_multidict(self)
|
||||
|
||||
def on_json_loading_failed(self, e: Exception) -> "te.NoReturn":
|
||||
if current_app and current_app.debug:
|
||||
raise BadRequest(f"Failed to decode JSON object: {e}")
|
||||
|
||||
raise BadRequest()
|
||||
|
||||
|
||||
class Response(ResponseBase):
|
||||
"""The response object that is used by default in Flask. Works like the
|
||||
response object from Werkzeug but is set to have an HTML mimetype by
|
||||
default. Quite often you don't have to create this object yourself because
|
||||
:meth:`~flask.Flask.make_response` will take care of that for you.
|
||||
|
||||
If you want to replace the response object used you can subclass this and
|
||||
set :attr:`~flask.Flask.response_class` to your subclass.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
JSON support is added to the response, like the request. This is useful
|
||||
when testing to get the test client response data as JSON.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
|
||||
Added :attr:`max_cookie_size`.
|
||||
"""
|
||||
|
||||
default_mimetype = "text/html"
|
||||
|
||||
json_module = json
|
||||
|
||||
@property
|
||||
def max_cookie_size(self) -> int: # type: ignore
|
||||
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
|
||||
|
||||
See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
|
||||
Werkzeug's docs.
|
||||
"""
|
||||
if current_app:
|
||||
return current_app.config["MAX_COOKIE_SIZE"]
|
||||
|
||||
# return Werkzeug's default when not in an app context
|
||||
return super().max_cookie_size
|
||||
Loading…
Add table
Add a link
Reference in a new issue