Просмотр исходного кода

Intermediate changes
commit_hash:ae9e37c897fc6d514389f7089184df33bf781005

robot-piglet 1 месяц назад
Родитель
Сommit
7a23ad1fa2

+ 2 - 2
contrib/python/Flask/py3/.dist-info/METADATA

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: Flask
-Version: 2.1.3
+Version: 2.2.5
 Summary: A simple framework for building complex web applications.
 Home-page: https://palletsprojects.com/p/flask
 Author: Armin Ronacher
@@ -29,7 +29,7 @@ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
 Requires-Python: >=3.7
 Description-Content-Type: text/x-rst
 License-File: LICENSE.rst
-Requires-Dist: Werkzeug (>=2.0)
+Requires-Dist: Werkzeug (>=2.2.2)
 Requires-Dist: Jinja2 (>=3.0)
 Requires-Dist: itsdangerous (>=2.0)
 Requires-Dist: click (>=8.0)

+ 31 - 5
contrib/python/Flask/py3/flask/__init__.py

@@ -1,7 +1,5 @@
 from markupsafe import escape
 from markupsafe import Markup
-from werkzeug.exceptions import abort as abort
-from werkzeug.utils import redirect as redirect
 
 from . import json as json
 from .app import Flask as Flask
@@ -13,16 +11,16 @@ from .ctx import after_this_request as after_this_request
 from .ctx import copy_current_request_context as copy_current_request_context
 from .ctx import has_app_context as has_app_context
 from .ctx import has_request_context as has_request_context
-from .globals import _app_ctx_stack as _app_ctx_stack
-from .globals import _request_ctx_stack as _request_ctx_stack
 from .globals import current_app as current_app
 from .globals import g as g
 from .globals import request as request
 from .globals import session as session
+from .helpers import abort as abort
 from .helpers import flash as flash
 from .helpers import get_flashed_messages as get_flashed_messages
 from .helpers import get_template_attribute as get_template_attribute
 from .helpers import make_response as make_response
+from .helpers import redirect as redirect
 from .helpers import send_file as send_file
 from .helpers import send_from_directory as send_from_directory
 from .helpers import stream_with_context as stream_with_context
@@ -41,5 +39,33 @@ from .signals import signals_available as signals_available
 from .signals import template_rendered as template_rendered
 from .templating import render_template as render_template
 from .templating import render_template_string as render_template_string
+from .templating import stream_template as stream_template
+from .templating import stream_template_string as stream_template_string
 
-__version__ = "2.1.3"
+__version__ = "2.2.5"
+
+
+def __getattr__(name):
+    if name == "_app_ctx_stack":
+        import warnings
+        from .globals import __app_ctx_stack
+
+        warnings.warn(
+            "'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return __app_ctx_stack
+
+    if name == "_request_ctx_stack":
+        import warnings
+        from .globals import __request_ctx_stack
+
+        warnings.warn(
+            "'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return __request_ctx_stack
+
+    raise AttributeError(name)

Разница между файлами не показана из-за своего большого размера
+ 452 - 162
contrib/python/Flask/py3/flask/app.py


+ 196 - 75
contrib/python/Flask/py3/flask/blueprints.py

@@ -1,3 +1,4 @@
+import json
 import os
 import typing as t
 from collections import defaultdict
@@ -7,11 +8,29 @@ from . import typing as ft
 from .scaffold import _endpoint_from_view_func
 from .scaffold import _sentinel
 from .scaffold import Scaffold
+from .scaffold import setupmethod
 
-if t.TYPE_CHECKING:
+if t.TYPE_CHECKING:  # pragma: no cover
     from .app import Flask
 
 DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
+T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable)
+T_before_first_request = t.TypeVar(
+    "T_before_first_request", bound=ft.BeforeFirstRequestCallable
+)
+T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable)
+T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable)
+T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
+T_template_context_processor = t.TypeVar(
+    "T_template_context_processor", bound=ft.TemplateContextProcessorCallable
+)
+T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
+T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
+T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)
+T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable)
+T_url_value_preprocessor = t.TypeVar(
+    "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable
+)
 
 
 class BlueprintSetupState:
@@ -152,15 +171,78 @@ class Blueprint(Scaffold):
     .. versionadded:: 0.7
     """
 
-    warn_on_modifications = False
     _got_registered_once = False
 
-    #: Blueprint local JSON encoder class to use. Set to ``None`` to use
-    #: the app's :class:`~flask.Flask.json_encoder`.
-    json_encoder = None
-    #: Blueprint local JSON decoder class to use. Set to ``None`` to use
-    #: the app's :class:`~flask.Flask.json_decoder`.
-    json_decoder = None
+    _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None
+    _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None
+
+    @property
+    def json_encoder(
+        self,
+    ) -> t.Union[t.Type[json.JSONEncoder], None]:
+        """Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's.
+
+        .. deprecated:: 2.2
+             Will be removed in Flask 2.3. Customize
+             :attr:`json_provider_class` instead.
+
+        .. versionadded:: 0.10
+        """
+        import warnings
+
+        warnings.warn(
+            "'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
+            " Customize 'app.json_provider_class' or 'app.json' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return self._json_encoder
+
+    @json_encoder.setter
+    def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None:
+        import warnings
+
+        warnings.warn(
+            "'bp.json_encoder' is deprecated and will be removed in Flask 2.3."
+            " Customize 'app.json_provider_class' or 'app.json' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        self._json_encoder = value
+
+    @property
+    def json_decoder(
+        self,
+    ) -> t.Union[t.Type[json.JSONDecoder], None]:
+        """Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's.
+
+        .. deprecated:: 2.2
+             Will be removed in Flask 2.3. Customize
+             :attr:`json_provider_class` instead.
+
+        .. versionadded:: 0.10
+        """
+        import warnings
+
+        warnings.warn(
+            "'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
+            " Customize 'app.json_provider_class' or 'app.json' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return self._json_decoder
+
+    @json_decoder.setter
+    def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None:
+        import warnings
+
+        warnings.warn(
+            "'bp.json_decoder' is deprecated and will be removed in Flask 2.3."
+            " Customize 'app.json_provider_class' or 'app.json' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        self._json_decoder = value
 
     def __init__(
         self,
@@ -168,7 +250,7 @@ class Blueprint(Scaffold):
         import_name: str,
         static_folder: t.Optional[t.Union[str, os.PathLike]] = None,
         static_url_path: t.Optional[str] = None,
-        template_folder: t.Optional[str] = None,
+        template_folder: t.Optional[t.Union[str, os.PathLike]] = None,
         url_prefix: t.Optional[str] = None,
         subdomain: t.Optional[str] = None,
         url_defaults: t.Optional[dict] = None,
@@ -198,27 +280,33 @@ class Blueprint(Scaffold):
         self.cli_group = cli_group
         self._blueprints: t.List[t.Tuple["Blueprint", dict]] = []
 
-    def _is_setup_finished(self) -> bool:
-        return self.warn_on_modifications and self._got_registered_once
+    def _check_setup_finished(self, f_name: str) -> None:
+        if self._got_registered_once:
+            import warnings
+
+            warnings.warn(
+                f"The setup method '{f_name}' can no longer be called on"
+                f" the blueprint '{self.name}'. It has already been"
+                " registered at least once, any changes will not be"
+                " applied consistently.\n"
+                "Make sure all imports, decorators, functions, etc."
+                " needed to set up the blueprint are done before"
+                " registering it.\n"
+                "This warning will become an exception in Flask 2.3.",
+                UserWarning,
+                stacklevel=3,
+            )
 
+    @setupmethod
     def record(self, func: t.Callable) -> None:
         """Registers a function that is called when the blueprint is
         registered on the application.  This function is called with the
         state as argument as returned by the :meth:`make_setup_state`
         method.
         """
-        if self._got_registered_once and self.warn_on_modifications:
-            from warnings import warn
-
-            warn(
-                Warning(
-                    "The blueprint was already registered once but is"
-                    " getting modified now. These changes will not show"
-                    " up."
-                )
-            )
         self.deferred_functions.append(func)
 
+    @setupmethod
     def record_once(self, func: t.Callable) -> None:
         """Works like :meth:`record` but wraps the function in another
         function that will ensure the function is only called once.  If the
@@ -230,7 +318,7 @@ class Blueprint(Scaffold):
             if state.first_registration:
                 func(state)
 
-        return self.record(update_wrapper(wrapper, func))
+        self.record(update_wrapper(wrapper, func))
 
     def make_setup_state(
         self, app: "Flask", options: dict, first_registration: bool = False
@@ -241,6 +329,7 @@ class Blueprint(Scaffold):
         """
         return BlueprintSetupState(self, app, options, first_registration)
 
+    @setupmethod
     def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None:
         """Register a :class:`~flask.Blueprint` on this blueprint. Keyword
         arguments passed to this method will override the defaults set
@@ -380,16 +469,20 @@ class Blueprint(Scaffold):
             bp_options["name_prefix"] = name
             blueprint.register(app, bp_options)
 
+    @setupmethod
     def add_url_rule(
         self,
         rule: str,
         endpoint: t.Optional[str] = None,
-        view_func: t.Optional[ft.ViewCallable] = None,
+        view_func: t.Optional[ft.RouteCallable] = None,
         provide_automatic_options: t.Optional[bool] = None,
         **options: t.Any,
     ) -> None:
-        """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
-        the :func:`url_for` function is prefixed with the name of the blueprint.
+        """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for
+        full documentation.
+
+        The URL rule is prefixed with the blueprint's URL prefix. The endpoint name,
+        used with :func:`url_for`, is prefixed with the blueprint's name.
         """
         if endpoint and "." in endpoint:
             raise ValueError("'endpoint' may not contain a dot '.' character.")
@@ -407,28 +500,30 @@ class Blueprint(Scaffold):
             )
         )
 
+    @setupmethod
     def app_template_filter(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[ft.TemplateFilterCallable], ft.TemplateFilterCallable]:
-        """Register a custom template filter, available application wide.  Like
-        :meth:`Flask.template_filter` but for a blueprint.
+    ) -> t.Callable[[T_template_filter], T_template_filter]:
+        """Register a template filter, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_filter`.
 
         :param name: the optional name of the filter, otherwise the
                      function name will be used.
         """
 
