Browse Source

Intermediate changes

robot-piglet 10 months ago
parent
commit
38934cfb0b

+ 4 - 6
contrib/python/Flask/py3/.dist-info/METADATA

@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: Flask
-Version: 2.0.3
+Version: 2.1.3
 Summary: A simple framework for building complex web applications.
 Home-page: https://palletsprojects.com/p/flask
 Author: Armin Ronacher
@@ -15,7 +15,6 @@ Project-URL: Source Code, https://github.com/pallets/flask/
 Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/
 Project-URL: Twitter, https://twitter.com/PalletsTeam
 Project-URL: Chat, https://discord.gg/pallets
-Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
 Classifier: Environment :: Web Environment
 Classifier: Framework :: Flask
@@ -27,13 +26,14 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
 Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
 Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
-Requires-Python: >=3.6
+Requires-Python: >=3.7
 Description-Content-Type: text/x-rst
 License-File: LICENSE.rst
 Requires-Dist: Werkzeug (>=2.0)
 Requires-Dist: Jinja2 (>=3.0)
 Requires-Dist: itsdangerous (>=2.0)
-Requires-Dist: click (>=7.1.2)
+Requires-Dist: click (>=8.0)
+Requires-Dist: importlib-metadata (>=3.6.0) ; python_version < "3.10"
 Provides-Extra: async
 Requires-Dist: asgiref (>=3.2) ; extra == 'async'
 Provides-Extra: dotenv
@@ -121,5 +121,3 @@ Links
 -   Website: https://palletsprojects.com/p/flask/
 -   Twitter: https://twitter.com/PalletsTeam
 -   Chat: https://discord.gg/pallets
-
-

+ 1 - 2
contrib/python/Flask/py3/flask/__init__.py

@@ -23,7 +23,6 @@ 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 safe_join as safe_join
 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
@@ -43,4 +42,4 @@ 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
 
-__version__ = "2.0.3"
+__version__ = "2.1.3"

+ 52 - 52
contrib/python/Flask/py3/flask/app.py

@@ -16,7 +16,6 @@ from werkzeug.exceptions import BadRequest
 from werkzeug.exceptions import BadRequestKeyError
 from werkzeug.exceptions import HTTPException
 from werkzeug.exceptions import InternalServerError
-from werkzeug.local import ContextVar
 from werkzeug.routing import BuildError
 from werkzeug.routing import Map
 from werkzeug.routing import MapAdapter
@@ -27,6 +26,7 @@ from werkzeug.wrappers import Response as BaseResponse
 
 from . import cli
 from . import json
+from . import typing as ft
 from .config import Config
 from .config import ConfigAttribute
 from .ctx import _AppCtxGlobals
@@ -51,6 +51,7 @@ from .scaffold import find_package
 from .scaffold import Scaffold
 from .scaffold import setupmethod
 from .sessions import SecureCookieSessionInterface
+from .sessions import SessionInterface
 from .signals import appcontext_tearing_down
 from .signals import got_request_exception
 from .signals import request_finished
@@ -58,12 +59,6 @@ from .signals import request_started
 from .signals import request_tearing_down
 from .templating import DispatchingJinjaLoader
 from .templating import Environment
-from .typing import BeforeFirstRequestCallable
-from .typing import ResponseReturnValue
-from .typing import TeardownCallable
-from .typing import TemplateFilterCallable
-from .typing import TemplateGlobalCallable
-from .typing import TemplateTestCallable
 from .wrappers import Request
 from .wrappers import Response
 
@@ -72,7 +67,6 @@ if t.TYPE_CHECKING:
     from .blueprints import Blueprint
     from .testing import FlaskClient
     from .testing import FlaskCliRunner
-    from .typing import ErrorHandlerCallable
 
 if sys.version_info >= (3, 8):
     iscoroutinefunction = inspect.iscoroutinefunction
@@ -379,7 +373,7 @@ class Flask(Scaffold):
     #: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
     #:
     #: .. versionadded:: 0.8
-    session_interface = SecureCookieSessionInterface()
+    session_interface: SessionInterface = SecureCookieSessionInterface()
 
     def __init__(
         self,
@@ -436,7 +430,7 @@ class Flask(Scaffold):
         #: :meth:`before_first_request` decorator.
         #:
         #: .. versionadded:: 0.8
-        self.before_first_request_funcs: t.List[BeforeFirstRequestCallable] = []
+        self.before_first_request_funcs: t.List[ft.BeforeFirstRequestCallable] = []
 
         #: A list of functions that are called when the application context
         #: is destroyed.  Since the application context is also torn down
@@ -444,7 +438,7 @@ class Flask(Scaffold):
         #: from databases.
         #:
         #: .. versionadded:: 0.9
-        self.teardown_appcontext_funcs: t.List[TeardownCallable] = []
+        self.teardown_appcontext_funcs: t.List[ft.TeardownCallable] = []
 
         #: A list of shell context processor functions that should be run
         #: when a shell context is created.
@@ -1039,7 +1033,7 @@ class Flask(Scaffold):
         self,
         rule: str,
         endpoint: t.Optional[str] = None,
-        view_func: t.Optional[t.Callable] = None,
+        view_func: t.Optional[ft.ViewCallable] = None,
         provide_automatic_options: t.Optional[bool] = None,
         **options: t.Any,
     ) -> None:
@@ -1096,7 +1090,7 @@ class Flask(Scaffold):
     @setupmethod
     def template_filter(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]:
+    ) -> t.Callable[[ft.TemplateFilterCallable], ft.TemplateFilterCallable]:
         """A decorator that is used to register custom template filter.
         You can specify a name for the filter, otherwise the function
         name will be used. Example::
@@ -1109,7 +1103,7 @@ class Flask(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
+        def decorator(f: ft.TemplateFilterCallable) -> ft.TemplateFilterCallable:
             self.add_template_filter(f, name=name)
             return f
 
@@ -1117,7 +1111,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_filter(
-        self, f: TemplateFilterCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None
     ) -> None:
         """Register a custom template filter.  Works exactly like the
         :meth:`template_filter` decorator.
