123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964 |
- 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,
- ) -> 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[str] = 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 and self.config["PROVIDE_AUTOMATIC_OPTIONS"]:
- 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
|