-        def decorator(f: ft.TemplateFilterCallable) -> ft.TemplateFilterCallable:
+        def decorator(f: T_template_filter) -> T_template_filter:
             self.add_app_template_filter(f, name=name)
             return f
 
         return decorator
 
+    @setupmethod
     def add_app_template_filter(
         self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None
     ) -> None:
-        """Register a custom template filter, available application wide.  Like
-        :meth:`Flask.add_template_filter` but for a blueprint.  Works exactly
-        like the :meth:`app_template_filter` decorator.
+        """Register a template filter, available in any template rendered by the
+        application. Works like the :meth:`app_template_filter` decorator. Equivalent to
+        :meth:`.Flask.add_template_filter`.
 
         :param name: the optional name of the filter, otherwise the
                      function name will be used.
@@ -439,11 +534,12 @@ class Blueprint(Scaffold):
 
         self.record_once(register_template)
 
+    @setupmethod
     def app_template_test(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[ft.TemplateTestCallable], ft.TemplateTestCallable]:
-        """Register a custom template test, available application wide.  Like
-        :meth:`Flask.template_test` but for a blueprint.
+    ) -> t.Callable[[T_template_test], T_template_test]:
+        """Register a template test, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_test`.
 
         .. versionadded:: 0.10
 
@@ -451,18 +547,19 @@ class Blueprint(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: ft.TemplateTestCallable) -> ft.TemplateTestCallable:
+        def decorator(f: T_template_test) -> T_template_test:
             self.add_app_template_test(f, name=name)
             return f
 
         return decorator
 
+    @setupmethod
     def add_app_template_test(
         self, f: ft.TemplateTestCallable, name: t.Optional[str] = None
     ) -> None:
-        """Register a custom template test, available application wide.  Like
-        :meth:`Flask.add_template_test` but for a blueprint.  Works exactly
-        like the :meth:`app_template_test` decorator.
+        """Register a template test, available in any template rendered by the
+        application. Works like the :meth:`app_template_test` decorator. Equivalent to
+        :meth:`.Flask.add_template_test`.
 
         .. versionadded:: 0.10
 
@@ -475,11 +572,12 @@ class Blueprint(Scaffold):
 
         self.record_once(register_template)
 
+    @setupmethod
     def app_template_global(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[ft.TemplateGlobalCallable], ft.TemplateGlobalCallable]:
-        """Register a custom template global, available application wide.  Like
-        :meth:`Flask.template_global` but for a blueprint.
+    ) -> t.Callable[[T_template_global], T_template_global]:
+        """Register a template global, available in any template rendered by the
+        application. Equivalent to :meth:`.Flask.template_global`.
 
         .. versionadded:: 0.10
 
@@ -487,18 +585,19 @@ class Blueprint(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: ft.TemplateGlobalCallable) -> ft.TemplateGlobalCallable:
+        def decorator(f: T_template_global) -> T_template_global:
             self.add_app_template_global(f, name=name)
             return f
 
         return decorator
 
+    @setupmethod
     def add_app_template_global(
         self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None
     ) -> None:
-        """Register a custom template global, available application wide.  Like
-        :meth:`Flask.add_template_global` but for a blueprint.  Works exactly
-        like the :meth:`app_template_global` decorator.
+        """Register a template global, available in any template rendered by the
+        application. Works like the :meth:`app_template_global` decorator. Equivalent to
+        :meth:`.Flask.add_template_global`.
 
         .. versionadded:: 0.10
 
@@ -511,80 +610,102 @@ class Blueprint(Scaffold):
 
         self.record_once(register_template)
 
-    def before_app_request(
-        self, f: ft.BeforeRequestCallable
-    ) -> ft.BeforeRequestCallable:
-        """Like :meth:`Flask.before_request`.  Such a function is executed
-        before each request, even if outside of a blueprint.
+    @setupmethod
+    def before_app_request(self, f: T_before_request) -> T_before_request:
+        """Like :meth:`before_request`, but before every request, not only those handled
+        by the blueprint. Equivalent to :meth:`.Flask.before_request`.
         """
         self.record_once(
             lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
         )
         return f
 
+    @setupmethod
     def before_app_first_request(
-        self, f: ft.BeforeFirstRequestCallable
-    ) -> ft.BeforeFirstRequestCallable:
-        """Like :meth:`Flask.before_first_request`.  Such a function is
-        executed before the first request to the application.
+        self, f: T_before_first_request
+    ) -> T_before_first_request:
+        """Register a function to run before the first request to the application is
+        handled by the worker. Equivalent to :meth:`.Flask.before_first_request`.
+
+        .. deprecated:: 2.2
+            Will be removed in Flask 2.3. Run setup code when creating
+            the application instead.
         """
+        import warnings
+
+        warnings.warn(
+            "'before_app_first_request' is deprecated and will be"
+            " removed in Flask 2.3. Use 'record_once' instead to run"
+            " setup code when registering the blueprint.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
         self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
         return f
 
-    def after_app_request(self, f: ft.AfterRequestCallable) -> ft.AfterRequestCallable:
-        """Like :meth:`Flask.after_request` but for a blueprint.  Such a function
-        is executed after each request, even if outside of the blueprint.
+    @setupmethod
+    def after_app_request(self, f: T_after_request) -> T_after_request:
+        """Like :meth:`after_request`, but after every request, not only those handled
+        by the blueprint. Equivalent to :meth:`.Flask.after_request`.
         """
         self.record_once(
             lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
         )
         return f
 
-    def teardown_app_request(self, f: ft.TeardownCallable) -> ft.TeardownCallable:
-        """Like :meth:`Flask.teardown_request` but for a blueprint.  Such a
-        function is executed when tearing down each request, even if outside of
-        the blueprint.
+    @setupmethod
+    def teardown_app_request(self, f: T_teardown) -> T_teardown:
+        """Like :meth:`teardown_request`, but after every request, not only those
+        handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`.
         """
         self.record_once(
             lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
         )
         return f
 
+    @setupmethod
     def app_context_processor(
-        self, f: ft.TemplateContextProcessorCallable
-    ) -> ft.TemplateContextProcessorCallable:
-        """Like :meth:`Flask.context_processor` but for a blueprint.  Such a
-        function is executed each request, even if outside of the blueprint.
+        self, f: T_template_context_processor
+    ) -> T_template_context_processor:
+        """Like :meth:`context_processor`, but for templates rendered by every view, not
+        only by the blueprint. Equivalent to :meth:`.Flask.context_processor`.
         """
         self.record_once(
             lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
         )
         return f
 