@@ -1130,7 +1124,7 @@ class Flask(Scaffold):
     @setupmethod
     def template_test(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]:
+    ) -> t.Callable[[ft.TemplateTestCallable], ft.TemplateTestCallable]:
         """A decorator that is used to register custom template test.
         You can specify a name for the test, otherwise the function
         name will be used. Example::
@@ -1150,7 +1144,7 @@ class Flask(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
+        def decorator(f: ft.TemplateTestCallable) -> ft.TemplateTestCallable:
             self.add_template_test(f, name=name)
             return f
 
@@ -1158,7 +1152,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_test(
-        self, f: TemplateTestCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateTestCallable, name: t.Optional[str] = None
     ) -> None:
         """Register a custom template test.  Works exactly like the
         :meth:`template_test` decorator.
@@ -1173,7 +1167,7 @@ class Flask(Scaffold):
     @setupmethod
     def template_global(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]:
+    ) -> t.Callable[[ft.TemplateGlobalCallable], ft.TemplateGlobalCallable]:
         """A decorator that is used to register a custom template global function.
         You can specify a name for the global function, otherwise the function
         name will be used. Example::
@@ -1188,7 +1182,7 @@ class Flask(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
+        def decorator(f: ft.TemplateGlobalCallable) -> ft.TemplateGlobalCallable:
             self.add_template_global(f, name=name)
             return f
 
@@ -1196,7 +1190,7 @@ class Flask(Scaffold):
 
     @setupmethod
     def add_template_global(
-        self, f: TemplateGlobalCallable, name: t.Optional[str] = None
+        self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None
     ) -> None:
         """Register a custom template global function. Works exactly like the
         :meth:`template_global` decorator.
@@ -1210,8 +1204,8 @@ class Flask(Scaffold):
 
     @setupmethod
     def before_first_request(
-        self, f: BeforeFirstRequestCallable
-    ) -> BeforeFirstRequestCallable:
+        self, f: ft.BeforeFirstRequestCallable
+    ) -> ft.BeforeFirstRequestCallable:
         """Registers a function to be run before the first request to this
         instance of the application.
 
@@ -1224,7 +1218,7 @@ class Flask(Scaffold):
         return f
 
     @setupmethod
-    def teardown_appcontext(self, f: TeardownCallable) -> TeardownCallable:
+    def teardown_appcontext(self, f: ft.TeardownCallable) -> ft.TeardownCallable:
         """Registers a function to be called when the application context
         ends.  These functions are typically also called when the request
         context is popped.
@@ -1265,9 +1259,7 @@ class Flask(Scaffold):
         self.shell_context_processors.append(f)
         return f
 
-    def _find_error_handler(
-        self, e: Exception
-    ) -> t.Optional["ErrorHandlerCallable[Exception]"]:
+    def _find_error_handler(self, e: Exception) -> t.Optional[ft.ErrorHandlerCallable]:
         """Return a registered error handler for an exception in this order:
         blueprint handler for a specific code, app handler for a specific code,
         blueprint handler for an exception class, app handler for an exception
@@ -1292,7 +1284,7 @@ class Flask(Scaffold):
 
     def handle_http_exception(
         self, e: HTTPException
-    ) -> t.Union[HTTPException, ResponseReturnValue]:
+    ) -> t.Union[HTTPException, ft.ResponseReturnValue]:
         """Handles an HTTP exception.  By default this will invoke the
         registered error handlers and fall back to returning the
         exception as response.
@@ -1362,7 +1354,7 @@ class Flask(Scaffold):
 
     def handle_user_exception(
         self, e: Exception
-    ) -> t.Union[HTTPException, ResponseReturnValue]:
+    ) -> t.Union[HTTPException, ft.ResponseReturnValue]:
         """This method is called whenever an exception occurs that
         should be handled. A special case is :class:`~werkzeug
         .exceptions.HTTPException` which is forwarded to the
@@ -1432,7 +1424,7 @@ class Flask(Scaffold):
             raise e
 
         self.log_exception(exc_info)
-        server_error: t.Union[InternalServerError, ResponseReturnValue]
+        server_error: t.Union[InternalServerError, ft.ResponseReturnValue]
         server_error = InternalServerError(original_exception=e)
         handler = self._find_error_handler(server_error)
 
@@ -1459,17 +1451,26 @@ class Flask(Scaffold):
         )
 
     def raise_routing_exception(self, request: Request) -> "te.NoReturn":
-        """Exceptions that are recording during routing are reraised with
-        this method.  During debug we are not reraising redirect requests
-        for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
-        a different error instead to help debug situations.
+        """Intercept routing exceptions and possibly do something else.
+
+        In debug mode, intercept a routing redirect and replace it with
+        an error if the body will be discarded.
+
+        With modern Werkzeug this shouldn't occur, since it now uses a
+        308 status which tells the browser to resend the method and
+        body.
+
+        .. versionchanged:: 2.1
+            Don't intercept 307 and 308 redirects.
 
+        :meta private:
         :internal:
         """
         if (
             not self.debug
             or not isinstance(request.routing_exception, RequestRedirect)
-            or request.method in ("GET", "HEAD", "OPTIONS")
+            or request.routing_exception.code in {307, 308}
+            or request.method in {"GET", "HEAD", "OPTIONS"}
         ):
             raise request.routing_exception  # type: ignore
 
@@ -1477,7 +1478,7 @@ class Flask(Scaffold):
 
         raise FormDataRoutingRedirect(request)
 
-    def dispatch_request(self) -> ResponseReturnValue:
+    def dispatch_request(self) -> ft.ResponseReturnValue:
         """Does the request dispatching.  Matches the URL and returns the
         return value of the view or error handler.  This does not have to
         be a response object.  In order to convert the return value to a
@@ -1520,7 +1521,7 @@ class Flask(Scaffold):
 
     def finalize_request(
         self,
-        rv: t.Union[ResponseReturnValue, HTTPException],
+        rv: t.Union[ft.ResponseReturnValue, HTTPException],
         from_error_handler: bool = False,
     ) -> Response:
         """Given the return value from a view function this finalizes
@@ -1621,16 +1622,9 @@ class Flask(Scaffold):
                 "Install Flask with the 'async' extra in order to use async views."
             ) from None
 
