Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
app.py965 linesDownload Raw Back to sansio
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 
codekingpro/portable-devtools · Team Ai