+    @setupmethod
     def app_errorhandler(
         self, code: t.Union[t.Type[Exception], int]
-    ) -> t.Callable[[ft.ErrorHandlerDecorator], ft.ErrorHandlerDecorator]:
-        """Like :meth:`Flask.errorhandler` but for a blueprint.  This
-        handler is used for all requests, even if outside of the blueprint.
+    ) -> t.Callable[[T_error_handler], T_error_handler]:
+        """Like :meth:`errorhandler`, but for every request, not only those handled by
+        the blueprint. Equivalent to :meth:`.Flask.errorhandler`.
         """
 
-        def decorator(f: ft.ErrorHandlerDecorator) -> ft.ErrorHandlerDecorator:
+        def decorator(f: T_error_handler) -> T_error_handler:
             self.record_once(lambda s: s.app.errorhandler(code)(f))
             return f
 
         return decorator
 
+    @setupmethod
     def app_url_value_preprocessor(
-        self, f: ft.URLValuePreprocessorCallable
-    ) -> ft.URLValuePreprocessorCallable:
-        """Same as :meth:`url_value_preprocessor` but application wide."""
+        self, f: T_url_value_preprocessor
+    ) -> T_url_value_preprocessor:
+        """Like :meth:`url_value_preprocessor`, but for every request, not only those
+        handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`.
+        """
         self.record_once(
             lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
         )
         return f
 
-    def app_url_defaults(self, f: ft.URLDefaultCallable) -> ft.URLDefaultCallable:
-        """Same as :meth:`url_defaults` but application wide."""
+    @setupmethod
+    def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults:
+        """Like :meth:`url_defaults`, but for every request, not only those handled by
+        the blueprint. Equivalent to :meth:`.Flask.url_defaults`.
+        """
         self.record_once(
             lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
         )

+ 241 - 177
contrib/python/Flask/py3/flask/cli.py

@@ -1,3 +1,5 @@
+from __future__ import annotations
+
 import ast
 import inspect
 import os
@@ -5,19 +7,23 @@ import platform
 import re
 import sys
 import traceback
+import typing as t
 from functools import update_wrapper
 from operator import attrgetter
-from threading import Lock
-from threading import Thread
 
 import click
+from click.core import ParameterSource
+from werkzeug import run_simple
+from werkzeug.serving import is_running_from_reloader
 from werkzeug.utils import import_string
 
 from .globals import current_app
 from .helpers import get_debug_flag
-from .helpers import get_env
 from .helpers import get_load_dotenv
 
+if t.TYPE_CHECKING:
+    from .app import Flask
+
 
 class NoAppException(click.UsageError):
     """Raised if an application cannot be found or loaded."""
@@ -44,8 +50,8 @@ def find_best_app(module):
     elif len(matches) > 1:
         raise NoAppException(
             "Detected multiple Flask applications in module"
-            f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
-            f" to specify the correct one."
+            f" '{module.__name__}'. Use '{module.__name__}:name'"
+            " to specify the correct one."
         )
 
     # Search for app factory functions.
@@ -63,15 +69,15 @@ def find_best_app(module):
                     raise
 
                 raise NoAppException(
-                    f"Detected factory {attr_name!r} in module {module.__name__!r},"
+                    f"Detected factory '{attr_name}' in module '{module.__name__}',"
                     " but could not call it without arguments. Use"
-                    f" \"FLASK_APP='{module.__name__}:{attr_name}(args)'\""
+                    f" '{module.__name__}:{attr_name}(args)'"
                     " to specify arguments."
                 ) from e
 
     raise NoAppException(
         "Failed to find Flask application or factory in module"
-        f" {module.__name__!r}. Use 'FLASK_APP={module.__name__}:name'"
+        f" '{module.__name__}'. Use '{module.__name__}:name'"
         " to specify one."
     )
 
@@ -208,8 +214,6 @@ def prepare_import(path):
 
 
 def locate_app(module_name, app_name, raise_if_not_found=True):
-    __traceback_hide__ = True  # noqa: F841
-
     try:
         __import__(module_name)
     except ImportError:
@@ -251,7 +255,7 @@ def get_version(ctx, param, value):
 
 version_option = click.Option(
     ["--version"],
-    help="Show the flask version",
+    help="Show the Flask version.",
     expose_value=False,
     callback=get_version,
     is_flag=True,
@@ -259,74 +263,6 @@ version_option = click.Option(
 )
 
 
-class DispatchingApp:
-    """Special application that dispatches to a Flask application which
-    is imported by name in a background thread.  If an error happens
-    it is recorded and shown as part of the WSGI handling which in case
-    of the Werkzeug debugger means that it shows up in the browser.
-    """
-
-    def __init__(self, loader, use_eager_loading=None):
-        self.loader = loader
-        self._app = None
-        self._lock = Lock()
-        self._bg_loading_exc = None
-
-        if use_eager_loading is None:
-            use_eager_loading = os.environ.get("WERKZEUG_RUN_MAIN") != "true"
-
-        if use_eager_loading:
-            self._load_unlocked()
-        else:
-            self._load_in_background()
-
-    def _load_in_background(self):
-        # Store the Click context and push it in the loader thread so
-        # script_info is still available.
-        ctx = click.get_current_context(silent=True)
-
-        def _load_app():
-            __traceback_hide__ = True  # noqa: F841
-
-            with self._lock:
-                if ctx is not None:
-                    click.globals.push_context(ctx)
-
-                try:
-                    self._load_unlocked()
-                except Exception as e:
-                    self._bg_loading_exc = e
-
-        t = Thread(target=_load_app, args=())
-        t.start()
-
-    def _flush_bg_loading_exception(self):
-        __traceback_hide__ = True  # noqa: F841
-        exc = self._bg_loading_exc
-
-        if exc is not None:
-            self._bg_loading_exc = None
-            raise exc
-
-    def _load_unlocked(self):
-        __traceback_hide__ = True  # noqa: F841
-        self._app = rv = self.loader()
-        self._bg_loading_exc = None
-        return rv
-
-    def __call__(self, environ, start_response):
-        __traceback_hide__ = True  # noqa: F841
-        if self._app is not None:
-            return self._app(environ, start_response)
-        self._flush_bg_loading_exception()
-        with self._lock:
-            if self._app is not None:
-                rv = self._app
-            else:
-                rv = self._load_unlocked()
-            return rv(environ, start_response)
-
-
 class ScriptInfo:
     """Helper object to deal with Flask applications.  This is usually not
     necessary to interface with as it's used internally in the dispatching
@@ -336,25 +272,28 @@ class ScriptInfo:
     onwards as click object.
     """
 
-    def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True):
+    def __init__(
+        self,
+        app_import_path: str | None = None,
+        create_app: t.Callable[..., Flask] | None = None,
+        set_debug_flag: bool = True,
+    ) -> None:
         #: Optionally the import path for the Flask application.
-        self.app_import_path = app_import_path or os.environ.get("FLASK_APP")
+        self.app_import_path = app_import_path
         #: Optionally a function that is passed the script info to create
         #: the instance of the application.
         self.create_app = create_app
         #: A dictionary with arbitrary data that can be associated with
         #: this script info.
-        self.data = {}
+        self.data: t.Dict[t.Any, t.Any] = {}
         self.set_debug_flag = set_debug_flag
-        self._loaded_app = None
+        self._loaded_app: Flask | None = None
 
-    def load_app(self):
+    def load_app(self) -> Flask:
         """Loads the Flask app (if not yet loaded) and returns it.  Calling
         this multiple times will just result in the already loaded app to
         be returned.
         """
-        __traceback_hide__ = True  # noqa: F841
-
         if self._loaded_app is not None:
             return self._loaded_app
 
@@ -377,9 +316,10 @@ class ScriptInfo:
 
         if not app:
             raise NoAppException(
-                "Could not locate a Flask application. You did not provide "
-                'the "FLASK_APP" environment variable, and a "wsgi.py" or '
-                '"app.py" module was not found in the current directory.'
+                "Could not locate a Flask application. Use the"
+                " 'flask --app' option, 'FLASK_APP' environment"
+                " variable, or a 'wsgi.py' or 'app.py' file in the"
+                " current directory."
             )
 
         if self.set_debug_flag:
@@ -396,15 +336,25 @@ pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True)
 
 def with_appcontext(f):
     """Wraps a callback so that it's guaranteed to be executed with the
-    script's application context.  If callbacks are registered directly
-    to the ``app.cli`` object then they are wrapped with this function
-    by default unless it's disabled.
+    script's application context.
+
+    Custom commands (and their options) registered under ``app.cli`` or
+    ``blueprint.cli`` will always have an app context available, this
+    decorator is not required in that case.
+
+    .. versionchanged:: 2.2
+        The app context is active for subcommands as well as the
+        decorated callback. The app context is always available to
+        ``app.cli`` command and parameter callbacks.
     """
 
     @click.pass_context
     def decorator(__ctx, *args, **kwargs):
-        with __ctx.ensure_object(ScriptInfo).load_app().app_context():
-            return __ctx.invoke(f, *args, **kwargs)
+        if not current_app:
+            app = __ctx.ensure_object(ScriptInfo).load_app()
+            __ctx.with_resource(app.app_context())
+
+        return __ctx.invoke(f, *args, **kwargs)
 
     return update_wrapper(decorator, f)
 
@@ -440,6 +390,94 @@ class AppGroup(click.Group):
         return click.Group.group(self, *args, **kwargs)
 
 
+def _set_app(ctx: click.Context, param: click.Option, value: str | None) -> str | None:
+    if value is None:
+        return None
+
+    info = ctx.ensure_object(ScriptInfo)
+    info.app_import_path = value
+    return value
+
+
+# This option is eager so the app will be available if --help is given.
+# --help is also eager, so --app must be before it in the param list.
+# no_args_is_help bypasses eager processing, so this option must be
+# processed manually in that case to ensure FLASK_APP gets picked up.
+_app_option = click.Option(
+    ["-A", "--app"],
+    metavar="IMPORT",
+    help=(
+        "The Flask application or factory function to load, in the form 'module:name'."
+        " Module can be a dotted import or file path. Name is not required if it is"
+        " 'app', 'application', 'create_app', or 'make_app', and can be 'name(args)' to"
+        " pass arguments."
+    ),
+    is_eager=True,
+    expose_value=False,
+    callback=_set_app,
+)
+
+
+def _set_debug(ctx: click.Context, param: click.Option, value: bool) -> bool | None:
+    # If the flag isn't provided, it will default to False. Don't use
+    # that, let debug be set by env in that case.
+    source = ctx.get_parameter_source(param.name)  # type: ignore[arg-type]
+
+    if source is not None and source in (
+        ParameterSource.DEFAULT,
+        ParameterSource.DEFAULT_MAP,
+    ):
+        return None
+
+    # Set with env var instead of ScriptInfo.load so that it can be
+    # accessed early during a factory function.
+    os.environ["FLASK_DEBUG"] = "1" if value else "0"
+    return value
+
+
+_debug_option = click.Option(
+    ["--debug/--no-debug"],
+    help="Set debug mode.",
+    expose_value=False,
+    callback=_set_debug,
+)
+
+
+def _env_file_callback(
+    ctx: click.Context, param: click.Option, value: str | None
+) -> str | None:
+    if value is None:
+        return None
+
+    import importlib
+
+    try:
+        importlib.import_module("dotenv")
+    except ImportError:
+        raise click.BadParameter(
+            "python-dotenv must be installed to load an env file.",
+            ctx=ctx,
+            param=param,
+        ) from None
+
+    # Don't check FLASK_SKIP_DOTENV, that only disables automatically
+    # loading .env and .flaskenv files.
+    load_dotenv(value)
+    return value
+
+
+# This option is eager so env vars are loaded as early as possible to be
+# used by other options.
+_env_file_option = click.Option(
+    ["-e", "--env-file"],
+    type=click.Path(exists=True, dir_okay=False),
+    help="Load environment variables from this file. python-dotenv must be installed.",
+    is_eager=True,
+    expose_value=False,
+    callback=_env_file_callback,
+)
+
+
 class FlaskGroup(AppGroup):
     """Special subclass of the :class:`AppGroup` group that supports
     loading more commands from the configured Flask app.  Normally a
@@ -455,8 +493,14 @@ class FlaskGroup(AppGroup):
     :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`
         files to set environment variables. Will also change the working
         directory to the directory containing the first file found.