-        # Check that Werkzeug isn't using its fallback ContextVar class.
-        if ContextVar.__module__ == "werkzeug.local":
-            raise RuntimeError(
-                "Async cannot be used with this combination of Python "
-                "and Greenlet versions."
-            )
-
         return asgiref_async_to_sync(func)
 
-    def make_response(self, rv: ResponseReturnValue) -> Response:
+    def make_response(self, rv: ft.ResponseReturnValue) -> Response:
         """Convert the return value from a view function to an instance of
         :attr:`response_class`.
 
@@ -1681,13 +1675,13 @@ class Flask(Scaffold):
 
             # a 3-tuple is unpacked directly
             if len_rv == 3:
-                rv, status, headers = rv
+                rv, status, headers = rv  # type: ignore[misc]
             # decide if a 2-tuple has status or headers
             elif len_rv == 2:
                 if isinstance(rv[1], (Headers, dict, tuple, list)):
                     rv, headers = rv
                 else:
-                    rv, status = rv
+                    rv, status = rv  # type: ignore[assignment,misc]
             # other sized tuples are not allowed
             else:
                 raise TypeError(
@@ -1710,7 +1704,11 @@ class Flask(Scaffold):
                 # let the response class set the status and headers instead of
                 # waiting to do it manually, so that the class can handle any
                 # special logic
-                rv = self.response_class(rv, status=status, headers=headers)
+                rv = self.response_class(
+                    rv,
+                    status=status,
+                    headers=headers,  # type: ignore[arg-type]
+                )
                 status = headers = None
             elif isinstance(rv, dict):
                 rv = jsonify(rv)
@@ -1718,7 +1716,9 @@ class Flask(Scaffold):
                 # evaluate a WSGI callable, or coerce a different response
                 # class to the correct type
                 try:
-                    rv = self.response_class.force_type(rv, request.environ)  # type: ignore  # noqa: B950
+                    rv = self.response_class.force_type(
+                        rv, request.environ  # type: ignore[arg-type]
+                    )
                 except TypeError as e:
                     raise TypeError(
                         f"{e}\nThe view function did not return a valid"
@@ -1738,13 +1738,13 @@ class Flask(Scaffold):
         # prefer the status if it was provided
         if status is not None:
             if isinstance(status, (str, bytes, bytearray)):
-                rv.status = status  # type: ignore
+                rv.status = status
             else:
                 rv.status_code = status
 
         # extend existing headers with provided headers
         if headers:
-            rv.headers.update(headers)
+            rv.headers.update(headers)  # type: ignore[arg-type]
 
         return rv
 
@@ -1834,7 +1834,7 @@ class Flask(Scaffold):
 
         raise error
 
-    def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
+    def preprocess_request(self) -> t.Optional[ft.ResponseReturnValue]:
         """Called before the request is dispatched. Calls
         :attr:`url_value_preprocessors` registered with the app and the
         current blueprint (if any). Then calls :attr:`before_request_funcs`

+ 33 - 51
contrib/python/Flask/py3/flask/blueprints.py

@@ -3,23 +3,13 @@ import typing as t
 from collections import defaultdict
 from functools import update_wrapper
 
+from . import typing as ft
 from .scaffold import _endpoint_from_view_func
 from .scaffold import _sentinel
 from .scaffold import Scaffold
-from .typing import AfterRequestCallable
-from .typing import BeforeFirstRequestCallable
-from .typing import BeforeRequestCallable
-from .typing import TeardownCallable
-from .typing import TemplateContextProcessorCallable
-from .typing import TemplateFilterCallable
-from .typing import TemplateGlobalCallable
-from .typing import TemplateTestCallable
-from .typing import URLDefaultCallable
-from .typing import URLValuePreprocessorCallable
 
 if t.TYPE_CHECKING:
     from .app import Flask
-    from .typing import ErrorHandlerCallable
 
 DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable]
 
@@ -299,24 +289,14 @@ class Blueprint(Scaffold):
         name = f"{name_prefix}.{self_name}".lstrip(".")
 
         if name in app.blueprints:
+            bp_desc = "this" if app.blueprints[name] is self else "a different"
             existing_at = f" '{name}'" if self_name != name else ""
 
-            if app.blueprints[name] is not self:
-                raise ValueError(
-                    f"The name '{self_name}' is already registered for"
-                    f" a different blueprint{existing_at}. Use 'name='"
-                    " to provide a unique name."
-                )
-            else:
-                import warnings
-
-                warnings.warn(
-                    f"The name '{self_name}' is already registered for"
-                    f" this blueprint{existing_at}. Use 'name=' to"
-                    " provide a unique name. This will become an error"
-                    " in Flask 2.1.",
-                    stacklevel=4,
-                )
+            raise ValueError(
+                f"The name '{self_name}' is already registered for"
+                f" {bp_desc} blueprint{existing_at}. Use 'name=' to"
+                f" provide a unique name."
+            )
 
         first_bp_registration = not any(bp is self for bp in app.blueprints.values())
         first_name_registration = name not in app.blueprints
@@ -404,7 +384,7 @@ class Blueprint(Scaffold):
         self,
         rule: str,
         endpoint: t.Optional[str] = None,
-        view_func: t.Optional[t.Callable] = None,
+        view_func: t.Optional[ft.ViewCallable] = None,
         provide_automatic_options: t.Optional[bool] = None,
         **options: t.Any,
     ) -> None:
@@ -429,7 +409,7 @@ class Blueprint(Scaffold):
 
     def app_template_filter(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateFilterCallable], TemplateFilterCallable]:
