codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import logging4import os5import sys6import typing as t7from datetime import timedelta8from itertools import chain9 10from werkzeug.exceptions import Aborter11from werkzeug.exceptions import BadRequest12from werkzeug.exceptions import BadRequestKeyError13from werkzeug.routing import BuildError14from werkzeug.routing import Map15from werkzeug.routing import Rule16from werkzeug.sansio.response import Response17from werkzeug.utils import cached_property18from werkzeug.utils import redirect as _wz_redirect19 20from .. import typing as ft21from ..config import Config22from ..config import ConfigAttribute23from ..ctx import _AppCtxGlobals24from ..helpers import _split_blueprint_path25from ..helpers import get_debug_flag26from ..json.provider import DefaultJSONProvider27from ..json.provider import JSONProvider28from ..logging import create_logger29from ..templating import DispatchingJinjaLoader30from ..templating import Environment31from .scaffold import _endpoint_from_view_func32from .scaffold import find_package33from .scaffold import Scaffold34from .scaffold import setupmethod35 36if t.TYPE_CHECKING: # pragma: no cover37 from werkzeug.wrappers import Response as BaseResponse38 39 from ..testing import FlaskClient40 from ..testing import FlaskCliRunner41 from .blueprints import Blueprint42 43T_shell_context_processor = t.TypeVar(44 "T_shell_context_processor", bound=ft.ShellContextProcessorCallable45)46T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)47T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)48T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)49T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)50 51 52def _make_timedelta(value: timedelta | int | None) -> timedelta | None:53 if value is None or isinstance(value, timedelta):54 return value55 56 return timedelta(seconds=value)57 58 59class App(Scaffold):60 """The flask object implements a WSGI application and acts as the central61 object. It is passed the name of the module or package of the62 application. Once it is created it will act as a central registry for63 the view functions, the URL rules, template configuration and much more.64 65 The name of the package is used to resolve resources from inside the66 package or the folder the module is contained in depending on if the67 package parameter resolves to an actual python package (a folder with68 an :file:`__init__.py` file inside) or a standard module (just a ``.py`` file).69 70 For more information about resource loading, see :func:`open_resource`.71 72 Usually you create a :class:`Flask` instance in your main module or73 in the :file:`__init__.py` file of your package like this::74 75 from flask import Flask76 app = Flask(__name__)77 78 .. admonition:: About the First Parameter79 80 The idea of the first parameter is to give Flask an idea of what81 belongs to your application. This name is used to find resources82 on the filesystem, can be used by extensions to improve debugging83 information and a lot more.84 85 So it's important what you provide there. If you are using a single86 module, `__name__` is always the correct value. If you however are87 using a package, it's usually recommended to hardcode the name of88 your package there.89 90 For example if your application is defined in :file:`yourapplication/app.py`91 you should create it with one of the two versions below::92 93 app = Flask('yourapplication')94 app = Flask(__name__.split('.')[0])95 96 Why is that? The application will work even with `__name__`, thanks97 to how resources are looked up. However it will make debugging more98 painful. Certain extensions can make assumptions based on the99 import name of your application. For example the Flask-SQLAlchemy100 extension will look for the code in your application that triggered101 an SQL query in debug mode. If the import name is not properly set102 up, that debugging information is lost. (For example it would only103 pick up SQL queries in `yourapplication.app` and not104 `yourapplication.views.frontend`)105 106 .. versionadded:: 0.7107 The `static_url_path`, `static_folder`, and `template_folder`108 parameters were added.109 110 .. versionadded:: 0.8111 The `instance_path` and `instance_relative_config` parameters were112 added.113 114 .. versionadded:: 0.11115 The `root_path` parameter was added.116 117 .. versionadded:: 1.0118 The ``host_matching`` and ``static_host`` parameters were added.119 120 .. versionadded:: 1.0121 The ``subdomain_matching`` parameter was added. Subdomain122 matching needs to be enabled manually now. Setting123 :data:`SERVER_NAME` does not implicitly enable it.124 125 :param import_name: the name of the application package126 :param static_url_path: can be used to specify a different path for the127 static files on the web. Defaults to the name128 of the `static_folder` folder.129 :param static_folder: The folder with static files that is served at130 ``static_url_path``. Relative to the application ``root_path``131 or an absolute path. Defaults to ``'static'``.132 :param static_host: the host to use when adding the static route.133 Defaults to None. Required when using ``host_matching=True``134 with a ``static_folder`` configured.135 :param host_matching: set ``url_map.host_matching`` attribute.136 Defaults to False.137 :param subdomain_matching: consider the subdomain relative to138 :data:`SERVER_NAME` when matching routes. Defaults to False.139 :param template_folder: the folder that contains the templates that should140 be used by the application. Defaults to141 ``'templates'`` folder in the root path of the142 application.143 :param instance_path: An alternative instance path for the application.144 By default the folder ``'instance'`` next to the145 package or module is assumed to be the instance146 path.147 :param instance_relative_config: if set to ``True`` relative filenames148 for loading the config are assumed to149 be relative to the instance path instead150 of the application root.151 :param root_path: The path to the root of the application files.152 This should only be set manually when it can't be detected153 automatically, such as for namespace packages.154 """155 156 #: The class of the object assigned to :attr:`aborter`, created by157 #: :meth:`create_aborter`. That object is called by158 #: :func:`flask.abort` to raise HTTP errors, and can be159 #: called directly as well.160 #:161 #: Defaults to :class:`werkzeug.exceptions.Aborter`.162 #:163 #: .. versionadded:: 2.2164 aborter_class = Aborter165 166 #: The class that is used for the Jinja environment.167 #:168 #: .. versionadded:: 0.11169 jinja_environment = Environment170 171 #: The class that is used for the :data:`~flask.g` instance.172 #:173 #: Example use cases for a custom class:174 #:175 #: 1. Store arbitrary attributes on flask.g.176 #: 2. Add a property for lazy per-request database connectors.177 #: 3. Return None instead of AttributeError on unexpected attributes.178 #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.179 #:180 #: In Flask 0.9 this property was called `request_globals_class` but it181 #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the182 #: flask.g object is now application context scoped.183 #:184 #: .. versionadded:: 0.10185 app_ctx_globals_class = _AppCtxGlobals186 187 #: The class that is used for the ``config`` attribute of this app.188 #: Defaults to :class:`~flask.Config`.189 #:190 #: Example use cases for a custom class:191 #:192 #: 1. Default values for certain config options.193 #: 2. Access to config values through attributes in addition to keys.194 #:195 #: .. versionadded:: 0.11196 config_class = Config197 198 #: The testing flag. Set this to ``True`` to enable the test mode of199 #: Flask extensions (and in the future probably also Flask itself).200 #: For example this might activate test helpers that have an201 #: additional runtime cost which should not be enabled by default.202 #:203 #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the204 #: default it's implicitly enabled.205 #:206 #: This attribute can also be configured from the config with the207 #: ``TESTING`` configuration key. Defaults to ``False``.208 testing = ConfigAttribute[bool]("TESTING")209 210 #: If a secret key is set, cryptographic components can use this to211 #: sign cookies and other things. Set this to a complex random value212 #: when you want to use the secure cookie for instance.213 #:214 #: This attribute can also be configured from the config with the215 #: :data:`SECRET_KEY` configuration key. Defaults to ``None``.216 secret_key = ConfigAttribute[t.Union[str, bytes, None]]("SECRET_KEY")217 218 #: A :class:`~datetime.timedelta` which is used to set the expiration219 #: date of a permanent session. The default is 31 days which makes a220 #: permanent session survive for roughly one month.221 #:222 #: This attribute can also be configured from the config with the223 #: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to224 #: ``timedelta(days=31)``225 permanent_session_lifetime = ConfigAttribute[timedelta](226 "PERMANENT_SESSION_LIFETIME",227 get_converter=_make_timedelta, # type: ignore[arg-type]228 )229 230 json_provider_class: type[JSONProvider] = DefaultJSONProvider231 """A subclass of :class:`~flask.json.provider.JSONProvider`. An232 instance is created and assigned to :attr:`app.json` when creating233 the app.234 235 The default, :class:`~flask.json.provider.DefaultJSONProvider`, uses236 Python's built-in :mod:`json` library. A different provider can use237 a different JSON library.238 239 .. versionadded:: 2.2240 """241 242 #: Options that are passed to the Jinja environment in243 #: :meth:`create_jinja_environment`. Changing these options after244 #: the environment is created (accessing :attr:`jinja_env`) will245 #: have no effect.246 #:247 #: .. versionchanged:: 1.1.0248 #: This is a ``dict`` instead of an ``ImmutableDict`` to allow249 #: easier configuration.250 #:251 jinja_options: dict[str, t.Any] = {}252 253 #: The rule object to use for URL rules created. This is used by254 #: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`.255 #:256 #: .. versionadded:: 0.7257 url_rule_class = Rule258 259 #: The map object to use for storing the URL rules and routing260 #: configuration parameters. Defaults to :class:`werkzeug.routing.Map`.261 #:262 #: .. versionadded:: 1.1.0263 url_map_class = Map264 265 #: The :meth:`test_client` method creates an instance of this test266 #: client class. Defaults to :class:`~flask.testing.FlaskClient`.267 #:268 #: .. versionadded:: 0.7269 test_client_class: type[FlaskClient] | None = None270 271 #: The :class:`~click.testing.CliRunner` subclass, by default272 #: :class:`~flask.testing.FlaskCliRunner` that is used by273 #: :meth:`test_cli_runner`. Its ``__init__`` method should take a274 #: Flask app object as the first argument.275 #:276 #: .. versionadded:: 1.0277 test_cli_runner_class: type[FlaskCliRunner] | None = None278 279 default_config: dict[str, t.Any]280 response_class: type[Response]281 282 def __init__(283 self,284 import_name: str,285 static_url_path: str | None = None,286 static_folder: str | os.PathLike[str] | None = "static",287 static_host: str | None = None,288 host_matching: bool = False,289 subdomain_matching: bool = False,290 template_folder: str | os.PathLike[str] | None = "templates",291 instance_path: str | None = None,292 instance_relative_config: bool = False,293 root_path: str | None = None,294 ) -> None:295 super().__init__(296 import_name=import_name,297 static_folder=static_folder,298 static_url_path=static_url_path,299 template_folder=template_folder,300 root_path=root_path,301 )302 303 if instance_path is None:304 instance_path = self.auto_find_instance_path()305 elif not os.path.isabs(instance_path):306 raise ValueError(307 "If an instance path is provided it must be absolute."308 " A relative path was given instead."309 )310 311 #: Holds the path to the instance folder.312 #:313 #: .. versionadded:: 0.8314 self.instance_path = instance_path315 316 #: The configuration dictionary as :class:`Config`. This behaves317 #: exactly like a regular dictionary but supports additional methods318 #: to load a config from files.319 self.config = self.make_config(instance_relative_config)320 321 #: An instance of :attr:`aborter_class` created by322 #: :meth:`make_aborter`. This is called by :func:`flask.abort`323 #: to raise HTTP errors, and can be called directly as well.324 #:325 #: .. versionadded:: 2.2326 #: Moved from ``flask.abort``, which calls this object.327 self.aborter = self.make_aborter()328 329 self.json: JSONProvider = self.json_provider_class(self)330 """Provides access to JSON methods. Functions in ``flask.json``331 will call methods on this provider when the application context332 is active. Used for handling JSON requests and responses.333 334 An instance of :attr:`json_provider_class`. Can be customized by335 changing that attribute on a subclass, or by assigning to this336 attribute afterwards.337 338 The default, :class:`~flask.json.provider.DefaultJSONProvider`,339 uses Python's built-in :mod:`json` library. A different provider340 can use a different JSON library.341 342 .. versionadded:: 2.2343 """344 345 #: A list of functions that are called by346 #: :meth:`handle_url_build_error` when :meth:`.url_for` raises a347 #: :exc:`~werkzeug.routing.BuildError`. Each function is called348 #: with ``error``, ``endpoint`` and ``values``. If a function349 #: returns ``None`` or raises a ``BuildError``, it is skipped.350 #: Otherwise, its return value is returned by ``url_for``.351 #:352 #: .. versionadded:: 0.9353 self.url_build_error_handlers: list[354 t.Callable[[Exception, str, dict[str, t.Any]], str]355 ] = []356 357 #: A list of functions that are called when the application context358 #: is destroyed. Since the application context is also torn down359 #: if the request ends this is the place to store code that disconnects360 #: from databases.361 #:362 #: .. versionadded:: 0.9363 self.teardown_appcontext_funcs: list[ft.TeardownCallable] = []364 365 #: A list of shell context processor functions that should be run366 #: when a shell context is created.367 #:368 #: .. versionadded:: 0.11369 self.shell_context_processors: list[ft.ShellContextProcessorCallable] = []370 371 #: Maps registered blueprint names to blueprint objects. The372 #: dict retains the order the blueprints were registered in.373 #: Blueprints can be registered multiple times, this dict does374 #: not track how often they were attached.375 #:376 #: .. versionadded:: 0.7377 self.blueprints: dict[str, Blueprint] = {}378 379 #: a place where extensions can store application specific state. For380 #: example this is where an extension could store database engines and381 #: similar things.382 #:383 #: The key must match the name of the extension module. For example in384 #: case of a "Flask-Foo" extension in `flask_foo`, the key would be385 #: ``'foo'``.386 #:387 #: .. versionadded:: 0.7388 self.extensions: dict[str, t.Any] = {}389 390 #: The :class:`~werkzeug.routing.Map` for this instance. You can use391 #: this to change the routing converters after the class was created392 #: but before any routes are connected. Example::393 #:394 #: from werkzeug.routing import BaseConverter395 #:396 #: class ListConverter(BaseConverter):397 #: def to_python(self, value):398 #: return value.split(',')399 #: def to_url(self, values):400 #: return ','.join(super(ListConverter, self).to_url(value)401 #: for value in values)402 #:403 #: app = Flask(__name__)404 #: app.url_map.converters['list'] = ListConverter405 self.url_map = self.url_map_class(host_matching=host_matching)406 407 self.subdomain_matching = subdomain_matching408 409 # tracks internally if the application already handled at least one410 # request.411 self._got_first_request = False412 413 def _check_setup_finished(self, f_name: str) -> None:414 if self._got_first_request:415 raise AssertionError(416 f"The setup method '{f_name}' can no longer be called"417 " on the application. It has already handled its first"418 " request, any changes will not be applied"419 " consistently.\n"420 "Make sure all imports, decorators, functions, etc."421 " needed to set up the application are done before"422 " running it."423 )424 425 @cached_property426 def name(self) -> str:427 """The name of the application. This is usually the import name428 with the difference that it's guessed from the run file if the429 import name is main. This name is used as a display name when430 Flask needs the name of the application. It can be set and overridden431 to change the value.432 433 .. versionadded:: 0.8434 """435 if self.import_name == "__main__":436 fn: str | None = getattr(sys.modules["__main__"], "__file__", None)437 if fn is None:438 return "__main__"439 return os.path.splitext(os.path.basename(fn))[0]440 return self.import_name441 442 @cached_property443 def logger(self) -> logging.Logger:444 """A standard Python :class:`~logging.Logger` for the app, with445 the same name as :attr:`name`.446 447 In debug mode, the logger's :attr:`~logging.Logger.level` will448 be set to :data:`~logging.DEBUG`.449 450 If there are no handlers configured, a default handler will be451 added. See :doc:`/logging` for more information.452 453 .. versionchanged:: 1.1.0454 The logger takes the same name as :attr:`name` rather than455 hard-coding ``"flask.app"``.456 457 .. versionchanged:: 1.0.0458 Behavior was simplified. The logger is always named459 ``"flask.app"``. The level is only set during configuration,460 it doesn't check ``app.debug`` each time. Only one format is461 used, not different ones depending on ``app.debug``. No462 handlers are removed, and a handler is only added if no463 handlers are already configured.464 465 .. versionadded:: 0.3466 """467 return create_logger(self)468 469 @cached_property470 def jinja_env(self) -> Environment:471 """The Jinja environment used to load templates.472 473 The environment is created the first time this property is474 accessed. Changing :attr:`jinja_options` after that will have no475 effect.476 """477 return self.create_jinja_environment()478 479 def create_jinja_environment(self) -> Environment:480 raise NotImplementedError()481 482 def make_config(self, instance_relative: bool = False) -> Config:483 """Used to create the config attribute by the Flask constructor.484 The `instance_relative` parameter is passed in from the constructor485 of Flask (there named `instance_relative_config`) and indicates if486 the config should be relative to the instance path or the root path487 of the application.488 489 .. versionadded:: 0.8490 """491 root_path = self.root_path492 if instance_relative:493 root_path = self.instance_path494 defaults = dict(self.default_config)495 defaults["DEBUG"] = get_debug_flag()496 return self.config_class(root_path, defaults)497 498 def make_aborter(self) -> Aborter:499 """Create the object to assign to :attr:`aborter`. That object500 is called by :func:`flask.abort` to raise HTTP errors, and can501 be called directly as well.502 503 By default, this creates an instance of :attr:`aborter_class`,504 which defaults to :class:`werkzeug.exceptions.Aborter`.505 506 .. versionadded:: 2.2507 """508 return self.aborter_class()509 510 def auto_find_instance_path(self) -> str:511 """Tries to locate the instance path if it was not provided to the512 constructor of the application class. It will basically calculate513 the path to a folder named ``instance`` next to your main file or514 the package.515 516 .. versionadded:: 0.8517 """518 prefix, package_path = find_package(self.import_name)519 if prefix is None:520 return os.path.join(package_path, "instance")521 return os.path.join(prefix, "var", f"{self.name}-instance")522 523 def create_global_jinja_loader(self) -> DispatchingJinjaLoader:524 """Creates the loader for the Jinja environment. Can be used to525 override just the loader and keeping the rest unchanged. It's526 discouraged to override this function. Instead one should override527 the :meth:`jinja_loader` function instead.528 529 The global loader dispatches between the loaders of the application530 and the individual blueprints.531 532 .. versionadded:: 0.7533 """534 return DispatchingJinjaLoader(self)535 536 def select_jinja_autoescape(self, filename: str | None) -> bool:537 """Returns ``True`` if autoescaping should be active for the given538 template name. If no template name is given, returns `True`.539 540 .. versionchanged:: 2.2541 Autoescaping is now enabled by default for ``.svg`` files.542 543 .. versionadded:: 0.5544 """545 if filename is None:546 return True547 return filename.endswith((".html", ".htm", ".xml", ".xhtml", ".svg"))548 549 @property550 def debug(self) -> bool:551 """Whether debug mode is enabled. When using ``flask run`` to start the552 development server, an interactive debugger will be shown for unhandled553 exceptions, and the server will be reloaded when code changes. This maps to the554 :data:`DEBUG` config key. It may not behave as expected if set late.555 556 **Do not enable debug mode when deploying in production.**557 558 Default: ``False``559 """560 return self.config["DEBUG"] # type: ignore[no-any-return]561 562 @debug.setter563 def debug(self, value: bool) -> None:564 self.config["DEBUG"] = value565 566 if self.config["TEMPLATES_AUTO_RELOAD"] is None:567 self.jinja_env.auto_reload = value568 569 @setupmethod570 def register_blueprint(self, blueprint: Blueprint, **options: t.Any) -> None:571 """Register a :class:`~flask.Blueprint` on the application. Keyword572 arguments passed to this method will override the defaults set on the573 blueprint.574 575 Calls the blueprint's :meth:`~flask.Blueprint.register` method after576 recording the blueprint in the application's :attr:`blueprints`.577 578 :param blueprint: The blueprint to register.579 :param url_prefix: Blueprint routes will be prefixed with this.580 :param subdomain: Blueprint routes will match on this subdomain.581 :param url_defaults: Blueprint routes will use these default values for582 view arguments.583 :param options: Additional keyword arguments are passed to584 :class:`~flask.blueprints.BlueprintSetupState`. They can be585 accessed in :meth:`~flask.Blueprint.record` callbacks.586 587 .. versionchanged:: 2.0.1588 The ``name`` option can be used to change the (pre-dotted)589 name the blueprint is registered with. This allows the same590 blueprint to be registered multiple times with unique names591 for ``url_for``.592 593 .. versionadded:: 0.7594 """595 blueprint.register(self, options)596 597 def iter_blueprints(self) -> t.ValuesView[Blueprint]:598 """Iterates over all blueprints by the order they were registered.599 600 .. versionadded:: 0.11601 """602 return self.blueprints.values()603 604 @setupmethod605 def add_url_rule(606 self,607 rule: str,608 endpoint: str | None = None,609 view_func: ft.RouteCallable | None = None,610 provide_automatic_options: bool | None = None,611 **options: t.Any,612 ) -> None:613 if endpoint is None:614 endpoint = _endpoint_from_view_func(view_func) # type: ignore615 options["endpoint"] = endpoint616 methods = options.pop("methods", None)617 618 # if the methods are not given and the view_func object knows its619 # methods we can use that instead. If neither exists, we go with620 # a tuple of only ``GET`` as default.621 if methods is None:622 methods = getattr(view_func, "methods", None) or ("GET",)623 if isinstance(methods, str):624 raise TypeError(625 "Allowed methods must be a list of strings, for"626 ' example: @app.route(..., methods=["POST"])'627 )628 methods = {item.upper() for item in methods}629 630 # Methods that should always be added631 required_methods: set[str] = set(getattr(view_func, "required_methods", ()))632 633 # starting with Flask 0.8 the view_func object can disable and634 # force-enable the automatic options handling.635 if provide_automatic_options is None:636 provide_automatic_options = getattr(637 view_func, "provide_automatic_options", None638 )639 640 if provide_automatic_options is None:641 if "OPTIONS" not in methods and self.config["PROVIDE_AUTOMATIC_OPTIONS"]:642 provide_automatic_options = True643 required_methods.add("OPTIONS")644 else:645 provide_automatic_options = False646 647 # Add the required methods now.648 methods |= required_methods649 650 rule_obj = self.url_rule_class(rule, methods=methods, **options)651 rule_obj.provide_automatic_options = provide_automatic_options # type: ignore[attr-defined]652 653 self.url_map.add(rule_obj)654 if view_func is not None:655 old_func = self.view_functions.get(endpoint)656 if old_func is not None and old_func != view_func:657 raise AssertionError(658 "View function mapping is overwriting an existing"659 f" endpoint function: {endpoint}"660 )661 self.view_functions[endpoint] = view_func662 663 @setupmethod664 def template_filter(665 self, name: str | None = None666 ) -> t.Callable[[T_template_filter], T_template_filter]:667 """A decorator that is used to register custom template filter.668 You can specify a name for the filter, otherwise the function669 name will be used. Example::670 671 @app.template_filter()672 def reverse(s):673 return s[::-1]674 675 :param name: the optional name of the filter, otherwise the676 function name will be used.677 """678 679 def decorator(f: T_template_filter) -> T_template_filter:680 self.add_template_filter(f, name=name)681 return f682 683 return decorator684 685 @setupmethod686 def add_template_filter(687 self, f: ft.TemplateFilterCallable, name: str | None = None688 ) -> None:689 """Register a custom template filter. Works exactly like the690 :meth:`template_filter` decorator.691 692 :param name: the optional name of the filter, otherwise the693 function name will be used.694 """695 self.jinja_env.filters[name or f.__name__] = f696 697 @setupmethod698 def template_test(699 self, name: str | None = None700 ) -> t.Callable[[T_template_test], T_template_test]:701 """A decorator that is used to register custom template test.702 You can specify a name for the test, otherwise the function703 name will be used. Example::704 705 @app.template_test()706 def is_prime(n):707 if n == 2:708 return True709 for i in range(2, int(math.ceil(math.sqrt(n))) + 1):710 if n % i == 0:711 return False712 return True713 714 .. versionadded:: 0.10715 716 :param name: the optional name of the test, otherwise the717 function name will be used.718 """719 720 def decorator(f: T_template_test) -> T_template_test:721 self.add_template_test(f, name=name)722 return f723 724 return decorator725 726 @setupmethod727 def add_template_test(728 self, f: ft.TemplateTestCallable, name: str | None = None729 ) -> None:730 """Register a custom template test. Works exactly like the731 :meth:`template_test` decorator.732 733 .. versionadded:: 0.10734 735 :param name: the optional name of the test, otherwise the736 function name will be used.737 """738 self.jinja_env.tests[name or f.__name__] = f739 740 @setupmethod741 def template_global(742 self, name: str | None = None743 ) -> t.Callable[[T_template_global], T_template_global]:744 """A decorator that is used to register a custom template global function.745 You can specify a name for the global function, otherwise the function746 name will be used. Example::747 748 @app.template_global()749 def double(n):750 return 2 * n751 752 .. versionadded:: 0.10753 754 :param name: the optional name of the global function, otherwise the755 function name will be used.756 """757 758 def decorator(f: T_template_global) -> T_template_global:759 self.add_template_global(f, name=name)760 return f761 762 return decorator763 764 @setupmethod765 def add_template_global(766 self, f: ft.TemplateGlobalCallable, name: str | None = None767 ) -> None:768 """Register a custom template global function. Works exactly like the769 :meth:`template_global` decorator.770 771 .. versionadded:: 0.10772 773 :param name: the optional name of the global function, otherwise the774 function name will be used.775 """776 self.jinja_env.globals[name or f.__name__] = f777 778 @setupmethod779 def teardown_appcontext(self, f: T_teardown) -> T_teardown:780 """Registers a function to be called when the application781 context is popped. The application context is typically popped782 after the request context for each request, at the end of CLI783 commands, or after a manually pushed context ends.784 785 .. code-block:: python786 787 with app.app_context():788 ...789 790 When the ``with`` block exits (or ``ctx.pop()`` is called), the791 teardown functions are called just before the app context is792 made inactive. Since a request context typically also manages an793 application context it would also be called when you pop a794 request context.795 796 When a teardown function was called because of an unhandled797 exception it will be passed an error object. If an798 :meth:`errorhandler` is registered, it will handle the exception799 and the teardown will not receive it.800 801 Teardown functions must avoid raising exceptions. If they802 execute code that might fail they must surround that code with a803 ``try``/``except`` block and log any errors.804 805 The return values of teardown functions are ignored.806 807 .. versionadded:: 0.9808 """809 self.teardown_appcontext_funcs.append(f)810 return f811 812 @setupmethod813 def shell_context_processor(814 self, f: T_shell_context_processor815 ) -> T_shell_context_processor:816 """Registers a shell context processor function.817 818 .. versionadded:: 0.11819 """820 self.shell_context_processors.append(f)821 return f822 823 def _find_error_handler(824 self, e: Exception, blueprints: list[str]825 ) -> ft.ErrorHandlerCallable | None:826 """Return a registered error handler for an exception in this order:827 blueprint handler for a specific code, app handler for a specific code,828 blueprint handler for an exception class, app handler for an exception829 class, or ``None`` if a suitable handler is not found.830 """831 exc_class, code = self._get_exc_class_and_code(type(e))832 names = (*blueprints, None)833 834 for c in (code, None) if code is not None else (None,):835 for name in names:836 handler_map = self.error_handler_spec[name][c]837 838 if not handler_map:839 continue840 841 for cls in exc_class.__mro__:842 handler = handler_map.get(cls)843 844 if handler is not None:845 return handler846 return None847 848 def trap_http_exception(self, e: Exception) -> bool:849 """Checks if an HTTP exception should be trapped or not. By default850 this will return ``False`` for all exceptions except for a bad request851 key error if ``TRAP_BAD_REQUEST_ERRORS`` is set to ``True``. It852 also returns ``True`` if ``TRAP_HTTP_EXCEPTIONS`` is set to ``True``.853 854 This is called for all HTTP exceptions raised by a view function.855 If it returns ``True`` for any exception the error handler for this856 exception is not called and it shows up as regular exception in the857 traceback. This is helpful for debugging implicitly raised HTTP858 exceptions.859 860 .. versionchanged:: 1.0861 Bad request errors are not trapped by default in debug mode.862 863 .. versionadded:: 0.8864 """865 if self.config["TRAP_HTTP_EXCEPTIONS"]:866 return True867 868 trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"]869 870 # if unset, trap key errors in debug mode871 if (872 trap_bad_request is None873 and self.debug874 and isinstance(e, BadRequestKeyError)875 ):876 return True877 878 if trap_bad_request:879 return isinstance(e, BadRequest)880 881 return False882 883 def should_ignore_error(self, error: BaseException | None) -> bool:884 """This is called to figure out if an error should be ignored885 or not as far as the teardown system is concerned. If this886 function returns ``True`` then the teardown handlers will not be887 passed the error.888 889 .. versionadded:: 0.10890 """891 return False892 893 def redirect(self, location: str, code: int = 302) -> BaseResponse:894 """Create a redirect response object.895 896 This is called by :func:`flask.redirect`, and can be called897 directly as well.898 899 :param location: The URL to redirect to.900 :param code: The status code for the redirect.901 902 .. versionadded:: 2.2903 Moved from ``flask.redirect``, which calls this method.904 """905 return _wz_redirect(906 location,907 code=code,908 Response=self.response_class, # type: ignore[arg-type]909 )910 911 def inject_url_defaults(self, endpoint: str, values: dict[str, t.Any]) -> None:912 """Injects the URL defaults for the given endpoint directly into913 the values dictionary passed. This is used internally and914 automatically called on URL building.915 916 .. versionadded:: 0.7917 """918 names: t.Iterable[str | None] = (None,)919 920 # url_for may be called outside a request context, parse the921 # passed endpoint instead of using request.blueprints.922 if "." in endpoint:923 names = chain(924 names, reversed(_split_blueprint_path(endpoint.rpartition(".")[0]))925 )926 927 for name in names:928 if name in self.url_default_functions:929 for func in self.url_default_functions[name]:930 func(endpoint, values)931 932 def handle_url_build_error(933 self, error: BuildError, endpoint: str, values: dict[str, t.Any]934 ) -> str:935 """Called by :meth:`.url_for` if a936 :exc:`~werkzeug.routing.BuildError` was raised. If this returns937 a value, it will be returned by ``url_for``, otherwise the error938 will be re-raised.939 940 Each function in :attr:`url_build_error_handlers` is called with941 ``error``, ``endpoint`` and ``values``. If a function returns942 ``None`` or raises a ``BuildError``, it is skipped. Otherwise,943 its return value is returned by ``url_for``.944 945 :param error: The active ``BuildError`` being handled.946 :param endpoint: The endpoint being built.947 :param values: The keyword arguments passed to ``url_for``.948 """949 for handler in self.url_build_error_handlers:950 try:951 rv = handler(error, endpoint, values)952 except BuildError as e:953 # make error available outside except block954 error = e955 else:956 if rv is not None:957 return rv958 959 # Re-raise if called with an active exception, otherwise raise960 # the passed in exception.961 if error is sys.exc_info()[1]:962 raise963 964 raise error965 