-    :param set_debug_flag: Set the app's debug flag based on the active
-        environment
+    :param set_debug_flag: Set the app's debug flag.
+
+    .. versionchanged:: 2.2
+        Added the ``-A/--app``, ``--debug/--no-debug``, ``-e/--env-file`` options.
+
+    .. versionchanged:: 2.2
+        An app context is pushed when running ``app.cli`` commands, so
+        ``@with_appcontext`` is no longer required for those commands.
 
     .. versionchanged:: 1.0
         If installed, python-dotenv will be used to load environment variables
@@ -465,19 +509,30 @@ class FlaskGroup(AppGroup):
 
     def __init__(
         self,
-        add_default_commands=True,
-        create_app=None,
-        add_version_option=True,
-        load_dotenv=True,
-        set_debug_flag=True,
-        **extra,
-    ):
+        add_default_commands: bool = True,
+        create_app: t.Callable[..., Flask] | None = None,
+        add_version_option: bool = True,
+        load_dotenv: bool = True,
+        set_debug_flag: bool = True,
+        **extra: t.Any,
+    ) -> None:
         params = list(extra.pop("params", None) or ())
+        # Processing is done with option callbacks instead of a group
+        # callback. This allows users to make a custom group callback
+        # without losing the behavior. --env-file must come first so
+        # that it is eagerly evaluated before --app.
+        params.extend((_env_file_option, _app_option, _debug_option))
 
         if add_version_option:
             params.append(version_option)
 
-        AppGroup.__init__(self, params=params, **extra)
+        if "context_settings" not in extra:
+            extra["context_settings"] = {}
+
+        extra["context_settings"].setdefault("auto_envvar_prefix", "FLASK")
+
+        super().__init__(params=params, **extra)
+
         self.create_app = create_app
         self.load_dotenv = load_dotenv
         self.set_debug_flag = set_debug_flag
@@ -520,9 +575,18 @@ class FlaskGroup(AppGroup):
         # Look up commands provided by the app, showing an error and
         # continuing if the app couldn't be loaded.
         try:
-            return info.load_app().cli.get_command(ctx, name)
+            app = info.load_app()
         except NoAppException as e:
             click.secho(f"Error: {e.format_message()}\n", err=True, fg="red")
+            return None
+
+        # Push an app context for the loaded app unless it is already
+        # active somehow. This makes the context available to parameter
+        # and command callbacks without needing @with_appcontext.
+        if not current_app or current_app._get_current_object() is not app:
+            ctx.with_resource(app.app_context())
+
+        return app.cli.get_command(ctx, name)
 
     def list_commands(self, ctx):
         self._load_plugin_commands()
@@ -545,26 +609,39 @@ class FlaskGroup(AppGroup):
 
         return sorted(rv)
 
-    def main(self, *args, **kwargs):
-        # Set a global flag that indicates that we were invoked from the
-        # command line interface. This is detected by Flask.run to make the
-        # call into a no-op. This is necessary to avoid ugly errors when the
-        # script that is loaded here also attempts to start a server.
+    def make_context(
+        self,
+        info_name: str | None,
+        args: list[str],
+        parent: click.Context | None = None,
+        **extra: t.Any,
+    ) -> click.Context:
+        # Set a flag to tell app.run to become a no-op. If app.run was
+        # not in a __name__ == __main__ guard, it would start the server
+        # when importing, blocking whatever command is being called.
         os.environ["FLASK_RUN_FROM_CLI"] = "true"
 
+        # Attempt to load .env and .flask env files. The --env-file
+        # option can cause another file to be loaded.
         if get_load_dotenv(self.load_dotenv):
             load_dotenv()
 
-        obj = kwargs.get("obj")
-
-        if obj is None:
-            obj = ScriptInfo(
+        if "obj" not in extra and "obj" not in self.context_settings:
+            extra["obj"] = ScriptInfo(
                 create_app=self.create_app, set_debug_flag=self.set_debug_flag
             )
 
-        kwargs["obj"] = obj
-        kwargs.setdefault("auto_envvar_prefix", "FLASK")
-        return super().main(*args, **kwargs)
+        return super().make_context(info_name, args, parent=parent, **extra)
+
+    def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
+        if not args and self.no_args_is_help:
+            # Attempt to load --env-file and --app early in case they
+            # were given as env vars. Otherwise no_args_is_help will not
+            # see commands from app.cli.
+            _env_file_option.handle_parse_result(ctx, {}, [])
+            _app_option.handle_parse_result(ctx, {}, [])
+
+        return super().parse_args(ctx, args)
 
 
 def _path_is_ancestor(path, other):
@@ -574,7 +651,7 @@ def _path_is_ancestor(path, other):
     return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other
 
 
-def load_dotenv(path=None):
+def load_dotenv(path: str | os.PathLike | None = None) -> bool:
     """Load "dotenv" files in order of precedence to set environment variables.
 
     If an env var is already set it is not overwritten, so earlier files in the
@@ -587,13 +664,17 @@ def load_dotenv(path=None):
     :param path: Load the file at this location instead of searching.
     :return: ``True`` if a file was loaded.
 
-    .. versionchanged:: 1.1.0
-        Returns ``False`` when python-dotenv is not installed, or when
-        the given path isn't a file.
+    .. versionchanged:: 2.0
+        The current directory is not changed to the location of the
+        loaded file.
 
     .. versionchanged:: 2.0
         When loading the env files, set the default encoding to UTF-8.
 
+    .. versionchanged:: 1.1.0
+        Returns ``False`` when python-dotenv is not installed, or when
+        the given path isn't a file.
+
     .. versionadded:: 1.0
     """
     try:
@@ -609,15 +690,15 @@ def load_dotenv(path=None):
 
         return False
 
-    # if the given path specifies the actual file then return True,
-    # else False
+    # Always return after attempting to load a given path, don't load
+    # the default files.
     if path is not None:
         if os.path.isfile(path):
             return dotenv.load_dotenv(path, encoding="utf-8")
 
         return False
 
-    new_dir = None
+    loaded = False
 
     for name in (".env", ".flaskenv"):
         path = dotenv.find_dotenv(name, usecwd=True)
@@ -625,38 +706,21 @@ def load_dotenv(path=None):
         if not path:
             continue
 
-        if new_dir is None:
-            new_dir = os.path.dirname(path)
-
         dotenv.load_dotenv(path, encoding="utf-8")
+        loaded = True
 
-    return new_dir is not None  # at least one file was located and loaded
+    return loaded  # True if at least one file was located and loaded.
 
 
-def show_server_banner(env, debug, app_import_path, eager_loading):
+def show_server_banner(debug, app_import_path):
     """Show extra startup messages the first time the server is run,
     ignoring the reloader.
     """
-    if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
+    if is_running_from_reloader():
         return
 
     if app_import_path is not None:
-        message = f" * Serving Flask app {app_import_path!r}"
-
-        if not eager_loading:
-            message += " (lazy loading)"
-
-        click.echo(message)
-
-    click.echo(f" * Environment: {env}")
-
-    if env == "production":
-        click.secho(
-            "   WARNING: This is a development server. Do not use it in"
-            " a production deployment.",
-            fg="red",
-        )
-        click.secho("   Use a production WSGI server instead.", dim=True)
+        click.echo(f" * Serving Flask app '{app_import_path}'")
 
     if debug is not None:
         click.echo(f" * Debug mode: {'on' if debug else 'off'}")
@@ -785,12 +849,6 @@ class SeparatedPathType(click.Path):
     help="Enable or disable the debugger. By default the debugger "
     "is active if debug is enabled.",
 )