+    ) -> t.Callable[[ft.TemplateFilterCallable], ft.TemplateFilterCallable]:
         """Register a custom template filter, available application wide.  Like
         :meth:`Flask.template_filter` but for a blueprint.
 
@@ -437,14 +417,14 @@ class Blueprint(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateFilterCallable) -> TemplateFilterCallable:
+        def decorator(f: ft.TemplateFilterCallable) -> ft.TemplateFilterCallable:
             self.add_app_template_filter(f, name=name)
             return f
 
         return decorator
 
     def add_app_template_filter(
-        self, f: TemplateFilterCallable, name: t.Optional[str] = None
+        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
@@ -461,7 +441,7 @@ class Blueprint(Scaffold):
 
     def app_template_test(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateTestCallable], TemplateTestCallable]:
+    ) -> t.Callable[[ft.TemplateTestCallable], ft.TemplateTestCallable]:
         """Register a custom template test, available application wide.  Like
         :meth:`Flask.template_test` but for a blueprint.
 
@@ -471,14 +451,14 @@ class Blueprint(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateTestCallable) -> TemplateTestCallable:
+        def decorator(f: ft.TemplateTestCallable) -> ft.TemplateTestCallable:
             self.add_app_template_test(f, name=name)
             return f
 
         return decorator
 
     def add_app_template_test(
-        self, f: TemplateTestCallable, name: t.Optional[str] = None
+        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
@@ -497,7 +477,7 @@ class Blueprint(Scaffold):
 
     def app_template_global(
         self, name: t.Optional[str] = None
-    ) -> t.Callable[[TemplateGlobalCallable], TemplateGlobalCallable]:
+    ) -> t.Callable[[ft.TemplateGlobalCallable], ft.TemplateGlobalCallable]:
         """Register a custom template global, available application wide.  Like
         :meth:`Flask.template_global` but for a blueprint.
 
@@ -507,14 +487,14 @@ class Blueprint(Scaffold):
                      function name will be used.
         """
 
-        def decorator(f: TemplateGlobalCallable) -> TemplateGlobalCallable:
+        def decorator(f: ft.TemplateGlobalCallable) -> ft.TemplateGlobalCallable:
             self.add_app_template_global(f, name=name)
             return f
 
         return decorator
 
     def add_app_template_global(
-        self, f: TemplateGlobalCallable, name: t.Optional[str] = None
+        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
@@ -531,7 +511,9 @@ class Blueprint(Scaffold):
 
         self.record_once(register_template)
 
-    def before_app_request(self, f: BeforeRequestCallable) -> BeforeRequestCallable:
+    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.
         """
@@ -541,15 +523,15 @@ class Blueprint(Scaffold):
         return f
 
     def before_app_first_request(
-        self, f: BeforeFirstRequestCallable
-    ) -> BeforeFirstRequestCallable:
+        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.record_once(lambda s: s.app.before_first_request_funcs.append(f))
         return f
 
-    def after_app_request(self, f: AfterRequestCallable) -> AfterRequestCallable:
+    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.
         """
@@ -558,7 +540,7 @@ class Blueprint(Scaffold):
         )
         return f
 
-    def teardown_app_request(self, f: TeardownCallable) -> TeardownCallable:
+    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.
@@ -569,8 +551,8 @@ class Blueprint(Scaffold):
         return f
 
     def app_context_processor(
-        self, f: TemplateContextProcessorCallable
-    ) -> TemplateContextProcessorCallable:
+        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.
         """
@@ -579,29 +561,29 @@ class Blueprint(Scaffold):
         )
         return f
 
-    def app_errorhandler(self, code: t.Union[t.Type[Exception], int]) -> t.Callable:
+    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.
         """
 
-        def decorator(
-            f: "ErrorHandlerCallable[Exception]",
-        ) -> "ErrorHandlerCallable[Exception]":
+        def decorator(f: ft.ErrorHandlerDecorator) -> ft.ErrorHandlerDecorator:
             self.record_once(lambda s: s.app.errorhandler(code)(f))
             return f
 
         return decorator
 
     def app_url_value_preprocessor(
-        self, f: URLValuePreprocessorCallable
-    ) -> URLValuePreprocessorCallable:
+        self, f: ft.URLValuePreprocessorCallable
+    ) -> ft.URLValuePreprocessorCallable:
         """Same as :meth:`url_value_preprocessor` but application wide."""
         self.record_once(
             lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
         )
         return f
 
-    def app_url_defaults(self, f: URLDefaultCallable) -> URLDefaultCallable:
+    def app_url_defaults(self, f: ft.URLDefaultCallable) -> ft.URLDefaultCallable:
         """Same as :meth:`url_defaults` but application wide."""
         self.record_once(
             lambda s: s.app.url_default_functions.setdefault(None, []).append(f)

+ 70 - 79
contrib/python/Flask/py3/flask/cli.py

@@ -5,7 +5,6 @@ import platform
 import re
 import sys
 import traceback
-import warnings
 from functools import update_wrapper
 from operator import attrgetter
 from threading import Lock
@@ -19,22 +18,12 @@ from .helpers import get_debug_flag
 from .helpers import get_env
 from .helpers import get_load_dotenv
 
-try:
-    import dotenv
-except ImportError:
-    dotenv = None
-
-try:
-    import ssl
-except ImportError:
-    ssl = None  # type: ignore
-
 
 class NoAppException(click.UsageError):
     """Raised if an application cannot be found or loaded."""
 
 
-def find_best_app(script_info, module):
+def find_best_app(module):
     """Given a module instance this tries to find the best possible
     application in the module or raises an exception.
     """
@@ -65,7 +54,7 @@ def find_best_app(script_info, module):
 
         if inspect.isfunction(app_factory):
             try:
-                app = call_factory(script_info, app_factory)
+                app = app_factory()
 
                 if isinstance(app, Flask):
                     return app
@@ -87,42 +76,6 @@ def find_best_app(script_info, module):
     )
 
 
