aboutsummaryrefslogtreecommitdiff
path: root/venv/lib/python3.8/site-packages/flask/sansio
diff options
context:
space:
mode:
authorsotech117 <michael_foiani@brown.edu>2025-07-31 17:27:24 -0400
committersotech117 <michael_foiani@brown.edu>2025-07-31 17:27:24 -0400
commit5bf22fc7e3c392c8bd44315ca2d06d7dca7d084e (patch)
tree8dacb0f195df1c0788d36dd0064f6bbaa3143ede /venv/lib/python3.8/site-packages/flask/sansio
parentb832d364da8c2efe09e3f75828caf73c50d01ce3 (diff)
add code for analysis of data
Diffstat (limited to 'venv/lib/python3.8/site-packages/flask/sansio')
-rw-r--r--venv/lib/python3.8/site-packages/flask/sansio/README.md6
-rw-r--r--venv/lib/python3.8/site-packages/flask/sansio/app.py964
-rw-r--r--venv/lib/python3.8/site-packages/flask/sansio/blueprints.py632
-rw-r--r--venv/lib/python3.8/site-packages/flask/sansio/scaffold.py801
4 files changed, 2403 insertions, 0 deletions
diff --git a/venv/lib/python3.8/site-packages/flask/sansio/README.md b/venv/lib/python3.8/site-packages/flask/sansio/README.md
new file mode 100644
index 0000000..623ac19
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/flask/sansio/README.md
@@ -0,0 +1,6 @@
+# Sansio
+
+This folder contains code that can be used by alternative Flask
+implementations, for example Quart. The code therefore cannot do any
+IO, nor be part of a likely IO path. Finally this code cannot use the
+Flask globals.
diff --git a/venv/lib/python3.8/site-packages/flask/sansio/app.py b/venv/lib/python3.8/site-packages/flask/sansio/app.py
new file mode 100644
index 0000000..01fd5db
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/flask/sansio/app.py
@@ -0,0 +1,964 @@
+from __future__ import annotations
+
+import logging
+import os
+import sys
+import typing as t
+from datetime import timedelta
+from itertools import chain
+
+from werkzeug.exceptions import Aborter
+from werkzeug.exceptions import BadRequest
+from werkzeug.exceptions import BadRequestKeyError
+from werkzeug.routing import BuildError
+from werkzeug.routing import Map
+from werkzeug.routing import Rule
+from werkzeug.sansio.response import Response
+from werkzeug.utils import cached_property
+from werkzeug.utils import redirect as _wz_redirect
+
+from .. import typing as ft
+from ..config import Config
+from ..config import ConfigAttribute
+from ..ctx import _AppCtxGlobals
+from ..helpers import _split_blueprint_path
+from ..helpers import get_debug_flag
+from ..json.provider import DefaultJSONProvider
+from ..json.provider import JSONProvider
+from ..logging import create_logger
+from ..templating import DispatchingJinjaLoader
+from ..templating import Environment
+from .scaffold import _endpoint_from_view_func
+from .scaffold import find_package
+from .scaffold import Scaffold
+from .scaffold import setupmethod
+
+if t.TYPE_CHECKING: # pragma: no cover
+ from werkzeug.wrappers import Response as BaseResponse
+
+ from ..testing import FlaskClient
+ from ..testing import FlaskCliRunner
+ from .blueprints import Blueprint
+
+T_shell_context_processor = t.TypeVar(
+ "T_shell_context_processor", bound=ft.ShellContextProcessorCallable
+)
+T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
+T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
+T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
+T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
+
+
+def _make_timedelta(value: timedelta | int | None) -> timedelta | None:
+ if value is None or isinstance(value, timedelta):
+ return value
+
+ return timedelta(seconds=value)
+
+
+class App(Scaffold):
+ """The flask object implements a WSGI application and acts as the central
+ object. It is passed the name of the module or package of the
+ application. Once it is created it will act as a central registry for
+ the view functions, the URL rules, template configuration and much more.
+
+ The name of the package is used to resolve resources from inside the
+ package or the folder the module is contained in depending on if the
+ package parameter resolves to an actual python package (a folder with
+ an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).
+
+ For more information about resource loading, see :func:`open_resource`.
+
+ Usually you create a :class:`Flask` instance in your main module or
+ in the :file:`__init__.py` file of your package like this::
+
+ from flask import Flask
+ app = Flask(__name__)
+
+ .. admonition:: About the First Parameter
+
+ The idea of the first parameter is to give Flask an idea of what
+ belongs to your application. This name is used to find resources
+ on the filesystem, can be used by extensions to improve debugging
+ information and a lot more.
+
+ So it's important what you provide there. If you are using a single
+ module, `__name__` is always the correct value. If you however are
+ using a package, it's usually recommended to hardcode the name of
+ your package there.
+
+ For example if your application is defined in :file:`yourapplication/app.py`
+ you should create it with one of the two versions below::
+
+ app = Flask('yourapplication')
+ app = Flask(__name__.split('.')[0])
+
+ Why is that? The application will work even with `__name__`, thanks
+ to how resources are looked up. However it will make debugging more
+ painful. Certain extensions can make assumptions based on the
+ import name of your application. For example the Flask-SQLAlchemy
+ extension will look for the code in your application that triggered
+ an SQL query in debug mode. If the import name is not properly set
+ up, that debugging information is lost. (For example it would only
+ pick up SQL queries in `yourapplication.app` and not
+ `yourapplication.views.frontend`)
+
+ .. versionadded:: 0.7
+ The `static_url_path`, `static_folder`, and `template_folder`
+ parameters were added.
+
+ .. versionadded:: 0.8
+ The `instance_path` and `instance_relative_config` parameters were
+ added.
+
+ .. versionadded:: 0.11
+ The `root_path` parameter was added.
+
+ .. versionadded:: 1.0
+ The ``host_matching`` and ``static_host`` parameters were added.
+
+ .. versionadded:: 1.0
+ The ``subdomain_matching`` parameter was added. Subdomain
+ matching needs to be enabled manually now. Setting
+ :data:`SERVER_NAME` does not implicitly enable it.
+
+ :param import_name: the name of the application package
+ :param static_url_path: can be used to specify a different path for the
+ static files on the web. Defaults to the name
+ of the `static_folder` folder.
+ :param static_folder: The folder with static files that is served at
+ ``static_url_path``. Relative to the application ``root_path``
+ or an absolute path. Defaults to ``'static'``.
+ :param static_host: the host to use when adding the static route.
+ Defaults to None. Required when using ``host_matching=True``
+ with a ``static_folder`` configured.
+ :param host_matching: set ``url_map.host_matching`` attribute.
+ Defaults to False.
+ :param subdomain_matching: consider the subdomain relative to
+ :data:`SERVER_NAME` when matching routes. Defaults to False.
+ :param template_folder: the folder that contains the templates that should
+ be used by the application. Defaults to
+ ``'templates'`` folder in the root path of the
+ application.
+ :param instance_path: An alternative instance path for the application.
+ By default the folder ``'instance'`` next to the
+ package or module is assumed to be the instance
+ path.
+ :param instance_relative_config: if set to ``True`` relative filenames
+ for loading the config are assumed to
+ be relative to the instance path instead
+ of the application root.
+ :param root_path: The path to the root of the application files.
+ This should only be set manually when it can't be detected
+ automatically, such as for namespace packages.
+ """
+
+ #: The class of the object assigned to :attr:`aborter`, created by
+ #: :meth:`create_aborter`. That object is called by
+ #: :func:`flask.abort` to raise HTTP errors, and can be
+ #: called directly as well.
+ #:
+ #: Defaults to :class:`werkzeug.exceptions.Aborter`.
+ #:
+ #: .. versionadded:: 2.2
+ aborter_class = Aborter
+
+ #: The class that is used for the Jinja environment.
+ #:
+ #: .. versionadded:: 0.11
+ jinja_environment = Environment
+
+ #: The class that is used for the :data:`~flask.g` instance.
+ #:
+ #: Example use cases for a custom class:
+ #:
+ #: 1. Store arbitrary attributes on flask.g.
+ #: 2. Add a property for lazy per-request database connectors.
+ #: 3. Return None instead of AttributeError on unexpected attributes.
+ #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
+ #:
+ #: In Flask 0.9 this property was called `request_globals_class` but it
+ #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the
+ #: flask.g object is now application context scoped.
+ #:
+ #: .. versionadded:: 0.10
+ app_ctx_globals_class = _AppCtxGlobals
+
+ #: The class that is used for the ``config`` attribute of this app.
+ #: Defaults to :class:`~flask.Config`.
+ #:
+ #: Example use cases for a custom class:
+ #:
+ #: 1. Default values for certain config options.
+ #: 2. Access to config values through attributes in addition to keys.
+ #:
+ #: .. versionadded:: 0.11
+ config_class = Config
+
+ #: The testing flag. Set this to ``True`` to enable the test mode of
+ #: Flask extensions (and in the future probably also Flask itself).
+ #: For example this might activate test helpers that have an
+ #: additional runtime cost which should not be enabled by default.
+ #:
+ #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the
+ #: default it's implicitly enabled.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``TESTING`` configuration key. Defaults to ``False``.
+ testing = ConfigAttribute[bool]("TESTING")
+
+ #: If a secret key is set, cryptographic components can use this to
+ #: sign cookies and other things. Set this to a complex random value
+ #: when you want to use the secure cookie for instance.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: :data:`SECRET_KEY` configuration key. Defaults to ``None``.
+ secret_key = ConfigAttribute[t.Union[str, bytes, None]]("SECRET_KEY")
+
+ #: A :class:`~datetime.timedelta` which is used to set the expiration
+ #: date of a permanent session. The default is 31 days which makes a
+ #: permanent session survive for roughly one month.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to
+ #: ``timedelta(days=31)``
+ permanent_session_lifetime = ConfigAttribute[timedelta](
+ "PERMANENT_SESSION_LIFETIME",
+ get_converter=_make_timedelta, # type: ignore[arg-type]
+ )
+
+ json_provider_class: type[JSONProvider] = DefaultJSONProvider
+ """A subclass of :class:`~flask.json.provider.JSONProvider`. An
+ instance is created and assigned to :attr:`app.json` when creating
+ the app.
+
+ The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses
+ Python's built-in :mod:`json` library. A different provider can use
+ a different JSON library.
+
+ .. versionadded:: 2.2
+ """
+
+ #: Options that are passed to the Jinja environment in
+ #: :meth:`create_jinja_environment`. Changing these options after
+ #: the environment is created (accessing :attr:`jinja_env`) will
+ #: have no effect.
+ #:
+ #: .. versionchanged:: 1.1.0
+ #: This is a ``dict`` instead of an ``ImmutableDict`` to allow
+ #: easier configuration.
+ #:
+ jinja_options: dict[str, t.Any] = {}
+
+ #: The rule object to use for URL rules created. This is used by
+ #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`.
+ #:
+ #: .. versionadded:: 0.7
+ url_rule_class = Rule
+
+ #: The map object to use for storing the URL rules and routing
+ #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`.
+ #:
+ #: .. versionadded:: 1.1.0
+ url_map_class = Map
+
+ #: The :meth:`test_client` method creates an instance of this test
+ #: client class. Defaults to :class:`~flask.testing.FlaskClient`.
+ #:
+ #: .. versionadded:: 0.7
+ test_client_class: type[FlaskClient] | None = None
+
+ #: The :class:`~click.testing.CliRunner` subclass, by default
+ #: :class:`~flask.testing.FlaskCliRunner` that is used by
+ #: :meth:`test_cli_runner`. Its ``__init__`` method should take a
+ #: Flask app object as the first argument.
+ #:
+ #: .. versionadded:: 1.0
+ test_cli_runner_class: type[FlaskCliRunner] | None = None
+
+ default_config: dict[str, t.Any]
+ response_class: type[Response]
+
+ def __init__(
+ self,
+ import_name: str,
+ static_url_path: str | None = None,
+ static_folder: str | os.PathLike[str] | None = "static",
+ static_host: str | None = None,
+ host_matching: bool = False,
+ subdomain_matching: bool = False,
+ template_folder: str | os.PathLike[str] | None = "templates",
+ instance_path: str | None = None,
+ instance_relative_config: bool = False,
+ root_path: str | None = None,
+ ):
+ 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 instance_path is None:
+ instance_path = self.auto_find_instance_path()
+ elif not os.path.isabs(instance_path):
+ raise ValueError(
+ "If an instance path is provided it must be absolute."
+ " A relative path was given instead."
+ )
+
+ #: Holds the path to the instance folder.
+ #:
+ #: .. versionadded:: 0.8
+ self.instance_path = instance_path
+
+ #: The configuration dictionary as :class:`Config`. This behaves
+ #: exactly like a regular dictionary but supports additional methods
+ #: to load a config from files.
+ self.config = self.make_config(instance_relative_config)
+
+ #: An instance of :attr:`aborter_class` created by
+ #: :meth:`make_aborter`. This is called by :func:`flask.abort`
+ #: to raise HTTP errors, and can be called directly as well.
+ #:
+ #: .. versionadded:: 2.2
+ #: Moved from ``flask.abort``, which calls this object.
+ self.aborter = self.make_aborter()
+
+ self.json: JSONProvider = self.json_provider_class(self)
+ """Provides access to JSON methods. Functions in ``flask.json``
+ will call methods on this provider when the application context
+ is active. Used for handling JSON requests and responses.
+
+ An instance of :attr:`json_provider_class`. Can be customized by
+ changing that attribute on a subclass, or by assigning to this
+ attribute afterwards.
+
+ The default, :class:`~flask.json.provider.DefaultJSONProvider`,
+ uses Python's built-in :mod:`json` library. A different provider
+ can use a different JSON library.
+
+ .. versionadded:: 2.2
+ """
+
+ #: A list of functions that are called by
+ #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a
+ #: :exc:`~werkzeug.routing.BuildError`. Each function is called
+ #: with ``error``, ``endpoint`` and ``values``. If a function
+ #: returns ``None`` or raises a ``BuildError``, it is skipped.
+ #: Otherwise, its return value is returned by ``url_for``.
+ #:
+ #: .. versionadded:: 0.9
+ self.url_build_error_handlers: list[
+ t.Callable[[Exception, str, dict[str, t.Any]], str]
+ ] = []
+
+ #: A list of functions that are called when the application context
+ #: is destroyed. Since the application context is also torn down
+ #: if the request ends this is the place to store code that disconnects
+ #: from databases.
+ #:
+ #: .. versionadded:: 0.9
+ self.teardown_appcontext_funcs: list[ft.TeardownCallable] = []
+
+ #: A list of shell context processor functions that should be run
+ #: when a shell context is created.
+ #:
+ #: .. versionadded:: 0.11
+ self.shell_context_processors: list[ft.ShellContextProcessorCallable] = []
+
+ #: Maps registered blueprint names to blueprint objects. The
+ #: dict retains the order the blueprints were registered in.
+ #: Blueprints can be registered multiple times, this dict does
+ #: not track how often they were attached.
+ #:
+ #: .. versionadded:: 0.7
+ self.blueprints: dict[str, Blueprint] = {}
+
+ #: a place where extensions can store application specific state. For
+ #: example this is where an extension could store database engines and
+ #: similar things.
+ #:
+ #: The key must match the name of the extension module. For example in
+ #: case of a "Flask-Foo" extension in `flask_foo`, the key would be
+ #: ``'foo'``.
+ #:
+ #: .. versionadded:: 0.7
+ self.extensions: dict[str, t.Any] = {}
+
+ #: The :class:`~werkzeug.routing.Map` for this instance. You can use
+ #: this to change the routing converters after the class was created
+ #: but before any routes are connected. Example::
+ #:
+ #: from werkzeug.routing import BaseConverter
+ #:
+ #: class ListConverter(BaseConverter):
+ #: def to_python(self, value):
+ #: return value.split(',')
+ #: def to_url(self, values):
+ #: return ','.join(super(ListConverter, self).to_url(value)
+ #: for value in values)
+ #:
+ #: app = Flask(__name__)
+ #: app.url_map.converters['list'] = ListConverter
+ self.url_map = self.url_map_class(host_matching=host_matching)
+
+ self.subdomain_matching = subdomain_matching
+
+ # tracks internally if the application already handled at least one
+ # request.
+ self._got_first_request = False
+
+ def _check_setup_finished(self, f_name: str) -> None:
+ if self._got_first_request:
+ raise AssertionError(
+ f"The setup method '{f_name}' can no longer be called"
+ " on the application. It has already handled its first"
+ " request, any changes will not be applied"
+ " consistently.\n"
+ "Make sure all imports, decorators, functions, etc."
+ " needed to set up the application are done before"
+ " running it."
+ )
+
+ @cached_property
+ def name(self) -> str: # type: ignore
+ """The name of the application. This is usually the import name
+ with the difference that it's guessed from the run file if the
+ import name is main. This name is used as a display name when
+ Flask needs the name of the application. It can be set and overridden
+ to change the value.
+
+ .. versionadded:: 0.8
+ """
+ if self.import_name == "__main__":
+ fn: str | None = getattr(sys.modules["__main__"], "__file__", None)
+ if fn is None:
+ return "__main__"
+ return os.path.splitext(os.path.basename(fn))[0]
+ return self.import_name
+
+ @cached_property
+ def logger(self) -> logging.Logger:
+ """A standard Python :class:`~logging.Logger` for the app, with
+ the same name as :attr:`name`.
+
+ In debug mode, the logger's :attr:`~logging.Logger.level` will
+ be set to :data:`~logging.DEBUG`.
+
+ If there are no handlers configured, a default handler will be
+ added. See :doc:`/logging` for more information.
+
+ .. versionchanged:: 1.1.0
+ The logger takes the same name as :attr:`name` rather than
+ hard-coding ``"flask.app"``.
+
+ .. versionchanged:: 1.0.0
+ Behavior was simplified. The logger is always named
+ ``"flask.app"``. The level is only set during configuration,
+ it doesn't check ``app.debug`` each time. Only one format is
+ used, not different ones depending on ``app.debug``. No
+ handlers are removed, and a handler is only added if no
+ handlers are already configured.
+
+ .. versionadded:: 0.3
+ """
+ return create_logger(self)
+
+ @cached_property
+ def jinja_env(self) -> Environment:
+ """The Jinja environment used to load templates.
+
+ The environment is created the first time this property is
+ accessed. Changing :attr:`jinja_options` after that will have no
+ effect.
+ """
+ return self.create_jinja_environment()
+
+ def create_jinja_environment(self) -> Environment:
+ raise NotImplementedError()
+
+ def make_config(self, instance_relative: bool = False) -> Config:
+ """Used to create the config attribute by the Flask constructor.
+ The `instance_relative` parameter is passed in from the constructor
+ of Flask (there named `instance_relative_config`) and indicates if
+ the config should be relative to the instance path or the root path
+ of the application.
+
+ .. versionadded:: 0.8
+ """
+ root_path = self.root_path
+ if instance_relative:
+ root_path = self.instance_path
+ defaults = dict(self.default_config)
+ defaults["DEBUG"] = get_debug_flag()
+ return self.config_class(root_path, defaults)
+
+ def make_aborter(self) -> Aborter:
+ """Create the object to assign to :attr:`aborter`. That object
+ is called by :func:`flask.abort` to raise HTTP errors, and can
+ be called directly as well.
+
+ By default, this creates an instance of :attr:`aborter_class`,
+ which defaults to :class:`werkzeug.exceptions.Aborter`.
+
+ .. versionadded:: 2.2
+ """
+ return self.aborter_class()
+
+ def auto_find_instance_path(self) -> str:
+ """Tries to locate the instance path if it was not provided to the
+ constructor of the application class. It will basically calculate
+ the path to a folder named ``instance`` next to your main file or
+ the package.
+
+ .. versionadded:: 0.8
+ """
+ prefix, package_path = find_package(self.import_name)
+ if prefix is None:
+ return os.path.join(package_path, "instance")
+ return os.path.join(prefix, "var", f"{self.name}-instance")
+
+ def create_global_jinja_loader(self) -> DispatchingJinjaLoader:
+ """Creates the loader for the Jinja2 environment. Can be used to
+ override just the loader and keeping the rest unchanged. It's
+ discouraged to override this function. Instead one should override
+ the :meth:`jinja_loader` function instead.
+
+ The global loader dispatches between the loaders of the application
+ and the individual blueprints.
+
+ .. versionadded:: 0.7
+ """
+ return DispatchingJinjaLoader(self)
+
+ def select_jinja_autoescape(self, filename: str) -> bool:
+ """Returns ``True`` if autoescaping should be active for the given
+ template name. If no template name is given, returns `True`.
+
+ .. versionchanged:: 2.2
+ Autoescaping is now enabled by default for ``.svg`` files.
+
+ .. versionadded:: 0.5
+ """
+ if filename is None:
+ return True
+ return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg"))
+
+ @property
+ def debug(self) -> bool:
+ """Whether debug mode is enabled. When using ``flask run`` to start the
+ development server, an interactive debugger will be shown for unhandled
+ exceptions, and the server will be reloaded when code changes. This maps to the
+ :data:`DEBUG` config key. It may not behave as expected if set late.
+
+ **Do not enable debug mode when deploying in production.**
+
+ Default: ``False``
+ """
+ return self.config["DEBUG"] # type: ignore[no-any-return]
+
+ @debug.setter
+ def debug(self, value: bool) -> None:
+ self.config["DEBUG"] = value
+
+ if self.config["TEMPLATES_AUTO_RELOAD"] is None:
+ self.jinja_env.auto_reload = value
+
+ @setupmethod
+ def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:
+ """Register a :class:`~flask.Blueprint` on the application. Keyword
+ arguments passed to this method will override the defaults set on the
+ blueprint.
+
+ Calls the blueprint's :meth:`~flask.Blueprint.register` method after
+ recording the blueprint in the application's :attr:`blueprints`.
+
+ :param blueprint: The blueprint to register.
+ :param url_prefix: Blueprint routes will be prefixed with this.
+ :param subdomain: Blueprint routes will match on this subdomain.
+ :param url_defaults: Blueprint routes will use these default values for
+ view arguments.
+ :param options: Additional keyword arguments are passed to
+ :class:`~flask.blueprints.BlueprintSetupState`. They can be
+ accessed in :meth:`~flask.Blueprint.record` callbacks.
+
+ .. 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:: 0.7
+ """
+ blueprint.register(self, options)
+
+ def iter_blueprints(self) -> t.ValuesView[Blueprint]:
+ """Iterates over all blueprints by the order they were registered.
+
+ .. versionadded:: 0.11
+ """
+ return self.blueprints.values()
+
+ @setupmethod
+ def add_url_rule(
+ self,
+ rule: str,
+ endpoint: str | None = None,
+ view_func: ft.RouteCallable | None = None,
+ provide_automatic_options: bool | None = None,
+ **options: t.Any,
+ ) -> None:
+ if endpoint is None:
+ endpoint = _endpoint_from_view_func(view_func) # type: ignore
+ options["endpoint"] = endpoint
+ methods = options.pop("methods", None)
+
+ # if the methods are not given and the view_func object knows its
+ # methods we can use that instead. If neither exists, we go with
+ # a tuple of only ``GET`` as default.
+ if methods is None:
+ methods = getattr(view_func, "methods", None) or ("GET",)
+ if isinstance(methods, str):
+ raise TypeError(
+ "Allowed methods must be a list of strings, for"
+ ' example: @app.route(..., methods=["POST"])'
+ )
+ methods = {item.upper() for item in methods}
+
+ # Methods that should always be added
+ required_methods = set(getattr(view_func, "required_methods", ()))
+
+ # starting with Flask 0.8 the view_func object can disable and
+ # force-enable the automatic options handling.
+ if provide_automatic_options is None:
+ provide_automatic_options = getattr(
+ view_func, "provide_automatic_options", None
+ )
+
+ if provide_automatic_options is None:
+ if "OPTIONS" not in methods:
+ provide_automatic_options = True
+ required_methods.add("OPTIONS")
+ else:
+ provide_automatic_options = False
+
+ # Add the required methods now.
+ methods |= required_methods
+
+ rule_obj = self.url_rule_class(rule, methods=methods, **options)
+ rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined]
+
+ self.url_map.add(rule_obj)
+ if view_func is not None:
+ old_func = self.view_functions.get(endpoint)
+ if old_func is not None and old_func != view_func:
+ raise AssertionError(
+ "View function mapping is overwriting an existing"
+ f" endpoint function: {endpoint}"
+ )
+ self.view_functions[endpoint] = view_func
+
+ @setupmethod
+ def template_filter(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_filter], T_template_filter]:
+ """A decorator that is used to register custom template filter.
+ You can specify a name for the filter, otherwise the function
+ name will be used. Example::
+
+ @app.template_filter()
+ def reverse(s):
+ return s[::-1]
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_filter) -> T_template_filter:
+ self.add_template_filter(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_template_filter(
+ self, f: ft.TemplateFilterCallable, name: str | None = None
+ ) -> None:
+ """Register a custom template filter. Works exactly like the
+ :meth:`template_filter` decorator.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.filters[name or f.__name__] = f
+
+ @setupmethod
+ def template_test(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_test], T_template_test]:
+ """A decorator that is used to register custom template test.
+ You can specify a name for the test, otherwise the function
+ name will be used. Example::
+
+ @app.template_test()
+ def is_prime(n):
+ if n == 2:
+ return True
+ for i in range(2, int(math.ceil(math.sqrt(n))) + 1):
+ if n % i == 0:
+ return False
+ return True
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_test) -> T_template_test:
+ self.add_template_test(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_template_test(
+ self, f: ft.TemplateTestCallable, name: str | None = None
+ ) -> None:
+ """Register a custom template test. Works exactly like the
+ :meth:`template_test` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.tests[name or f.__name__] = f
+
+ @setupmethod
+ def template_global(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_global], T_template_global]:
+ """A decorator that is used to register a custom template global function.
+ You can specify a name for the global function, otherwise the function
+ name will be used. Example::
+
+ @app.template_global()
+ def double(n):
+ return 2 * n
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global function, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_global) -> T_template_global:
+ self.add_template_global(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_template_global(
+ self, f: ft.TemplateGlobalCallable, name: str | None = None
+ ) -> None:
+ """Register a custom template global function. Works exactly like the
+ :meth:`template_global` decorator.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global function, otherwise the
+ function name will be used.
+ """
+ self.jinja_env.globals[name or f.__name__] = f
+
+ @setupmethod
+ def teardown_appcontext(self, f: T_teardown) -> T_teardown:
+ """Registers a function to be called when the application
+ context is popped. The application context is typically popped
+ after the request context for each request, at the end of CLI
+ commands, or after a manually pushed context ends.
+
+ .. code-block:: python
+
+ with app.app_context():
+ ...
+
+ When the ``with`` block exits (or ``ctx.pop()`` is called), the
+ teardown functions are called just before the app context is
+ made inactive. Since a request context typically also manages an
+ application context it would also be called when you pop a
+ request context.
+
+ When a teardown function was called because of an unhandled
+ exception it will be passed an error object. If an
+ :meth:`errorhandler` is registered, it will handle the exception
+ and the teardown will not receive it.
+
+ Teardown functions must avoid raising exceptions. If they
+ execute code that might fail they must surround that code with a
+ ``try``/``except`` block and log any errors.
+
+ The return values of teardown functions are ignored.
+
+ .. versionadded:: 0.9
+ """
+ self.teardown_appcontext_funcs.append(f)
+ return f
+
+ @setupmethod
+ def shell_context_processor(
+ self, f: T_shell_context_processor
+ ) -> T_shell_context_processor:
+ """Registers a shell context processor function.
+
+ .. versionadded:: 0.11
+ """
+ self.shell_context_processors.append(f)
+ return f
+
+ def _find_error_handler(
+ self, e: Exception, blueprints: list[str]
+ ) -> ft.ErrorHandlerCallable | None:
+ """Return a registered error handler for an exception in this order:
+ blueprint handler for a specific code, app handler for a specific code,
+ blueprint handler for an exception class, app handler for an exception
+ class, or ``None`` if a suitable handler is not found.
+ """
+ exc_class, code = self._get_exc_class_and_code(type(e))
+ names = (*blueprints, None)
+
+ for c in (code, None) if code is not None else (None,):
+ for name in names:
+ handler_map = self.error_handler_spec[name][c]
+
+ if not handler_map:
+ continue
+
+ for cls in exc_class.__mro__:
+ handler = handler_map.get(cls)
+
+ if handler is not None:
+ return handler
+ return None
+
+ def trap_http_exception(self, e: Exception) -> bool:
+ """Checks if an HTTP exception should be trapped or not. By default
+ this will return ``False`` for all exceptions except for a bad request
+ key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It
+ also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``.
+
+ This is called for all HTTP exceptions raised by a view function.
+ If it returns ``True`` for any exception the error handler for this
+ exception is not called and it shows up as regular exception in the
+ traceback. This is helpful for debugging implicitly raised HTTP
+ exceptions.
+
+ .. versionchanged:: 1.0
+ Bad request errors are not trapped by default in debug mode.
+
+ .. versionadded:: 0.8
+ """
+ if self.config["TRAP_HTTP_EXCEPTIONS"]:
+ return True
+
+ trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"]
+
+ # if unset, trap key errors in debug mode
+ if (
+ trap_bad_request is None
+ and self.debug
+ and isinstance(e, BadRequestKeyError)
+ ):
+ return True
+
+ if trap_bad_request:
+ return isinstance(e, BadRequest)
+
+ return False
+
+ def should_ignore_error(self, error: BaseException | None) -> bool:
+ """This is called to figure out if an error should be ignored
+ or not as far as the teardown system is concerned. If this
+ function returns ``True`` then the teardown handlers will not be
+ passed the error.
+
+ .. versionadded:: 0.10
+ """
+ return False
+
+ def redirect(self, location: str, code: int = 302) -> BaseResponse:
+ """Create a redirect response object.
+
+ This is called by :func:`flask.redirect`, and can be called
+ directly as well.
+
+ :param location: The URL to redirect to.
+ :param code: The status code for the redirect.
+
+ .. versionadded:: 2.2
+ Moved from ``flask.redirect``, which calls this method.
+ """
+ return _wz_redirect(
+ location,
+ code=code,
+ Response=self.response_class, # type: ignore[arg-type]
+ )
+
+ def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None:
+ """Injects the URL defaults for the given endpoint directly into
+ the values dictionary passed. This is used internally and
+ automatically called on URL building.
+
+ .. versionadded:: 0.7
+ """
+ names: t.Iterable[str | None] = (None,)
+
+ # url_for may be called outside a request context, parse the
+ # passed endpoint instead of using request.blueprints.
+ if "." in endpoint:
+ names = chain(
+ names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0]))
+ )
+
+ for name in names:
+ if name in self.url_default_functions:
+ for func in self.url_default_functions[name]:
+ func(endpoint, values)
+
+ def handle_url_build_error(
+ self, error: BuildError, endpoint: str, values: dict[str, t.Any]
+ ) -> str:
+ """Called by :meth:`.url_for` if a
+ :exc:`~werkzeug.routing.BuildError` was raised. If this returns
+ a value, it will be returned by ``url_for``, otherwise the error
+ will be re-raised.
+
+ Each function in :attr:`url_build_error_handlers` is called with
+ ``error``, ``endpoint`` and ``values``. If a function returns
+ ``None`` or raises a ``BuildError``, it is skipped. Otherwise,
+ its return value is returned by ``url_for``.
+
+ :param error: The active ``BuildError`` being handled.
+ :param endpoint: The endpoint being built.
+ :param values: The keyword arguments passed to ``url_for``.
+ """
+ for handler in self.url_build_error_handlers:
+ try:
+ rv = handler(error, endpoint, values)
+ except BuildError as e:
+ # make error available outside except block
+ error = e
+ else:
+ if rv is not None:
+ return rv
+
+ # Re-raise if called with an active exception, otherwise raise
+ # the passed in exception.
+ if error is sys.exc_info()[1]:
+ raise
+
+ raise error
diff --git a/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py b/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py
new file mode 100644
index 0000000..4f912cc
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/flask/sansio/blueprints.py
@@ -0,0 +1,632 @@
+from __future__ import annotations
+
+import os
+import typing as t
+from collections import defaultdict
+from functools import update_wrapper
+
+from .. import typing as ft
+from .scaffold import _endpoint_from_view_func
+from .scaffold import _sentinel
+from .scaffold import Scaffold
+from .scaffold import setupmethod
+
+if t.TYPE_CHECKING: # pragma: no cover
+ from .app import App
+
+DeferredSetupFunction = t.Callable[["BlueprintSetupState"], None]
+T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any])
+T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
+T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
+T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
+T_template_context_processor = t.TypeVar(
+ "T_template_context_processor", bound=ft.TemplateContextProcessorCallable
+)
+T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
+T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
+T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
+T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable)
+T_url_value_preprocessor = t.TypeVar(
+ "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable
+)
+
+
+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: App,
+ 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: str | None = None,
+ view_func: ft.RouteCallable | None = 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
+ """
+
+ _got_registered_once = False
+
+ def __init__(
+ self,
+ name: str,
+ import_name: str,
+ static_folder: str | os.PathLike[str] | None = None,
+ static_url_path: str | None = None,
+ template_folder: str | os.PathLike[str] | None = None,
+ url_prefix: str | None = None,
+ subdomain: str | None = None,
+ url_defaults: dict[str, t.Any] | None = None,
+ root_path: str | None = None,
+ cli_group: str | None = _sentinel, # type: ignore[assignment]
+ ):
+ 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 not name:
+ raise ValueError("'name' may not be empty.")
+
+ 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: list[DeferredSetupFunction] = []
+
+ if url_defaults is None:
+ url_defaults = {}
+
+ self.url_values_defaults = url_defaults
+ self.cli_group = cli_group
+ self._blueprints: list[tuple[Blueprint, dict[str, t.Any]]] = []
+
+ def _check_setup_finished(self, f_name: str) -> None:
+ if self._got_registered_once:
+ raise AssertionError(
+ f"The setup method '{f_name}' can no longer be called on the blueprint"
+ f" '{self.name}'. It has already been registered at least once, any"
+ " changes will not be applied consistently.\n"
+ "Make sure all imports, decorators, functions, etc. needed to set up"
+ " the blueprint are done before registering it."
+ )
+
+ @setupmethod
+ def record(self, func: DeferredSetupFunction) -> 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.
+ """
+ self.deferred_functions.append(func)
+
+ @setupmethod
+ def record_once(self, func: DeferredSetupFunction) -> 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)
+
+ self.record(update_wrapper(wrapper, func))
+
+ def make_setup_state(
+ self, app: App, options: dict[str, t.Any], 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)
+
+ @setupmethod
+ 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: App, options: dict[str, t.Any]) -> 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.3
+ Nested blueprints now correctly apply subdomains.
+
+ .. versionchanged:: 2.1
+ Registering the same blueprint with the same name multiple
+ times is an error.
+
+ .. 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``.
+ """
+ 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:
+ bp_desc = "this" if app.blueprints[name] is self else "a different"
+ existing_at = f" '{name}'" if self_name != name else ""
+
+ raise ValueError(
+ f"The name '{self_name}' is already registered for"
+ f" {bp_desc} blueprint{existing_at}. Use 'name=' to"
+ f" provide a unique name."
+ )
+
+ 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, # type: ignore[attr-defined]
+ endpoint="static",
+ )
+
+ # Merge blueprint data into parent.
+ if first_bp_registration or first_name_registration:
+ self._merge_blueprint_funcs(app, name)
+
+ 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")
+ bp_subdomain = bp_options.get("subdomain")
+
+ if bp_subdomain is None:
+ bp_subdomain = blueprint.subdomain
+
+ if state.subdomain is not None and bp_subdomain is not None:
+ bp_options["subdomain"] = bp_subdomain + "." + state.subdomain
+ elif bp_subdomain is not None:
+ bp_options["subdomain"] = bp_subdomain
+ elif state.subdomain is not None:
+ bp_options["subdomain"] = state.subdomain
+
+ 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 _merge_blueprint_funcs(self, app: App, name: str) -> None:
+ def extend(
+ bp_dict: dict[ft.AppOrBlueprintKey, list[t.Any]],
+ parent_dict: dict[ft.AppOrBlueprintKey, list[t.Any]],
+ ) -> None:
+ 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)
+
+ @setupmethod
+ def add_url_rule(
+ self,
+ rule: str,
+ endpoint: str | None = None,
+ view_func: ft.RouteCallable | None = None,
+ provide_automatic_options: bool | None = None,
+ **options: t.Any,
+ ) -> None:
+ """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for
+ full documentation.
+
+ The URL rule is prefixed with the blueprint's URL prefix. The endpoint name,
+ used with :func:`url_for`, is prefixed with the blueprint's name.
+ """
+ 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,
+ )
+ )
+
+ @setupmethod
+ def app_template_filter(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_filter], T_template_filter]:
+ """Register a template filter, available in any template rendered by the
+ application. Equivalent to :meth:`.Flask.template_filter`.
+
+ :param name: the optional name of the filter, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_filter) -> T_template_filter:
+ self.add_app_template_filter(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_app_template_filter(
+ self, f: ft.TemplateFilterCallable, name: str | None = None
+ ) -> None:
+ """Register a template filter, available in any template rendered by the
+ application. Works like the :meth:`app_template_filter` decorator. Equivalent to
+ :meth:`.Flask.add_template_filter`.
+
+ :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)
+
+ @setupmethod
+ def app_template_test(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_test], T_template_test]:
+ """Register a template test, available in any template rendered by the
+ application. Equivalent to :meth:`.Flask.template_test`.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the test, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_test) -> T_template_test:
+ self.add_app_template_test(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_app_template_test(
+ self, f: ft.TemplateTestCallable, name: str | None = None
+ ) -> None:
+ """Register a template test, available in any template rendered by the
+ application. Works like the :meth:`app_template_test` decorator. Equivalent to
+ :meth:`.Flask.add_template_test`.
+
+ .. 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)
+
+ @setupmethod
+ def app_template_global(
+ self, name: str | None = None
+ ) -> t.Callable[[T_template_global], T_template_global]:
+ """Register a template global, available in any template rendered by the
+ application. Equivalent to :meth:`.Flask.template_global`.
+
+ .. versionadded:: 0.10
+
+ :param name: the optional name of the global, otherwise the
+ function name will be used.
+ """
+
+ def decorator(f: T_template_global) -> T_template_global:
+ self.add_app_template_global(f, name=name)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def add_app_template_global(
+ self, f: ft.TemplateGlobalCallable, name: str | None = None
+ ) -> None:
+ """Register a template global, available in any template rendered by the
+ application. Works like the :meth:`app_template_global` decorator. Equivalent to
+ :meth:`.Flask.add_template_global`.
+
+ .. 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)
+
+ @setupmethod
+ def before_app_request(self, f: T_before_request) -> T_before_request:
+ """Like :meth:`before_request`, but before every request, not only those handled
+ by the blueprint. Equivalent to :meth:`.Flask.before_request`.
+ """
+ self.record_once(
+ lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
+ )
+ return f
+
+ @setupmethod
+ def after_app_request(self, f: T_after_request) -> T_after_request:
+ """Like :meth:`after_request`, but after every request, not only those handled
+ by the blueprint. Equivalent to :meth:`.Flask.after_request`.
+ """
+ self.record_once(
+ lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
+ )
+ return f
+
+ @setupmethod
+ def teardown_app_request(self, f: T_teardown) -> T_teardown:
+ """Like :meth:`teardown_request`, but after every request, not only those
+ handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`.
+ """
+ self.record_once(
+ lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
+ )
+ return f
+
+ @setupmethod
+ def app_context_processor(
+ self, f: T_template_context_processor
+ ) -> T_template_context_processor:
+ """Like :meth:`context_processor`, but for templates rendered by every view, not
+ only by the blueprint. Equivalent to :meth:`.Flask.context_processor`.
+ """
+ self.record_once(
+ lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
+ )
+ return f
+
+ @setupmethod
+ def app_errorhandler(
+ self, code: type[Exception] | int
+ ) -> t.Callable[[T_error_handler], T_error_handler]:
+ """Like :meth:`errorhandler`, but for every request, not only those handled by
+ the blueprint. Equivalent to :meth:`.Flask.errorhandler`.
+ """
+
+ def decorator(f: T_error_handler) -> T_error_handler:
+ def from_blueprint(state: BlueprintSetupState) -> None:
+ state.app.errorhandler(code)(f)
+
+ self.record_once(from_blueprint)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def app_url_value_preprocessor(
+ self, f: T_url_value_preprocessor
+ ) -> T_url_value_preprocessor:
+ """Like :meth:`url_value_preprocessor`, but for every request, not only those
+ handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`.
+ """
+ self.record_once(
+ lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
+ )
+ return f
+
+ @setupmethod
+ def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults:
+ """Like :meth:`url_defaults`, but for every request, not only those handled by
+ the blueprint. Equivalent to :meth:`.Flask.url_defaults`.
+ """
+ self.record_once(
+ lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
+ )
+ return f
diff --git a/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py b/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py
new file mode 100644
index 0000000..69e33a0
--- /dev/null
+++ b/venv/lib/python3.8/site-packages/flask/sansio/scaffold.py
@@ -0,0 +1,801 @@
+from __future__ import annotations
+
+import importlib.util
+import os
+import pathlib
+import sys
+import typing as t
+from collections import defaultdict
+from functools import update_wrapper
+
+from jinja2 import BaseLoader
+from jinja2 import FileSystemLoader
+from werkzeug.exceptions import default_exceptions
+from werkzeug.exceptions import HTTPException
+from werkzeug.utils import cached_property
+
+from .. import typing as ft
+from ..helpers import get_root_path
+from ..templating import _default_template_ctx_processor
+
+if t.TYPE_CHECKING: # pragma: no cover
+ from click import Group
+
+# a singleton sentinel value for parameter defaults
+_sentinel = object()
+
+F = t.TypeVar("F", bound=t.Callable[..., t.Any])
+T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable[t.Any])
+T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
+T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
+T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
+T_template_context_processor = t.TypeVar(
+ "T_template_context_processor", bound=ft.TemplateContextProcessorCallable
+)
+T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable)
+T_url_value_preprocessor = t.TypeVar(
+ "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable
+)
+T_route = t.TypeVar("T_route", bound=ft.RouteCallable)
+
+
+def setupmethod(f: F) -> F:
+ f_name = f.__name__
+
+ def wrapper_func(self: Scaffold, *args: t.Any, **kwargs: t.Any) -> t.Any:
+ self._check_setup_finished(f_name)
+ 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
+ """
+
+ cli: Group
+ name: str
+ _static_folder: str | None = None
+ _static_url_path: str | None = None
+
+ def __init__(
+ self,
+ import_name: str,
+ static_folder: str | os.PathLike[str] | None = None,
+ static_url_path: str | None = None,
+ template_folder: str | os.PathLike[str] | None = None,
+ root_path: str | None = 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
+
+ #: 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: dict[str, ft.RouteCallable] = {}
+
+ #: 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: dict[
+ ft.AppOrBlueprintKey,
+ dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]],
+ ] = 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: dict[
+ ft.AppOrBlueprintKey, list[ft.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: dict[
+ ft.AppOrBlueprintKey, list[ft.AfterRequestCallable[t.Any]]
+ ] = 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: dict[
+ ft.AppOrBlueprintKey, list[ft.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: dict[
+ ft.AppOrBlueprintKey, list[ft.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: dict[
+ ft.AppOrBlueprintKey,
+ list[ft.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: dict[
+ ft.AppOrBlueprintKey, list[ft.URLDefaultCallable]
+ ] = defaultdict(list)
+
+ def __repr__(self) -> str:
+ return f"<{type(self).__name__} {self.name!r}>"
+
+ def _check_setup_finished(self, f_name: str) -> None:
+ raise NotImplementedError
+
+ @property
+ def static_folder(self) -> str | None:
+ """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: str | os.PathLike[str] | None) -> 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) -> str | None:
+ """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: str | None) -> None:
+ if value is not None:
+ value = value.rstrip("/")
+
+ self._static_url_path = value
+
+ @cached_property
+ def jinja_loader(self) -> BaseLoader | None:
+ """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 _method_route(
+ self,
+ method: str,
+ rule: str,
+ options: dict[str, t.Any],
+ ) -> t.Callable[[T_route], T_route]:
+ if "methods" in options:
+ raise TypeError("Use the 'route' decorator to use the 'methods' argument.")
+
+ return self.route(rule, methods=[method], **options)
+
+ @setupmethod
+ def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """Shortcut for :meth:`route` with ``methods=["GET"]``.
+
+ .. versionadded:: 2.0
+ """
+ return self._method_route("GET", rule, options)
+
+ @setupmethod
+ def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """Shortcut for :meth:`route` with ``methods=["POST"]``.
+
+ .. versionadded:: 2.0
+ """
+ return self._method_route("POST", rule, options)
+
+ @setupmethod
+ def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """Shortcut for :meth:`route` with ``methods=["PUT"]``.
+
+ .. versionadded:: 2.0
+ """
+ return self._method_route("PUT", rule, options)
+
+ @setupmethod
+ def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """Shortcut for :meth:`route` with ``methods=["DELETE"]``.
+
+ .. versionadded:: 2.0
+ """
+ return self._method_route("DELETE", rule, options)
+
+ @setupmethod
+ def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """Shortcut for :meth:`route` with ``methods=["PATCH"]``.
+
+ .. versionadded:: 2.0
+ """
+ return self._method_route("PATCH", rule, options)
+
+ @setupmethod
+ def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
+ """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_route) -> T_route:
+ 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: str | None = None,
+ view_func: ft.RouteCallable | None = None,
+ provide_automatic_options: bool | None = 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
+
+ @setupmethod
+ def endpoint(self, endpoint: str) -> t.Callable[[F], F]:
+ """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: F) -> F:
+ self.view_functions[endpoint] = f
+ return f
+
+ return decorator
+
+ @setupmethod
+ def before_request(self, f: T_before_request) -> T_before_request:
+ """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.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ executes before every request. When used on a blueprint, this executes before
+ every request that the blueprint handles. To register with a blueprint and
+ execute before every request, use :meth:`.Blueprint.before_app_request`.
+ """
+ self.before_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def after_request(self, f: T_after_request) -> T_after_request:
+ """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.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ executes after every request. When used on a blueprint, this executes after
+ every request that the blueprint handles. To register with a blueprint and
+ execute after every request, use :meth:`.Blueprint.after_app_request`.
+ """
+ self.after_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def teardown_request(self, f: T_teardown) -> T_teardown:
+ """Register a function to be called when the request context is
+ popped. Typically this happens at the end of each request, but
+ contexts may be pushed manually as well during testing.
+
+ .. code-block:: python
+
+ with app.test_request_context():
+ ...
+
+ When the ``with`` block exits (or ``ctx.pop()`` is called), the
+ teardown functions are called just before the request context is
+ made inactive.
+
+ When a teardown function was called because of an unhandled
+ exception it will be passed an error object. If an
+ :meth:`errorhandler` is registered, it will handle the exception
+ and the teardown will not receive it.
+
+ Teardown functions must avoid raising exceptions. If they
+ execute code that might fail they must surround that code with a
+ ``try``/``except`` block and log any errors.
+
+ The return values of teardown functions are ignored.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ executes after every request. When used on a blueprint, this executes after
+ every request that the blueprint handles. To register with a blueprint and
+ execute after every request, use :meth:`.Blueprint.teardown_app_request`.
+ """
+ self.teardown_request_funcs.setdefault(None, []).append(f)
+ return f
+
+ @setupmethod
+ def context_processor(
+ self,
+ f: T_template_context_processor,
+ ) -> T_template_context_processor:
+ """Registers a template context processor function. These functions run before
+ rendering a template. The keys of the returned dict are added as variables
+ available in the template.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ is called for every rendered template. When used on a blueprint, this is called
+ for templates rendered from the blueprint's views. To register with a blueprint
+ and affect every template, use :meth:`.Blueprint.app_context_processor`.
+ """
+ self.template_context_processors[None].append(f)
+ return f
+
+ @setupmethod
+ def url_value_preprocessor(
+ self,
+ f: T_url_value_preprocessor,
+ ) -> T_url_value_preprocessor:
+ """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.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ is called for every request. When used on a blueprint, this is called for
+ requests that the blueprint handles. To register with a blueprint and affect
+ every request, use :meth:`.Blueprint.app_url_value_preprocessor`.
+ """
+ self.url_value_preprocessors[None].append(f)
+ return f
+
+ @setupmethod
+ def url_defaults(self, f: T_url_defaults) -> T_url_defaults:
+ """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.
+
+ This is available on both app and blueprint objects. When used on an app, this
+ is called for every request. When used on a blueprint, this is called for
+ requests that the blueprint handles. To register with a blueprint and affect
+ every request, use :meth:`.Blueprint.app_url_defaults`.
+ """
+ self.url_default_functions[None].append(f)
+ return f
+
+ @setupmethod
+ def errorhandler(
+ self, code_or_exception: type[Exception] | int
+ ) -> t.Callable[[T_error_handler], T_error_handler]:
+ """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
+
+ This is available on both app and blueprint objects. When used on an app, this
+ can handle errors from every request. When used on a blueprint, this can handle
+ errors from requests that the blueprint handles. To register with a blueprint
+ and affect every request, use :meth:`.Blueprint.app_errorhandler`.
+
+ .. 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: T_error_handler) -> T_error_handler:
+ self.register_error_handler(code_or_exception, f)
+ return f
+
+ return decorator
+
+ @setupmethod
+ def register_error_handler(
+ self,
+ code_or_exception: type[Exception] | int,
+ f: ft.ErrorHandlerCallable,
+ ) -> None:
+ """Alternative error attach function to the :meth:`errorhandler`
+ decorator that is more straightforward to use for non decorator
+ usage.
+
+ .. versionadded:: 0.7
+ """
+ exc_class, code = self._get_exc_class_and_code(code_or_exception)
+ self.error_handler_spec[None][code][exc_class] = f
+
+ @staticmethod
+ def _get_exc_class_and_code(
+ exc_class_or_code: type[Exception] | int,
+ ) -> tuple[type[Exception], int | None]:
+ """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: type[Exception]
+
+ if isinstance(exc_class_or_code, int):
+ try:
+ exc_class = default_exceptions[exc_class_or_code]
+ except KeyError:
+ raise ValueError(
+ f"'{exc_class_or_code}' is not a recognized HTTP"
+ " error code. Use a subclass of HTTPException with"
+ " that code instead."
+ ) from None
+ else:
+ exc_class = exc_class_or_code
+
+ if isinstance(exc_class, Exception):
+ raise TypeError(
+ f"{exc_class!r} is an instance, not a class. Handlers"
+ " can only be registered for Exception classes or HTTP"
+ " error codes."
+ )
+
+ if not issubclass(exc_class, Exception):
+ raise ValueError(
+ f"'{exc_class.__name__}' is not a subclass of Exception."
+ " Handlers can only be registered for Exception classes"
+ " or HTTP error codes."
+ )
+
+ if issubclass(exc_class, HTTPException):
+ return exc_class, exc_class.code
+ else:
+ return exc_class, None
+
+
+def _endpoint_from_view_func(view_func: ft.RouteCallable) -> 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 _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool:
+ # Path.is_relative_to doesn't exist until Python 3.9
+ try:
+ path.relative_to(base)
+ return True
+ except ValueError:
+ return False
+
+
+def _find_package_path(import_name: str) -> str:
+ """Find the path that contains the package or module."""
+ root_mod_name, _, _ = import_name.partition(".")
+
+ try:
+ root_spec = importlib.util.find_spec(root_mod_name)
+
+ if root_spec is None:
+ raise ValueError("not found")
+ except (ImportError, ValueError):
+ # 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 `root_spec` being `None`
+ return os.getcwd()
+
+ if root_spec.submodule_search_locations:
+ if root_spec.origin is None or root_spec.origin == "namespace":
+ # namespace package
+ package_spec = importlib.util.find_spec(import_name)
+
+ if package_spec is not None and package_spec.submodule_search_locations:
+ # Pick the path in the namespace that contains the submodule.
+ package_path = pathlib.Path(
+ os.path.commonpath(package_spec.submodule_search_locations)
+ )
+ search_location = next(
+ location
+ for location in root_spec.submodule_search_locations
+ if _path_is_relative_to(package_path, location)
+ )
+ else:
+ # Pick the first path.
+ search_location = root_spec.submodule_search_locations[0]
+
+ return os.path.dirname(search_location)
+ else:
+ # package with __init__.py
+ return os.path.dirname(os.path.dirname(root_spec.origin))
+ else:
+ # module
+ return os.path.dirname(root_spec.origin) # type: ignore[type-var, return-value]
+
+
+def find_package(import_name: str) -> tuple[str | None, 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.
+ """
+ package_path = _find_package_path(import_name)
+ py_prefix = os.path.abspath(sys.prefix)
+
+ # installed to the system
+ if _path_is_relative_to(pathlib.PurePath(package_path), 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