-@click.option(
-    "--eager-loading/--lazy-loading",
-    default=None,
-    help="Enable or disable eager loading. By default eager "
-    "loading is enabled if the reloader is disabled.",
-)
 @click.option(
     "--with-threads/--without-threads",
     default=True,
@@ -822,7 +880,6 @@ def run_command(
     port,
     reload,
     debugger,
-    eager_loading,
     with_threads,
     cert,
     extra_files,
@@ -833,9 +890,26 @@ def run_command(
     This server is for development purposes only. It does not provide
     the stability, security, or performance of production WSGI servers.
 
-    The reloader and debugger are enabled by default if
-    FLASK_ENV=development or FLASK_DEBUG=1.
+    The reloader and debugger are enabled by default with the '--debug'
+    option.
     """
+    try:
+        app = info.load_app()
+    except Exception as e:
+        if is_running_from_reloader():
+            # When reloading, print out the error immediately, but raise
+            # it later so the debugger or server can handle it.
+            traceback.print_exc()
+            err = e
+
+            def app(environ, start_response):
+                raise err from None
+
+        else:
+            # When not reloading, raise the error immediately so the
+            # command fails.
+            raise e from None
+
     debug = get_debug_flag()
 
     if reload is None:
@@ -844,10 +918,7 @@ def run_command(
     if debugger is None:
         debugger = debug
 
-    show_server_banner(get_env(), debug, info.app_import_path, eager_loading)
-    app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
-
-    from werkzeug.serving import run_simple
+    show_server_banner(debug, info.app_import_path)
 
     run_simple(
         host,
@@ -862,6 +933,9 @@ def run_command(
     )
 
 
+run_command.params.insert(0, _debug_option)
+
+
 @click.command("shell", short_help="Run a shell in the app context.")
 @with_appcontext
 def shell_command() -> None:
@@ -873,13 +947,11 @@ def shell_command() -> None:
     without having to manually configure the application.
     """
     import code
-    from .globals import _app_ctx_stack
 
-    app = _app_ctx_stack.top.app
     banner = (
         f"Python {sys.version} on {sys.platform}\n"
-        f"App: {app.import_name} [{app.env}]\n"
-        f"Instance: {app.instance_path}"
+        f"App: {current_app.import_name}\n"
+        f"Instance: {current_app.instance_path}"
     )
     ctx: dict = {}
 
@@ -890,7 +962,7 @@ def shell_command() -> None:
         with open(startup) as f:
             eval(compile(f.read(), startup, "exec"), ctx)
 
-    ctx.update(app.make_shell_context())
+    ctx.update(current_app.make_shell_context())
 
     # Site, customize, or startup script can set a hook to call when
     # entering interactive mode. The default one sets up readline with
@@ -963,22 +1035,14 @@ def routes_command(sort: str, all_methods: bool) -> None:
 
 
 cli = FlaskGroup(
+    name="flask",
     help="""\
 A general utility script for Flask applications.
 
-Provides commands from Flask, extensions, and the application. Loads the
-application defined in the FLASK_APP environment variable, or from a wsgi.py
-file. Setting the FLASK_ENV environment variable to 'development' will enable
-debug mode.
-
-\b
-  {prefix}{cmd} FLASK_APP=hello.py
-  {prefix}{cmd} FLASK_ENV=development
-  {prefix}flask run
-""".format(
-        cmd="export" if os.name == "posix" else "set",
-        prefix="$ " if os.name == "posix" else "> ",
-    )
+An application to load must be given with the '--app' option,
+'FLASK_APP' environment variable, or with a 'wsgi.py' or 'app.py' file
+in the current directory.
+""",
 )
 
 

+ 3 - 2
contrib/python/Flask/py3/flask/config.py

@@ -275,8 +275,9 @@ class Config(dict):
     def from_mapping(
         self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
     ) -> bool:
-        """Updates the config like :meth:`update` ignoring items with non-upper
-        keys.
+        """Updates the config like :meth:`update` ignoring items with
+        non-upper keys.
+
         :return: Always returns ``True``.
 
         .. versionadded:: 0.11

+ 68 - 143
contrib/python/Flask/py3/flask/ctx.py

@@ -1,3 +1,4 @@
+import contextvars
 import sys
 import typing as t
 from functools import update_wrapper
@@ -6,12 +7,12 @@ from types import TracebackType
 from werkzeug.exceptions import HTTPException
 
 from . import typing as ft
-from .globals import _app_ctx_stack
-from .globals import _request_ctx_stack
+from .globals import _cv_app
+from .globals import _cv_request
 from .signals import appcontext_popped
 from .signals import appcontext_pushed
 
-if t.TYPE_CHECKING:
+if t.TYPE_CHECKING:  # pragma: no cover
     from .app import Flask
     from .sessions import SessionMixin
     from .wrappers import Request
@@ -103,9 +104,9 @@ class _AppCtxGlobals:
         return iter(self.__dict__)
 
     def __repr__(self) -> str:
-        top = _app_ctx_stack.top
-        if top is not None:
-            return f"<flask.g of {top.app.name!r}>"
+        ctx = _cv_app.get(None)
+        if ctx is not None:
+            return f"<flask.g of '{ctx.app.name}'>"
         return object.__repr__(self)
 
 
@@ -130,15 +131,15 @@ def after_this_request(f: ft.AfterRequestCallable) -> ft.AfterRequestCallable:
 
     .. versionadded:: 0.9
     """
-    top = _request_ctx_stack.top
+    ctx = _cv_request.get(None)
 
-    if top is None:
+    if ctx is None:
         raise RuntimeError(
-            "This decorator can only be used when a request context is"
-            " active, such as within a view function."
+            "'after_this_request' can only be used when a request"
+            " context is active, such as in a view function."
         )
 
-    top._after_request_functions.append(f)
+    ctx._after_request_functions.append(f)
     return f
 
 
@@ -166,19 +167,19 @@ def copy_current_request_context(f: t.Callable) -> t.Callable:
 
     .. versionadded:: 0.10
     """
-    top = _request_ctx_stack.top
+    ctx = _cv_request.get(None)
 
-    if top is None:
+    if ctx is None:
         raise RuntimeError(
-            "This decorator can only be used when a request context is"
-            " active, such as within a view function."
+            "'copy_current_request_context' can only be used when a"
+            " request context is active, such as in a view function."
         )
 
-    reqctx = top.copy()
+    ctx = ctx.copy()
 
     def wrapper(*args, **kwargs):
-        with reqctx:
-            return reqctx.app.ensure_sync(f)(*args, **kwargs)
+        with ctx:
+            return ctx.app.ensure_sync(f)(*args, **kwargs)
 
     return update_wrapper(wrapper, f)
 
@@ -212,7 +213,7 @@ def has_request_context() -> bool:
 
     .. versionadded:: 0.7
     """
-    return _request_ctx_stack.top is not None
+    return _cv_request.get(None) is not None
 
 
 def has_app_context() -> bool:
@@ -222,44 +223,43 @@ def has_app_context() -> bool:
 
     .. versionadded:: 0.9
     """
-    return _app_ctx_stack.top is not None
+    return _cv_app.get(None) is not None
 
 
 class AppContext:
-    """The application context binds an application object implicitly
-    to the current thread or greenlet, similar to how the
-    :class:`RequestContext` binds request information.  The application
-    context is also implicitly created if a request context is created
-    but the application is not on top of the individual application
-    context.
+    """The app context contains application-specific information. An app
+    context is created and pushed at the beginning of each request if
+    one is not already active. An app context is also pushed when
+    running CLI commands.
     """
 
     def __init__(self, app: "Flask") -> None:
         self.app = app
         self.url_adapter = app.create_url_adapter(None)
-        self.g = app.app_ctx_globals_class()
-
-        # Like request context, app contexts can be pushed multiple times
-        # but there a basic "refcount" is enough to track them.
-        self._refcnt = 0
+        self.g: _AppCtxGlobals = app.app_ctx_globals_class()
+        self._cv_tokens: t.List[contextvars.Token] = []
 
     def push(self) -> None:
         """Binds the app context to the current context."""
-        self._refcnt += 1
-        _app_ctx_stack.push(self)
+        self._cv_tokens.append(_cv_app.set(self))
         appcontext_pushed.send(self.app)
 
     def pop(self, exc: t.Optional[BaseException] = _sentinel) -> None:  # type: ignore
         """Pops the app context."""
         try:
-            self._refcnt -= 1
-            if self._refcnt <= 0:
+            if len(self._cv_tokens) == 1:
                 if exc is _sentinel:
                     exc = sys.exc_info()[1]
                 self.app.do_teardown_appcontext(exc)
         finally:
-            rv = _app_ctx_stack.pop()
-        assert rv is self, f"Popped wrong app context.  ({rv!r} instead of {self!r})"
+            ctx = _cv_app.get()
+            _cv_app.reset(self._cv_tokens.pop())
+
+        if ctx is not self:
+            raise AssertionError(
+                f"Popped wrong app context. ({ctx!r} instead of {self!r})"
+            )
+
         appcontext_popped.send(self.app)
 
     def __enter__(self) -> "AppContext":
@@ -276,10 +276,10 @@ class AppContext:
 
 
 class RequestContext:
-    """The request context contains all request relevant information.  It is
-    created at the beginning of the request and pushed to the
-    `_request_ctx_stack` and removed at the end of it.  It will create the
-    URL adapter and request object for the WSGI environment provided.
+    """The request context contains per-request information. The Flask
+    app creates and pushes it at the beginning of the request, then pops
+    it at the end of the request. It will create the URL adapter and
+    request object for the WSGI environment provided.
 
     Do not attempt to use this class directly, instead use
     :meth:`~flask.Flask.test_request_context` and
@@ -289,20 +289,12 @@ class RequestContext:
     functions registered on the application for teardown execution
     (:meth:`~flask.Flask.teardown_request`).
 
-    The request context is automatically popped at the end of the request
-    for you.  In debug mode the request context is kept around if
-    exceptions happen so that interactive debuggers have a chance to
-    introspect the data.  With 0.4 this can also be forced for requests
-    that did not fail and outside of ``DEBUG`` mode.  By setting
-    ``'flask._preserve_context'`` to ``True`` on the WSGI environment the
-    context will not pop itself at the end of the request.  This is used by
-    the :meth:`~flask.Flask.test_client` for example to implement the
-    deferred cleanup functionality.
-
-    You might find this helpful for unittests where you need the
-    information from the context local around for a little longer.  Make
-    sure to properly :meth:`~werkzeug.LocalStack.pop` the stack yourself in
-    that situation, otherwise your unittests will leak memory.
+    The request context is automatically popped at the end of the
+    request. When using the interactive debugger, the context will be
+    restored so ``request`` is still accessible. Similarly, the test
+    client can preserve the context after the request ends. However,
+    teardown functions may already have closed some resources such as
+    database connections.
     """
 
     def __init__(
@@ -315,59 +307,21 @@ class RequestContext:
         self.app = app
         if request is None:
             request = app.request_class(environ)
-        self.request = request
+            request.json_module = app.json
+        self.request: Request = request
         self.url_adapter = None
         try:
             self.url_adapter = app.create_url_adapter(self.request)
         except HTTPException as e:
             self.request.routing_exception = e
-        self.flashes = None
-        self.session = session
-
-        # Request contexts can be pushed multiple times and interleaved with
-        # other request contexts.  Now only if the last level is popped we
-        # get rid of them.  Additionally if an application context is missing
-        # one is created implicitly so for each level we add this information
-        self._implicit_app_ctx_stack: t.List[t.Optional["AppContext"]] = []
-
-        # indicator if the context was preserved.  Next time another context
-        # is pushed the preserved context is popped.
-        self.preserved = False
-
-        # remembers the exception for pop if there is one in case the context
-        # preservation kicks in.
-        self._preserved_exc = None
-
+        self.flashes: t.Optional[t.List[t.Tuple[str, str]]] = None
+        self.session: t.Optional["SessionMixin"] = session
         # Functions that should be executed after the request on the response
         # object.  These will be called before the regular "after_request"
         # functions.
         self._after_request_functions: t.List[ft.AfterRequestCallable] = []
 
-    @property
-    def g(self) -> _AppCtxGlobals:
-        import warnings
-
-        warnings.warn(
-            "Accessing 'g' on the request context is deprecated and"
-            " will be removed in Flask 2.2. Access `g` directly or from"
-            "the application context instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return _app_ctx_stack.top.g
-
-    @g.setter
-    def g(self, value: _AppCtxGlobals) -> None:
-        import warnings
-
-        warnings.warn(
-            "Setting 'g' on the request context is deprecated and"
-            " will be removed in Flask 2.2. Set it on the application"
-            " context instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        _app_ctx_stack.top.g = value
+        self._cv_tokens: t.List[t.Tuple[contextvars.Token, t.Optional[AppContext]]] = []
 
     def copy(self) -> "RequestContext":
         """Creates a copy of this request context with the same request object.
@@ -400,30 +354,17 @@ class RequestContext:
             self.request.routing_exception = e
 
     def push(self) -> None:
-        """Binds the request context to the current context."""
-        # If an exception occurs in debug mode or if context preservation is
-        # activated under exception situations exactly one context stays
-        # on the stack.  The rationale is that you want to access that
-        # information under debug situations.  However if someone forgets to
-        # pop that context again we want to make sure that on the next push
-        # it's invalidated, otherwise we run at risk that something leaks
-        # memory.  This is usually only a problem in test suite since this
-        # functionality is not active in production environments.
-        top = _request_ctx_stack.top
-        if top is not None and top.preserved:
-            top.pop(top._preserved_exc)
-
         # Before we push the request context we have to ensure that there
         # is an application context.
-        app_ctx = _app_ctx_stack.top
-        if app_ctx is None or app_ctx.app != self.app:
+        app_ctx = _cv_app.get(None)
+
+        if app_ctx is None or app_ctx.app is not self.app:
             app_ctx = self.app.app_context()
             app_ctx.push()
-            self._implicit_app_ctx_stack.append(app_ctx)
         else:
-            self._implicit_app_ctx_stack.append(None)
+            app_ctx = None
 
-        _request_ctx_stack.push(self)
+        self._cv_tokens.append((_cv_request.set(self), app_ctx))
 
         # Open the session at the moment that the request context is available.
         # This allows a custom open_session method to use the request context.
@@ -449,13 +390,10 @@ class RequestContext:
         .. versionchanged:: 0.9
            Added the `exc` argument.
         """
-        app_ctx = self._implicit_app_ctx_stack.pop()
-        clear_request = False
+        clear_request = len(self._cv_tokens) == 1
 
         try:
-            if not self._implicit_app_ctx_stack:
-                self.preserved = False
-                self._preserved_exc = None
+            if clear_request:
                 if exc is _sentinel:
                     exc = sys.exc_info()[1]
                 self.app.do_teardown_request(exc)
@@ -463,31 +401,23 @@ class RequestContext:
                 request_close = getattr(self.request, "close", None)
                 if request_close is not None:
                     request_close()
-                clear_request = True
         finally:
-            rv = _request_ctx_stack.pop()
+            ctx = _cv_request.get()
+            token, app_ctx = self._cv_tokens.pop()
+            _cv_request.reset(token)
 
             # get rid of circular dependencies at the end of the request
             # so that we don't require the GC to be active.
             if clear_request:
-                rv.request.environ["werkzeug.request"] = None
+                ctx.request.environ["werkzeug.request"] = None
 
-            # Get rid of the app as well if necessary.
             if app_ctx is not None:
                 app_ctx.pop(exc)
 
-            assert (
-                rv is self
-            ), f"Popped wrong request context. ({rv!r} instead of {self!r})"
-
-    def auto_pop(self, exc: t.Optional[BaseException]) -> None:
-        if self.request.environ.get("flask._preserve_context") or (
-            exc is not None and self.app.preserve_context_on_exception
-        ):
-            self.preserved = True
-            self._preserved_exc = exc  # type: ignore
-        else:
-            self.pop(exc)
+            if ctx is not self:
+                raise AssertionError(
+                    f"Popped wrong request context. ({ctx!r} instead of {self!r})"
+                )
 
     def __enter__(self) -> "RequestContext":
         self.push()
@@ -499,12 +429,7 @@ class RequestContext:
         exc_value: t.Optional[BaseException],
         tb: t.Optional[TracebackType],
     ) -> None:
-        # do not pop the request stack if we are in debug mode and an
-        # exception happened.  This will allow the debugger to still
-        # access the request object in the interactive shell.  Furthermore
-        # the context can be force kept alive for the test client.
-        # See flask.testing for how this works.
-        self.auto_pop(exc_value)
+        self.pop(exc_value)
 
     def __repr__(self) -> str:
         return (

+ 3 - 19
contrib/python/Flask/py3/flask/debughelpers.py

@@ -1,10 +1,8 @@
-import os
 import typing as t
-from warnings import warn
 
 from .app import Flask
 from .blueprints import Blueprint
-from .globals import _request_ctx_stack
+from .globals import request_ctx
 
 
 class UnexpectedUnicodeError(AssertionError, UnicodeError):
@@ -118,9 +116,8 @@ def explain_template_loading_attempts(app: Flask, template, attempts) -> None:
     info = [f"Locating template {template!r}:"]
     total_found = 0
     blueprint = None
-    reqctx = _request_ctx_stack.top
-    if reqctx is not None and reqctx.request.blueprint is not None:
-        blueprint = reqctx.request.blueprint
+    if request_ctx and request_ctx.request.blueprint is not None:
+        blueprint = request_ctx.request.blueprint
 
     for idx, (loader, srcobj, triple) in enumerate(attempts):
         if isinstance(srcobj, Flask):
@@ -159,16 +156,3 @@ def explain_template_loading_attempts(app: Flask, template, attempts) -> None:
         info.append("  See https://flask.palletsprojects.com/blueprints/#templates")
 
     app.logger.info("\n".join(info))
-
-
-def explain_ignored_app_run() -> None:
-    if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
-        warn(
-            Warning(
-                "Silently ignoring app.run() because the application is"
-                " run from the flask command line executable. Consider"
-                ' putting app.run() behind an if __name__ == "__main__"'
-                " guard to silence this warning."
-            ),
-            stacklevel=3,
-        )

+ 85 - 37
contrib/python/Flask/py3/flask/globals.py

@@ -1,59 +1,107 @@
 import typing as t
-from functools import partial
+from contextvars import ContextVar
 
 from werkzeug.local import LocalProxy
-from werkzeug.local import LocalStack
 
-if t.TYPE_CHECKING:
+if t.TYPE_CHECKING:  # pragma: no cover
     from .app import Flask
     from .ctx import _AppCtxGlobals
+    from .ctx import AppContext
+    from .ctx import RequestContext
     from .sessions import SessionMixin
     from .wrappers import Request
 
-_request_ctx_err_msg = """\
-Working outside of request context.
 
-This typically means that you attempted to use functionality that needed
-an active HTTP request.  Consult the documentation on testing for
-information about how to avoid this problem.\
-"""
-_app_ctx_err_msg = """\
+class _FakeStack:
+    def __init__(self, name: str, cv: ContextVar[t.Any]) -> None:
+        self.name = name
+        self.cv = cv
+
+    def _warn(self):
+        import warnings
+
+        warnings.warn(
+            f"'_{self.name}_ctx_stack' is deprecated and will be"
+            " removed in Flask 2.3. Use 'g' to store data, or"
+            f" '{self.name}_ctx' to access the current context.",
+            DeprecationWarning,
+            stacklevel=3,
+        )
+
+    def push(self, obj: t.Any) -> None:
+        self._warn()
+        self.cv.set(obj)
+
+    def pop(self) -> t.Any:
+        self._warn()
+        ctx = self.cv.get(None)
+        self.cv.set(None)
+        return ctx
+
+    @property
+    def top(self) -> t.Optional[t.Any]:
+        self._warn()
+        return self.cv.get(None)
+
+
+_no_app_msg = """\
 Working outside of application context.
 
 This typically means that you attempted to use functionality that needed
-to interface with the current application object in some way. To solve
-this, set up an application context with app.app_context().  See the
-documentation for more information.\
+the current application. To solve this, set up an application context
+with app.app_context(). See the documentation for more information.\
 """
+_cv_app: ContextVar["AppContext"] = ContextVar("flask.app_ctx")
+__app_ctx_stack = _FakeStack("app", _cv_app)
+app_ctx: "AppContext" = LocalProxy(  # type: ignore[assignment]
+    _cv_app, unbound_message=_no_app_msg
+)
+current_app: "Flask" = LocalProxy(  # type: ignore[assignment]
+    _cv_app, "app", unbound_message=_no_app_msg
+)
+g: "_AppCtxGlobals" = LocalProxy(  # type: ignore[assignment]
+    _cv_app, "g", unbound_message=_no_app_msg
+)
 
+_no_req_msg = """\
+Working outside of request context.
 
-def _lookup_req_object(name):
-    top = _request_ctx_stack.top
-    if top is None:
-        raise RuntimeError(_request_ctx_err_msg)
-    return getattr(top, name)
+This typically means that you attempted to use functionality that needed
+an active HTTP request. Consult the documentation on testing for
+information about how to avoid this problem.\
+"""
+_cv_request: ContextVar["RequestContext"] = ContextVar("flask.request_ctx")
+__request_ctx_stack = _FakeStack("request", _cv_request)
+request_ctx: "RequestContext" = LocalProxy(  # type: ignore[assignment]
+    _cv_request, unbound_message=_no_req_msg
+)
+request: "Request" = LocalProxy(  # type: ignore[assignment]
+    _cv_request, "request", unbound_message=_no_req_msg
+)
+session: "SessionMixin" = LocalProxy(  # type: ignore[assignment]
+    _cv_request, "session", unbound_message=_no_req_msg
+)
 
 
-def _lookup_app_object(name):
-    top = _app_ctx_stack.top
-    if top is None:
-        raise RuntimeError(_app_ctx_err_msg)
-    return getattr(top, name)
+def __getattr__(name: str) -> t.Any:
+    if name == "_app_ctx_stack":
+        import warnings
 
+        warnings.warn(
+            "'_app_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return __app_ctx_stack
 
-def _find_app():
-    top = _app_ctx_stack.top
-    if top is None:
-        raise RuntimeError(_app_ctx_err_msg)
-    return top.app
+    if name == "_request_ctx_stack":
+        import warnings
 
+        warnings.warn(
+            "'_request_ctx_stack' is deprecated and will be removed in Flask 2.3.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return __request_ctx_stack
 
-# context locals
-_request_ctx_stack = LocalStack()
-_app_ctx_stack = LocalStack()
-current_app: "Flask" = LocalProxy(_find_app)  # type: ignore
-request: "Request" = LocalProxy(partial(_lookup_req_object, "request"))  # type: ignore
-session: "SessionMixin" = LocalProxy(  # type: ignore
-    partial(_lookup_req_object, "session")
-)
-g: "_AppCtxGlobals" = LocalProxy(partial(_lookup_app_object, "g"))  # type: ignore
+    raise AttributeError(name)

+ 139 - 225
contrib/python/Flask/py3/flask/helpers.py

@@ -3,53 +3,73 @@ import pkgutil
 import socket
 import sys
 import typing as t
-import warnings
 from datetime import datetime
 from functools import lru_cache
 from functools import update_wrapper
 from threading import RLock
 
 import werkzeug.utils
-from werkzeug.routing import BuildError
-from werkzeug.urls import url_quote
+from werkzeug.exceptions import abort as _wz_abort
+from werkzeug.utils import redirect as _wz_redirect
 
-from .globals import _app_ctx_stack
-from .globals import _request_ctx_stack
+from .globals import _cv_request
 from .globals import current_app
 from .globals import request
+from .globals import request_ctx
 from .globals import session
 from .signals import message_flashed
 
-if t.TYPE_CHECKING:
+if t.TYPE_CHECKING:  # pragma: no cover
+    from werkzeug.wrappers import Response as BaseResponse
     from .wrappers import Response
+    import typing_extensions as te
 
 
 def get_env() -> str:
     """Get the environment the app is running in, indicated by the
     :envvar:`FLASK_ENV` environment variable. The default is
     ``'production'``.
+
+    .. deprecated:: 2.2
+        Will be removed in Flask 2.3.
     """
+    import warnings
+
+    warnings.warn(
+        "'FLASK_ENV' and 'get_env' are deprecated and will be removed"
+        " in Flask 2.3. Use 'FLASK_DEBUG' instead.",
+        DeprecationWarning,
+        stacklevel=2,
+    )
     return os.environ.get("FLASK_ENV") or "production"
 
 
 def get_debug_flag() -> bool:
-    """Get whether debug mode should be enabled for the app, indicated
-    by the :envvar:`FLASK_DEBUG` environment variable. The default is
-    ``True`` if :func:`.get_env` returns ``'development'``, or ``False``
-    otherwise.
+    """Get whether debug mode should be enabled for the app, indicated by the
+    :envvar:`FLASK_DEBUG` environment variable. The default is ``False``.
     """
     val = os.environ.get("FLASK_DEBUG")
 
     if not val:
-        return get_env() == "development"
+        env = os.environ.get("FLASK_ENV")
+
+        if env is not None:
+            print(
+                "'FLASK_ENV' is deprecated and will not be used in"
+                " Flask 2.3. Use 'FLASK_DEBUG' instead.",
+                file=sys.stderr,
+            )
+            return env == "development"
 
-    return val.lower() not in ("0", "false", "no")
+        return False
+
+    return val.lower() not in {"0", "false", "no"}
 
 
 def get_load_dotenv(default: bool = True) -> bool:
-    """Get whether the user has disabled loading dotenv files by setting
-    :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load the
-    files.
+    """Get whether the user has disabled loading default dotenv files by
+    setting :envvar:`FLASK_SKIP_DOTENV`. The default is ``True``, load
+    the files.
 
     :param default: What to return if the env var isn't set.
     """
@@ -110,11 +130,11 @@ def stream_with_context(
         return update_wrapper(decorator, generator_or_function)  # type: ignore
 
     def generator() -> t.Generator:
-        ctx = _request_ctx_stack.top
+        ctx = _cv_request.get(None)
         if ctx is None:
             raise RuntimeError(
-                "Attempted to stream with context but "
-                "there was no context in the first place to keep around."
+                "'stream_with_context' can only be used when a request"
+                " context is active, such as in a view function."
             )
         with ctx:
             # Dummy sentinel.  Has to be inside the context block or we're
@@ -129,7 +149,7 @@ def stream_with_context(
                 yield from gen
             finally:
                 if hasattr(gen, "close"):
-                    gen.close()  # type: ignore
+                    gen.close()
 
     # The trick is to start the generator.  Then the code execution runs until
     # the first dummy None is yielded at which point the context was already
@@ -189,155 +209,107 @@ def make_response(*args: t.Any) -> "Response":
     return current_app.make_response(args)  # type: ignore
 
 
-def url_for(endpoint: str, **values: t.Any) -> str:
-    """Generates a URL to the given endpoint with the method provided.
-
-    Variable arguments that are unknown to the target endpoint are appended
-    to the generated URL as query arguments.  If the value of a query argument
-    is ``None``, the whole pair is skipped.  In case blueprints are active
-    you can shortcut references to the same blueprint by prefixing the
-    local endpoint with a dot (``.``).
-
-    This will reference the index function local to the current blueprint::
-
-        url_for('.index')
-
-    See :ref:`url-building`.
-
-    Configuration values ``APPLICATION_ROOT`` and ``SERVER_NAME`` are only used when
-    generating URLs outside of a request context.
-
-    To integrate applications, :class:`Flask` has a hook to intercept URL build
-    errors through :attr:`Flask.url_build_error_handlers`.  The `url_for`
-    function results in a :exc:`~werkzeug.routing.BuildError` when the current
-    app does not have a URL for the given endpoint and values.  When it does, the
-    :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
-    it is not ``None``, which can return a string to use as the result of
-    `url_for` (instead of `url_for`'s default to raise the
-    :exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
-    An example::
-
-        def external_url_handler(error, endpoint, values):
-            "Looks up an external URL when `url_for` cannot build a URL."
-            # This is an example of hooking the build_error_handler.
-            # Here, lookup_url is some utility function you've built
-            # which looks up the endpoint in some external URL registry.
-            url = lookup_url(endpoint, **values)
-            if url is None:
-                # External lookup did not have a URL.
-                # Re-raise the BuildError, in context of original traceback.
-                exc_type, exc_value, tb = sys.exc_info()
-                if exc_value is error:
-                    raise exc_type(exc_value).with_traceback(tb)
-                else:
-                    raise error
-            # url_for will use this result, instead of raising BuildError.
-            return url
-
-        app.url_build_error_handlers.append(external_url_handler)
-
-    Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
-    `endpoint` and `values` are the arguments passed into `url_for`.  Note
-    that this is for building URLs outside the current application, and not for
-    handling 404 NotFound errors.
-
-    .. versionadded:: 0.10
-       The `_scheme` parameter was added.
+def url_for(
+    endpoint: str,
+    *,
+    _anchor: t.Optional[str] = None,
+    _method: t.Optional[str] = None,
+    _scheme: t.Optional[str] = None,
+    _external: t.Optional[bool] = None,
+    **values: t.Any,
+) -> str:
+    """Generate a URL to the given endpoint with the given values.
+
+    This requires an active request or application context, and calls
+    :meth:`current_app.url_for() <flask.Flask.url_for>`. See that method
+    for full documentation.
+
+    :param endpoint: The endpoint name associated with the URL to
+        generate. If this starts with a ``.``, the current blueprint
+        name (if any) will be used.
+    :param _anchor: If given, append this as ``#anchor`` to the URL.
+    :param _method: If given, generate the URL associated with this
+        method for the endpoint.
+    :param _scheme: If given, the URL will have this scheme if it is
+        external.
+    :param _external: If given, prefer the URL to be internal (False) or
+        require it to be external (True). External URLs include the
+        scheme and domain. When not in an active request, URLs are
+        external by default.
+    :param values: Values to use for the variable parts of the URL rule.
+        Unknown keys are appended as query string arguments, like
+        ``?a=b&c=d``.
+
+    .. versionchanged:: 2.2
+        Calls ``current_app.url_for``, allowing an app to override the
+        behavior.
+
+    .. versionchanged:: 0.10
+       The ``_scheme`` parameter was added.
 
-    .. versionadded:: 0.9
-       The `_anchor` and `_method` parameters were added.
+    .. versionchanged:: 0.9
+       The ``_anchor`` and ``_method`` parameters were added.
 
-    .. versionadded:: 0.9
-       Calls :meth:`Flask.handle_build_error` on
-       :exc:`~werkzeug.routing.BuildError`.
-
-    :param endpoint: the endpoint of the URL (name of the function)
-    :param values: the variable arguments of the URL rule
-    :param _external: if set to ``True``, an absolute URL is generated. Server
-      address can be changed via ``SERVER_NAME`` configuration variable which
-      falls back to the `Host` header, then to the IP and port of the request.
-    :param _scheme: a string specifying the desired URL scheme. The `_external`
-      parameter must be set to ``True`` or a :exc:`ValueError` is raised. The default
-      behavior uses the same scheme as the current request, or
-      :data:`PREFERRED_URL_SCHEME` if no request context is available.
-      This also can be set to an empty string to build protocol-relative
-      URLs.
-    :param _anchor: if provided this is added as anchor to the URL.
-    :param _method: if provided this explicitly specifies an HTTP method.
+    .. versionchanged:: 0.9
+       Calls ``app.handle_url_build_error`` on build errors.
     """
-    appctx = _app_ctx_stack.top
-    reqctx = _request_ctx_stack.top
-
-    if appctx is None:
-        raise RuntimeError(
-            "Attempted to generate a URL without the application context being"
-            " pushed. This has to be executed when application context is"
-            " available."
-        )
+    return current_app.url_for(
+        endpoint,
+        _anchor=_anchor,
+        _method=_method,
+        _scheme=_scheme,
+        _external=_external,
+        **values,
+    )
 
-    # If request specific information is available we have some extra
-    # features that support "relative" URLs.
-    if reqctx is not None:
-        url_adapter = reqctx.url_adapter
-        blueprint_name = request.blueprint
 
-        if endpoint[:1] == ".":
-            if blueprint_name is not None:
-                endpoint = f"{blueprint_name}{endpoint}"
-            else:
-                endpoint = endpoint[1:]
+def redirect(
+    location: str, code: int = 302, Response: t.Optional[t.Type["BaseResponse"]] = None
+) -> "BaseResponse":
+    """Create a redirect response object.
 
-        external = values.pop("_external", False)
+    If :data:`~flask.current_app` is available, it will use its
+    :meth:`~flask.Flask.redirect` method, otherwise it will use
+    :func:`werkzeug.utils.redirect`.
 
-    # Otherwise go with the url adapter from the appctx and make
-    # the URLs external by default.
-    else:
-        url_adapter = appctx.url_adapter
+    :param location: The URL to redirect to.
+    :param code: The status code for the redirect.
+    :param Response: The response class to use. Not used when
+        ``current_app`` is active, which uses ``app.response_class``.
 
-        if url_adapter is None:
-            raise RuntimeError(
-                "Application was not able to create a URL adapter for request"
-                " independent URL generation. You might be able to fix this by"
-                " setting the SERVER_NAME config variable."
-            )
+    .. versionadded:: 2.2
+        Calls ``current_app.redirect`` if available instead of always
+        using Werkzeug's default ``redirect``.
+    """
+    if current_app:
+        return current_app.redirect(location, code=code)
 
-        external = values.pop("_external", True)
+    return _wz_redirect(location, code=code, Response=Response)
 
-    anchor = values.pop("_anchor", None)
-    method = values.pop("_method", None)
-    scheme = values.pop("_scheme", None)
-    appctx.app.inject_url_defaults(endpoint, values)
 
-    # This is not the best way to deal with this but currently the
-    # underlying Werkzeug router does not support overriding the scheme on
-    # a per build call basis.
-    old_scheme = None
-    if scheme is not None:
-        if not external:
-            raise ValueError("When specifying _scheme, _external must be True")
-        old_scheme = url_adapter.url_scheme
-        url_adapter.url_scheme = scheme
+def abort(
+    code: t.Union[int, "BaseResponse"], *args: t.Any, **kwargs: t.Any
+) -> "te.NoReturn":
+    """Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given
+    status code.
 
-    try:
-        try:
-            rv = url_adapter.build(
-                endpoint, values, method=method, force_external=external
-            )
-        finally:
-            if old_scheme is not None:
-                url_adapter.url_scheme = old_scheme
-    except BuildError as error:
-        # We need to inject the values again so that the app callback can
-        # deal with that sort of stuff.
-        values["_external"] = external
-        values["_anchor"] = anchor
-        values["_method"] = method
-        values["_scheme"] = scheme
-        return appctx.app.handle_url_build_error(error, endpoint, values)
-
-    if anchor is not None:
-        rv += f"#{url_quote(anchor)}"
-    return rv
+    If :data:`~flask.current_app` is available, it will call its
+    :attr:`~flask.Flask.aborter` object, otherwise it will use
+    :func:`werkzeug.exceptions.abort`.
+
+    :param code: The status code for the exception, which must be
+        registered in ``app.aborter``.
+    :param args: Passed to the exception.
+    :param kwargs: Passed to the exception.
+
+    .. versionadded:: 2.2
+        Calls ``current_app.aborter`` if available instead of always
+        using Werkzeug's default ``abort``.
+    """
+    if current_app:
+        current_app.aborter(code, *args, **kwargs)
+
+    _wz_abort(code, *args, **kwargs)
 
 
 def get_template_attribute(template_name: str, attribute: str) -> t.Any:
@@ -425,11 +397,10 @@ def get_flashed_messages(
     :param category_filter: filter of categories to limit return values.  Only
                             categories in the list will be returned.
     """
-    flashes = _request_ctx_stack.top.flashes
+    flashes = request_ctx.flashes
     if flashes is None:
-        _request_ctx_stack.top.flashes = flashes = (
-            session.pop("_flashes") if "_flashes" in session else []
-        )
+        flashes = session.pop("_flashes") if "_flashes" in session else []
+        request_ctx.flashes = flashes
     if category_filter:
         flashes = list(filter(lambda f: f[0] in category_filter, flashes))
     if not with_categories:
@@ -437,54 +408,13 @@ def get_flashed_messages(
     return flashes
 
 
-def _prepare_send_file_kwargs(
-    download_name: t.Optional[str] = None,
-    attachment_filename: t.Optional[str] = None,
-    etag: t.Optional[t.Union[bool, str]] = None,
-    add_etags: t.Optional[t.Union[bool]] = None,
-    max_age: t.Optional[
-        t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
-    ] = None,
-    cache_timeout: t.Optional[int] = None,
-    **kwargs: t.Any,
-) -> t.Dict[str, t.Any]:
-    if attachment_filename is not None:
-        warnings.warn(
-            "The 'attachment_filename' parameter has been renamed to"
-            " 'download_name'. The old name will be removed in Flask"
-            " 2.2.",
-            DeprecationWarning,
-            stacklevel=3,
-        )
-        download_name = attachment_filename
-
-    if cache_timeout is not None:
-        warnings.warn(
-            "The 'cache_timeout' parameter has been renamed to"
-            " 'max_age'. The old name will be removed in Flask 2.2.",
-            DeprecationWarning,
-            stacklevel=3,
-        )
-        max_age = cache_timeout
-
-    if add_etags is not None:
-        warnings.warn(
-            "The 'add_etags' parameter has been renamed to 'etag'. The"
-            " old name will be removed in Flask 2.2.",
-            DeprecationWarning,
-            stacklevel=3,
-        )
-        etag = add_etags
-
-    if max_age is None:
-        max_age = current_app.get_send_file_max_age
+def _prepare_send_file_kwargs(**kwargs: t.Any) -> t.Dict[str, t.Any]:
+    if kwargs.get("max_age") is None:
+        kwargs["max_age"] = current_app.get_send_file_max_age
 
     kwargs.update(
         environ=request.environ,
-        download_name=download_name,
-        etag=etag,
-        max_age=max_age,
-        use_x_sendfile=current_app.use_x_sendfile,
+        use_x_sendfile=current_app.config["USE_X_SENDFILE"],
         response_class=current_app.response_class,
         _root_path=current_app.root_path,  # type: ignore
     )
@@ -496,16 +426,13 @@ def send_file(
     mimetype: t.Optional[str] = None,
     as_attachment: bool = False,
     download_name: t.Optional[str] = None,
-    attachment_filename: t.Optional[str] = None,
     conditional: bool = True,
     etag: t.Union[bool, str] = True,
-    add_etags: t.Optional[bool] = None,
     last_modified: t.Optional[t.Union[datetime, int, float]] = None,
     max_age: t.Optional[
         t.Union[int, t.Callable[[t.Optional[str]], t.Optional[int]]]
     ] = None,
-    cache_timeout: t.Optional[int] = None,
-):
+) -> "Response":
     """Send the contents of a file to the client.
 
     The first argument can be a file path or a file-like object. Paths
@@ -607,20 +534,17 @@ def send_file(
 
     .. versionadded:: 0.2
     """
-    return werkzeug.utils.send_file(
+    return werkzeug.utils.send_file(  # type: ignore[return-value]
         **_prepare_send_file_kwargs(
             path_or_file=path_or_file,
             environ=request.environ,
             mimetype=mimetype,
             as_attachment=as_attachment,
             download_name=download_name,
-            attachment_filename=attachment_filename,
             conditional=conditional,
             etag=etag,
-            add_etags=add_etags,
             last_modified=last_modified,
             max_age=max_age,
-            cache_timeout=cache_timeout,
         )
     )
 
@@ -628,7 +552,6 @@ def send_file(
 def send_from_directory(
     directory: t.Union[os.PathLike, str],
     path: t.Union[os.PathLike, str],
-    filename: t.Optional[str] = None,
     **kwargs: t.Any,
 ) -> "Response":
     """Send a file from within a directory using :func:`send_file`.
@@ -664,16 +587,7 @@ def send_from_directory(
 
     .. versionadded:: 0.5
     """
-    if filename is not None:
-        warnings.warn(
-            "The 'filename' parameter has been renamed to 'path'. The"
-            " old name will be removed in Flask 2.2.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        path = filename
-
-    return werkzeug.utils.send_from_directory(  # type: ignore
+    return werkzeug.utils.send_from_directory(  # type: ignore[return-value]
         directory, path, **_prepare_send_file_kwargs(**kwargs)
     )
 
@@ -703,7 +617,7 @@ def get_root_path(import_name: str) -> str:
         return os.getcwd()
 
     if hasattr(loader, "get_filename"):
-        filepath = loader.get_filename(import_name)  # type: ignore
+        filepath = loader.get_filename(import_name)
     else:
         # Fall back to imports.
         __import__(import_name)

Некоторые файлы не были показаны из-за большого количества измененных файлов