-def call_factory(script_info, app_factory, args=None, kwargs=None):
-    """Takes an app factory, a ``script_info` object and  optionally a tuple
-    of arguments. Checks for the existence of a script_info argument and calls
-    the app_factory depending on that and the arguments provided.
-    """
-    sig = inspect.signature(app_factory)
-    args = [] if args is None else args
-    kwargs = {} if kwargs is None else kwargs
-
-    if "script_info" in sig.parameters:
-        warnings.warn(
-            "The 'script_info' argument is deprecated and will not be"
-            " passed to the app factory function in Flask 2.1.",
-            DeprecationWarning,
-        )
-        kwargs["script_info"] = script_info
-
-    if not args and len(sig.parameters) == 1:
-        first_parameter = next(iter(sig.parameters.values()))
-
-        if (
-            first_parameter.default is inspect.Parameter.empty
-            # **kwargs is reported as an empty default, ignore it
-            and first_parameter.kind is not inspect.Parameter.VAR_KEYWORD
-        ):
-            warnings.warn(
-                "Script info is deprecated and will not be passed as the"
-                " single argument to the app factory function in Flask"
-                " 2.1.",
-                DeprecationWarning,
-            )
-            args.append(script_info)
-
-    return app_factory(*args, **kwargs)
-
-
 def _called_with_wrong_args(f):
     """Check whether calling a function raised a ``TypeError`` because
     the call failed or because something in the factory raised the
@@ -149,7 +102,7 @@ def _called_with_wrong_args(f):
         del tb
 
 
-def find_app_by_string(script_info, module, app_name):
+def find_app_by_string(module, app_name):
     """Check if the given string is a variable name or a function. Call
     a function to get the app instance, or return the variable directly.
     """
@@ -166,7 +119,8 @@ def find_app_by_string(script_info, module, app_name):
 
     if isinstance(expr, ast.Name):
         name = expr.id
-        args = kwargs = None
+        args = []
+        kwargs = {}
     elif isinstance(expr, ast.Call):
         # Ensure the function name is an attribute name only.
         if not isinstance(expr.func, ast.Name):
@@ -202,7 +156,7 @@ def find_app_by_string(script_info, module, app_name):
     # to get the real application.
     if inspect.isfunction(attr):
         try:
-            app = call_factory(script_info, attr, args, kwargs)
+            app = attr(*args, **kwargs)
         except TypeError as e:
             if not _called_with_wrong_args(attr):
                 raise
@@ -253,7 +207,7 @@ def prepare_import(path):
     return ".".join(module_name[::-1])
 
 
-def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
+def locate_app(module_name, app_name, raise_if_not_found=True):
     __traceback_hide__ = True  # noqa: F841
 
     try:
@@ -274,9 +228,9 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
     module = sys.modules[module_name]
 
     if app_name is None:
-        return find_best_app(script_info, module)
+        return find_best_app(module)
     else:
-        return find_app_by_string(script_info, module, app_name)
+        return find_app_by_string(module, app_name)
 
 
 def get_version(ctx, param, value):
@@ -327,9 +281,17 @@ class DispatchingApp:
             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:
@@ -397,18 +359,18 @@ class ScriptInfo:
             return self._loaded_app
 
         if self.create_app is not None:
-            app = call_factory(self, self.create_app)
+            app = self.create_app()
         else:
             if self.app_import_path:
                 path, name = (
                     re.split(r":(?![\\/])", self.app_import_path, 1) + [None]
                 )[:2]
                 import_name = prepare_import(path)
-                app = locate_app(self, import_name, name)
+                app = locate_app(import_name, name)
             else:
                 for path in ("wsgi.py", "app.py"):
                     import_name = prepare_import(path)
-                    app = locate_app(self, import_name, None, raise_if_not_found=False)
+                    app = locate_app(import_name, None, raise_if_not_found=False)
 
                     if app:
                         break
@@ -530,14 +492,18 @@ class FlaskGroup(AppGroup):
     def _load_plugin_commands(self):
         if self._loaded_plugin_commands:
             return
-        try:
-            import pkg_resources
-        except ImportError:
-            self._loaded_plugin_commands = True
-            return
 
-        for ep in pkg_resources.iter_entry_points("flask.commands"):
+        if sys.version_info >= (3, 10):
+            from importlib import metadata
+        else:
+            # Use a backport on Python < 3.10. We technically have
+            # importlib.metadata on 3.8+, but the API changed in 3.10,
+            # so use the backport for consistency.
+            import importlib_metadata as metadata
+
+        for ep in metadata.entry_points(group="flask.commands"):
             self.add_command(ep.load(), ep.name)
+
         self._loaded_plugin_commands = True
 
     def get_command(self, ctx, name):
@@ -630,7 +596,9 @@ def load_dotenv(path=None):
 
     .. versionadded:: 1.0
     """
-    if dotenv is None:
+    try:
+        import dotenv
+    except ImportError:
         if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"):
             click.secho(
                 " * Tip: There are .env or .flaskenv files present."
@@ -706,12 +674,14 @@ class CertParamType(click.ParamType):
         self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True)
 
     def convert(self, value, param, ctx):
-        if ssl is None:
+        try:
+            import ssl
+        except ImportError:
             raise click.BadParameter(
                 'Using "--cert" requires Python to be compiled with SSL support.',
                 ctx,
                 param,
-            )
+            ) from None
 
         try:
             return self.path_type(value, param, ctx)
@@ -744,7 +714,13 @@ def _validate_key(ctx, param, value):
     """
     cert = ctx.params.get("cert")
     is_adhoc = cert == "adhoc"
-    is_context = ssl and isinstance(cert, ssl.SSLContext)
+
+    try:
+        import ssl
+    except ImportError:
+        is_context = False
+    else:
+        is_context = isinstance(cert, ssl.SSLContext)
 
     if value is not None:
         if is_adhoc:
@@ -785,7 +761,10 @@ class SeparatedPathType(click.Path):
 @click.option("--host", "-h", default="127.0.0.1", help="The interface to bind to.")
 @click.option("--port", "-p", default=5000, help="The port to bind to.")
 @click.option(
-    "--cert", type=CertParamType(), help="Specify a certificate file to use HTTPS."
+    "--cert",
+    type=CertParamType(),
+    help="Specify a certificate file to use HTTPS.",
+    is_eager=True,
 )
 @click.option(
     "--key",
@@ -826,9 +805,28 @@ class SeparatedPathType(click.Path):
         f" are separated by {os.path.pathsep!r}."
     ),
 )
+@click.option(
+    "--exclude-patterns",
+    default=None,
+    type=SeparatedPathType(),
+    help=(
+        "Files matching these fnmatch patterns will not trigger a reload"
+        " on change. Multiple patterns are separated by"
+        f" {os.path.pathsep!r}."
+    ),
+)
 @pass_script_info
 def run_command(
-    info, host, port, reload, debugger, eager_loading, with_threads, cert, extra_files
+    info,
+    host,
+    port,
+    reload,
+    debugger,
+    eager_loading,
+    with_threads,
+    cert,
+    extra_files,
+    exclude_patterns,
 ):
     """Run a local development server.
 
@@ -860,6 +858,7 @@ def run_command(
         threaded=with_threads,
         ssl_context=cert,
         extra_files=extra_files,
+        exclude_patterns=exclude_patterns,
     )
 
 
@@ -984,15 +983,7 @@ debug mode.
 
 
 def main() -> None:
-    if int(click.__version__[0]) < 8:
-        warnings.warn(
-            "Using the `flask` cli with Click 7 is deprecated and"
-            " will not be supported starting with Flask 2.1."
-            " Please upgrade to Click 8 as soon as possible.",
-            DeprecationWarning,
-        )
-    # TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
-    cli.main(args=sys.argv[1:])
+    cli.main()
 
 
 if __name__ == "__main__":

+ 69 - 27
contrib/python/Flask/py3/flask/config.py

@@ -1,4 +1,5 @@
 import errno
+import json
 import os
 import types
 import typing as t
@@ -70,7 +71,7 @@ class Config(dict):
     """
 
     def __init__(self, root_path: str, defaults: t.Optional[dict] = None) -> None:
-        dict.__init__(self, defaults or {})
+        super().__init__(defaults or {})
         self.root_path = root_path
 
     def from_envvar(self, variable_name: str, silent: bool = False) -> bool:
@@ -97,6 +98,70 @@ class Config(dict):
             )
         return self.from_pyfile(rv, silent=silent)
 
+    def from_prefixed_env(
+        self, prefix: str = "FLASK", *, loads: t.Callable[[str], t.Any] = json.loads
+    ) -> bool:
+        """Load any environment variables that start with ``FLASK_``,
+        dropping the prefix from the env key for the config key. Values
+        are passed through a loading function to attempt to convert them
+        to more specific types than strings.
+
+        Keys are loaded in :func:`sorted` order.
+
+        The default loading function attempts to parse values as any
+        valid JSON type, including dicts and lists.
+
+        Specific items in nested dicts can be set by separating the
+        keys with double underscores (``__``). If an intermediate key
+        doesn't exist, it will be initialized to an empty dict.
+
+        :param prefix: Load env vars that start with this prefix,
+            separated with an underscore (``_``).
+        :param loads: Pass each string value to this function and use
+            the returned value as the config value. If any error is
+            raised it is ignored and the value remains a string. The
+            default is :func:`json.loads`.
+
+        .. versionadded:: 2.1
+        """
+        prefix = f"{prefix}_"
+        len_prefix = len(prefix)
+
+        for key in sorted(os.environ):
+            if not key.startswith(prefix):
+                continue
+
+            value = os.environ[key]
+
+            try:
+                value = loads(value)
+            except Exception:
+                # Keep the value as a string if loading failed.
+                pass
+
+            # Change to key.removeprefix(prefix) on Python >= 3.9.
+            key = key[len_prefix:]
+
+            if "__" not in key:
+                # A non-nested key, set directly.
+                self[key] = value
+                continue
+
+            # Traverse nested dictionaries with keys separated by "__".
+            current = self
+            *parts, tail = key.split("__")
+
+            for part in parts:
+                # If an intermediate dict does not exist, create it.
+                if part not in current:
+                    current[part] = {}
+
+                current = current[part]
+
+            current[tail] = value
+
+        return True
+
     def from_pyfile(self, filename: str, silent: bool = False) -> bool:
         """Updates the values in the config from a Python file.  This function
         behaves as if the file was imported as module with the
@@ -176,6 +241,9 @@ class Config(dict):
 
         .. code-block:: python
 
+            import json
+            app.config.from_file("config.json", load=json.load)
+
             import toml
             app.config.from_file("config.toml", load=toml.load)
 
@@ -204,32 +272,6 @@ class Config(dict):
 
         return self.from_mapping(obj)
 
-    def from_json(self, filename: str, silent: bool = False) -> bool:
-        """Update the values in the config from a JSON file. The loaded
-        data is passed to the :meth:`from_mapping` method.
-
-        :param filename: The path to the JSON file. This can be an
-            absolute path or relative to the config root path.
-        :param silent: Ignore the file if it doesn't exist.
-        :return: ``True`` if the file was loaded successfully.
-
-        .. deprecated:: 2.0.0
-            Will be removed in Flask 2.1. Use :meth:`from_file` instead.
-            This was removed early in 2.0.0, was added back in 2.0.1.
-
-        .. versionadded:: 0.11
-        """
-        import warnings
-        from . import json
-
-        warnings.warn(
-            "'from_json' is deprecated and will be removed in Flask"
-            " 2.1. Use 'from_file(path, json.load)' instead.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-        return self.from_file(filename, json.load, silent=silent)
-
     def from_mapping(
         self, mapping: t.Optional[t.Mapping[str, t.Any]] = None, **kwargs: t.Any
     ) -> bool:

+ 32 - 8
contrib/python/Flask/py3/flask/ctx.py

@@ -5,11 +5,11 @@ 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 .signals import appcontext_popped
 from .signals import appcontext_pushed
-from .typing import AfterRequestCallable
 
 if t.TYPE_CHECKING:
     from .app import Flask
@@ -109,7 +109,7 @@ class _AppCtxGlobals:
         return object.__repr__(self)
 
 
-def after_this_request(f: AfterRequestCallable) -> AfterRequestCallable:
+def after_this_request(f: ft.AfterRequestCallable) -> ft.AfterRequestCallable:
     """Executes a function after this request.  This is useful to modify
     response objects.  The function is passed the response object and has
     to return the same or a new one.
@@ -178,7 +178,7 @@ def copy_current_request_context(f: t.Callable) -> t.Callable:
 
     def wrapper(*args, **kwargs):
         with reqctx:
-            return f(*args, **kwargs)
+            return reqctx.app.ensure_sync(f)(*args, **kwargs)
 
     return update_wrapper(wrapper, f)
 
@@ -267,7 +267,10 @@ class AppContext:
         return self
 
     def __exit__(
-        self, exc_type: type, exc_value: BaseException, tb: TracebackType
+        self,
+        exc_type: t.Optional[type],
+        exc_value: t.Optional[BaseException],
+        tb: t.Optional[TracebackType],
     ) -> None:
         self.pop(exc_value)
 
@@ -338,14 +341,32 @@ class RequestContext:
         # 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[AfterRequestCallable] = []
+        self._after_request_functions: t.List[ft.AfterRequestCallable] = []
 
     @property
-    def g(self) -> AppContext:
+    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: AppContext) -> None:
+    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
 
     def copy(self) -> "RequestContext":
@@ -473,7 +494,10 @@ class RequestContext:
         return self
 
     def __exit__(
-        self, exc_type: type, exc_value: BaseException, tb: TracebackType
+        self,
+        exc_type: t.Optional[type],
+        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

+ 24 - 22
contrib/python/Flask/py3/flask/debughelpers.py

@@ -41,53 +41,55 @@ class DebugFilesKeyError(KeyError, AssertionError):
 
 
 class FormDataRoutingRedirect(AssertionError):
-    """This exception is raised by Flask in debug mode if it detects a
-    redirect caused by the routing system when the request method is not
-    GET, HEAD or OPTIONS.  Reasoning: form data will be dropped.
+    """This exception is raised in debug mode if a routing redirect
+    would cause the browser to drop the method or body. This happens
+    when method is not GET, HEAD or OPTIONS and the status code is not
+    307 or 308.
     """
 
     def __init__(self, request):
         exc = request.routing_exception
         buf = [
-            f"A request was sent to this URL ({request.url}) but a"
-            " redirect was issued automatically by the routing system"
-            f" to {exc.new_url!r}."
+            f"A request was sent to '{request.url}', but routing issued"
+            f" a redirect to the canonical URL '{exc.new_url}'."
         ]
 
-        # In case just a slash was appended we can be extra helpful
-        if f"{request.base_url}/" == exc.new_url.split("?")[0]:
+        if f"{request.base_url}/" == exc.new_url.partition("?")[0]:
             buf.append(
-                "  The URL was defined with a trailing slash so Flask"
-                " will automatically redirect to the URL with the"
-                " trailing slash if it was accessed without one."
+                " The URL was defined with a trailing slash. Flask"
+                " will redirect to the URL with a trailing slash if it"
+                " was accessed without one."
             )
 
         buf.append(
-            "  Make sure to directly send your"
-            f" {request.method}-request to this URL since we can't make"
-            " browsers or HTTP clients redirect with form data reliably"
-            " or without user interaction."
+            " Send requests to the canonical URL, or use 307 or 308 for"
+            " routing redirects. Otherwise, browsers will drop form"
+            " data.\n\n"
+            "This exception is only raised in debug mode."
         )
-        buf.append("\n\nNote: this exception is only raised in debug mode")
-        AssertionError.__init__(self, "".join(buf).encode("utf-8"))
+        super().__init__("".join(buf))
 
 
 def attach_enctype_error_multidict(request):
-    """Since Flask 0.8 we're monkeypatching the files object in case a
-    request is detected that does not use multipart form data but the files
-    object is accessed.
+    """Patch ``request.files.__getitem__`` to raise a descriptive error
+    about ``enctype=multipart/form-data``.
+
+    :param request: The request to patch.
+    :meta private:
     """
     oldcls = request.files.__class__
 
     class newcls(oldcls):
         def __getitem__(self, key):
             try:
-                return oldcls.__getitem__(self, key)
+                return super().__getitem__(key)
             except KeyError as e:
                 if key not in request.form:
                     raise
 
-                raise DebugFilesKeyError(request, key) from e
+                raise DebugFilesKeyError(request, key).with_traceback(
+                    e.__traceback__
+                ) from None
 
     newcls.__name__ = oldcls.__name__
     newcls.__module__ = oldcls.__module__

+ 7 - 52
contrib/python/Flask/py3/flask/helpers.py

@@ -5,13 +5,11 @@ import sys
 import typing as t
 import warnings
 from datetime import datetime
-from datetime import timedelta
 from functools import lru_cache
 from functools import update_wrapper
 from threading import RLock
 
 import werkzeug.utils
-from werkzeug.exceptions import NotFound
 from werkzeug.routing import BuildError
 from werkzeug.urls import url_quote
 
@@ -188,7 +186,7 @@ def make_response(*args: t.Any) -> "Response":
         return current_app.response_class()
     if len(args) == 1:
         args = args[0]
-    return current_app.make_response(args)
+    return current_app.make_response(args)  # type: ignore
 
 
 def url_for(endpoint: str, **values: t.Any) -> str:
@@ -454,7 +452,7 @@ def _prepare_send_file_kwargs(
         warnings.warn(
             "The 'attachment_filename' parameter has been renamed to"
             " 'download_name'. The old name will be removed in Flask"
-            " 2.1.",
+            " 2.2.",
             DeprecationWarning,
             stacklevel=3,
         )
@@ -463,7 +461,7 @@ def _prepare_send_file_kwargs(
     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.1.",
+            " 'max_age'. The old name will be removed in Flask 2.2.",
             DeprecationWarning,
             stacklevel=3,
         )
@@ -472,7 +470,7 @@ def _prepare_send_file_kwargs(
     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.1.",
+            " old name will be removed in Flask 2.2.",
             DeprecationWarning,
             stacklevel=3,
         )
@@ -627,29 +625,6 @@ def send_file(
     )
 
 
-def safe_join(directory: str, *pathnames: str) -> str:
-    """Safely join zero or more untrusted path components to a base
-    directory to avoid escaping the base directory.
-
-    :param directory: The trusted base directory.
-    :param pathnames: The untrusted path components relative to the
-        base directory.
-    :return: A safe path, otherwise ``None``.
-    """
-    warnings.warn(
-        "'flask.helpers.safe_join' is deprecated and will be removed in"
-        " Flask 2.1. Use 'werkzeug.utils.safe_join' instead.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-    path = werkzeug.utils.safe_join(directory, *pathnames)
-
-    if path is None:
-        raise NotFound()
-
-    return path
-
-
 def send_from_directory(
     directory: t.Union[os.PathLike, str],
     path: t.Union[os.PathLike, str],
@@ -674,7 +649,8 @@ def send_from_directory(
     If the final path does not point to an existing regular file,
     raises a 404 :exc:`~werkzeug.exceptions.NotFound` error.
 
-    :param directory: The directory that ``path`` must be located under.
+    :param directory: The directory that ``path`` must be located under,
+        relative to the current application's root path.
     :param path: The path to the file to send, relative to
         ``directory``.
     :param kwargs: Arguments to pass to :func:`send_file`.
@@ -691,7 +667,7 @@ def send_from_directory(
     if filename is not None:
         warnings.warn(
             "The 'filename' parameter has been renamed to 'path'. The"
-            " old name will be removed in Flask 2.1.",
+            " old name will be removed in Flask 2.2.",
             DeprecationWarning,
             stacklevel=2,
         )
@@ -785,27 +761,6 @@ class locked_cached_property(werkzeug.utils.cached_property):
             super().__delete__(obj)
 
 
-def total_seconds(td: timedelta) -> int:
-    """Returns the total seconds from a timedelta object.
-
-    :param timedelta td: the timedelta to be converted in seconds
-
-    :returns: number of seconds
-    :rtype: int
-
-    .. deprecated:: 2.0
-        Will be removed in Flask 2.1. Use
-        :meth:`timedelta.total_seconds` instead.
-    """
-    warnings.warn(
-        "'total_seconds' is deprecated and will be removed in Flask"
-        " 2.1. Use 'timedelta.total_seconds' instead.",
-        DeprecationWarning,
-        stacklevel=2,
-    )
-    return td.days * 60 * 60 * 24 + td.seconds
-
-
 def is_ip(value: str) -> bool:
     """Determine if the given string is an IP address.
 

+ 8 - 67
contrib/python/Flask/py3/flask/json/__init__.py

@@ -1,9 +1,8 @@
+import dataclasses
 import decimal
-import io
 import json as _json
 import typing as t
 import uuid
-import warnings
 from datetime import date
 
 from jinja2.utils import htmlsafe_json_dumps as _jinja_htmlsafe_dumps
@@ -16,12 +15,6 @@ if t.TYPE_CHECKING:
     from ..app import Flask
     from ..wrappers import Response
 
-try:
-    import dataclasses
-except ImportError:
-    # Python < 3.7
-    dataclasses = None  # type: ignore
-
 
 class JSONEncoder(_json.JSONEncoder):
     """The default JSON encoder. Handles extra types compared to the
@@ -30,6 +23,7 @@ class JSONEncoder(_json.JSONEncoder):
     -   :class:`datetime.datetime` and :class:`datetime.date` are
         serialized to :rfc:`822` strings. This is the same as the HTTP
         date format.
+    -   :class:`decimal.Decimal` is serialized to a string.
     -   :class:`uuid.UUID` is serialized to a string.
     -   :class:`dataclasses.dataclass` is passed to
         :func:`dataclasses.asdict`.
@@ -135,20 +129,7 @@ def dumps(obj: t.Any, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> str:
         context for configuration.
     """
     _dump_arg_defaults(kwargs, app=app)
-    encoding = kwargs.pop("encoding", None)
-    rv = _json.dumps(obj, **kwargs)
-
-    if encoding is not None:
-        warnings.warn(
-            "'encoding' is deprecated and will be removed in Flask 2.1.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-        if isinstance(rv, str):
-            return rv.encode(encoding)  # type: ignore
-
-    return rv
+    return _json.dumps(obj, **kwargs)
 
 
 def dump(
@@ -170,27 +151,14 @@ def dump(
         deprecated and will be removed in Flask 2.1.
     """
     _dump_arg_defaults(kwargs, app=app)
-    encoding = kwargs.pop("encoding", None)
-    show_warning = encoding is not None
-
-    try:
-        fp.write("")
-    except TypeError:
-        show_warning = True
-        fp = io.TextIOWrapper(fp, encoding or "utf-8")  # type: ignore
-
-    if show_warning:
-        warnings.warn(
-            "Writing to a binary file, and the 'encoding' argument, is"
-            " deprecated and will be removed in Flask 2.1.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
     _json.dump(obj, fp, **kwargs)
 
 
-def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
+def loads(
+    s: t.Union[str, bytes],
+    app: t.Optional["Flask"] = None,
+    **kwargs: t.Any,
+) -> t.Any:
     """Deserialize an object from a string of JSON.
 
     Takes the same arguments as the built-in :func:`json.loads`, with
@@ -210,19 +178,6 @@ def loads(s: str, app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.Any:
         context for configuration.
     """
     _load_arg_defaults(kwargs, app=app)
-    encoding = kwargs.pop("encoding", None)
-
-    if encoding is not None:
-        warnings.warn(
-            "'encoding' is deprecated and will be removed in Flask 2.1."
-            " The data must be a string or UTF-8 bytes.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-        if isinstance(s, bytes):
-            s = s.decode(encoding)
-
     return _json.loads(s, **kwargs)
 
 
@@ -242,20 +197,6 @@ def load(fp: t.IO[str], app: t.Optional["Flask"] = None, **kwargs: t.Any) -> t.A
         file must be text mode, or binary mode with UTF-8 bytes.
     """
     _load_arg_defaults(kwargs, app=app)
-    encoding = kwargs.pop("encoding", None)
-
-    if encoding is not None:
-        warnings.warn(
-            "'encoding' is deprecated and will be removed in Flask 2.1."
-            " The file must be text mode, or binary mode with UTF-8"
-            " bytes.",
-            DeprecationWarning,
-            stacklevel=2,
-        )
-
-        if isinstance(fp.read(0), bytes):
-            fp = io.TextIOWrapper(fp, encoding)  # type: ignore
-
     return _json.load(fp, **kwargs)
 
 

Some files were not shown because too many files changed in this diff