From e21e003f62b57c35e1832c0bf14e9cd27f4e5bea Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 06:46:54 -0800 Subject: [PATCH 1/7] remove deprecated script_info factory arg --- CHANGES.rst | 6 +++- src/flask/cli.py | 70 +++++++++-------------------------------------- tests/test_cli.py | 60 +++++++++++----------------------------- 3 files changed, 34 insertions(+), 102 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6e092adc..2ef2f0e5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,7 +5,11 @@ Version 2.1.0 Unreleased -- Update Click dependency to >= 8.0. +- Drop support for Python 3.6. :pr:`4335` +- Update Click dependency to >= 8.0. :pr:`4008` +- Remove previously deprecated code. :pr:`4337` + + - The CLI does not pass ``script_info`` to app factory functions. Version 2.0.2 diff --git a/src/flask/cli.py b/src/flask/cli.py index 7ab4fa1c..64e103fc 100644 --- a/src/flask/cli.py +++ b/src/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 @@ -34,7 +33,7 @@ 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 +64,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 +86,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 +112,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 +129,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 +166,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 +217,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: @@ -273,9 +237,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): @@ -396,18 +360,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 @@ -983,15 +947,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__": diff --git a/tests/test_cli.py b/tests/test_cli.py index 08ba4800..a2fcc5a1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -49,29 +49,27 @@ def test_cli_name(test_apps): def test_find_best_app(test_apps): - script_info = ScriptInfo() - class Module: app = Flask("appname") - assert find_best_app(script_info, Module) == Module.app + assert find_best_app(Module) == Module.app class Module: application = Flask("appname") - assert find_best_app(script_info, Module) == Module.application + assert find_best_app(Module) == Module.application class Module: myapp = Flask("appname") - assert find_best_app(script_info, Module) == Module.myapp + assert find_best_app(Module) == Module.myapp class Module: @staticmethod def create_app(): return Flask("appname") - app = find_best_app(script_info, Module) + app = find_best_app(Module) assert isinstance(app, Flask) assert app.name == "appname" @@ -80,29 +78,7 @@ def test_find_best_app(test_apps): def create_app(**kwargs): return Flask("appname") - app = find_best_app(script_info, Module) - assert isinstance(app, Flask) - assert app.name == "appname" - - class Module: - @staticmethod - def create_app(foo): - return Flask("appname") - - with pytest.deprecated_call(match="Script info"): - app = find_best_app(script_info, Module) - - assert isinstance(app, Flask) - assert app.name == "appname" - - class Module: - @staticmethod - def create_app(foo=None, script_info=None): - return Flask("appname") - - with pytest.deprecated_call(match="script_info"): - app = find_best_app(script_info, Module) - + app = find_best_app(Module) assert isinstance(app, Flask) assert app.name == "appname" @@ -111,7 +87,7 @@ def test_find_best_app(test_apps): def make_app(): return Flask("appname") - app = find_best_app(script_info, Module) + app = find_best_app(Module) assert isinstance(app, Flask) assert app.name == "appname" @@ -122,7 +98,7 @@ def test_find_best_app(test_apps): def create_app(): return Flask("appname2") - assert find_best_app(script_info, Module) == Module.myapp + assert find_best_app(Module) == Module.myapp class Module: myapp = Flask("appname1") @@ -131,32 +107,32 @@ def test_find_best_app(test_apps): def create_app(): return Flask("appname2") - assert find_best_app(script_info, Module) == Module.myapp + assert find_best_app(Module) == Module.myapp class Module: pass - pytest.raises(NoAppException, find_best_app, script_info, Module) + pytest.raises(NoAppException, find_best_app, Module) class Module: myapp1 = Flask("appname1") myapp2 = Flask("appname2") - pytest.raises(NoAppException, find_best_app, script_info, Module) + pytest.raises(NoAppException, find_best_app, Module) class Module: @staticmethod def create_app(foo, bar): return Flask("appname2") - pytest.raises(NoAppException, find_best_app, script_info, Module) + pytest.raises(NoAppException, find_best_app, Module) class Module: @staticmethod def create_app(): raise TypeError("bad bad factory!") - pytest.raises(TypeError, find_best_app, script_info, Module) + pytest.raises(TypeError, find_best_app, Module) @pytest.mark.parametrize( @@ -220,8 +196,7 @@ def test_prepare_import(request, value, path, result): ), ) def test_locate_app(test_apps, iname, aname, result): - info = ScriptInfo() - assert locate_app(info, iname, aname).name == result + assert locate_app(iname, aname).name == result @pytest.mark.parametrize( @@ -243,20 +218,17 @@ def test_locate_app(test_apps, iname, aname, result): ), ) def test_locate_app_raises(test_apps, iname, aname): - info = ScriptInfo() - with pytest.raises(NoAppException): - locate_app(info, iname, aname) + locate_app(iname, aname) def test_locate_app_suppress_raise(test_apps): - info = ScriptInfo() - app = locate_app(info, "notanapp.py", None, raise_if_not_found=False) + app = locate_app("notanapp.py", None, raise_if_not_found=False) assert app is None # only direct import error is suppressed with pytest.raises(NoAppException): - locate_app(info, "cliapp.importerrorapp", None, raise_if_not_found=False) + locate_app("cliapp.importerrorapp", None, raise_if_not_found=False) def test_get_version(test_apps, capsys): From 2bd7aed1a4e257b00df4282b96e7e96edc4567f6 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 06:50:57 -0800 Subject: [PATCH 2/7] remove deprecated config.from_json --- CHANGES.rst | 2 ++ src/flask/config.py | 29 +++-------------------------- 2 files changed, 5 insertions(+), 26 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2ef2f0e5..9a6a7937 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,8 @@ Unreleased - Remove previously deprecated code. :pr:`4337` - The CLI does not pass ``script_info`` to app factory functions. + - ``config.from_json`` is replaced by + ``config.from_file(name, load=json.load)``. Version 2.0.2 diff --git a/src/flask/config.py b/src/flask/config.py index ca769022..9657edc8 100644 --- a/src/flask/config.py +++ b/src/flask/config.py @@ -176,6 +176,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 +207,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: From 218534a9f20c98600e7c41321edb4063a8e158ca Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 06:59:08 -0800 Subject: [PATCH 3/7] remove deprecated json encoding parameter --- CHANGES.rst | 1 + src/flask/json/__init__.py | 61 +------------------------------------- 2 files changed, 2 insertions(+), 60 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9a6a7937..cca2284f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,7 @@ Unreleased - The CLI does not pass ``script_info`` to app factory functions. - ``config.from_json`` is replaced by ``config.from_file(name, load=json.load)``. + - ``json`` functions no longer take an ``encoding`` parameter. Version 2.0.2 diff --git a/src/flask/json/__init__.py b/src/flask/json/__init__.py index 9e553046..eac2f576 100644 --- a/src/flask/json/__init__.py +++ b/src/flask/json/__init__.py @@ -1,10 +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 @@ -124,20 +122,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( @@ -159,23 +144,6 @@ 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) @@ -199,19 +167,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) @@ -231,20 +186,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) From b7501776a12c15e64b6cdac1e3b903a00df7d285 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 09:24:32 -0800 Subject: [PATCH 4/7] remove deprecated safe_join --- CHANGES.rst | 2 ++ docs/api.rst | 2 -- src/flask/__init__.py | 1 - src/flask/helpers.py | 24 ------------------------ 4 files changed, 2 insertions(+), 27 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index cca2284f..2358e159 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,8 @@ Unreleased - ``config.from_json`` is replaced by ``config.from_file(name, load=json.load)``. - ``json`` functions no longer take an ``encoding`` parameter. + - ``safe_join`` is removed, use ``werkzeug.utils.safe_join`` + instead. Version 2.0.2 diff --git a/docs/api.rst b/docs/api.rst index 09fc71a9..5eb8b693 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -218,8 +218,6 @@ Useful Functions and Classes .. autofunction:: send_from_directory -.. autofunction:: safe_join - .. autofunction:: escape .. autoclass:: Markup diff --git a/src/flask/__init__.py b/src/flask/__init__.py index 3e7198a6..e6987709 100644 --- a/src/flask/__init__.py +++ b/src/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 diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 7b8b0870..df6a14cb 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -11,7 +11,6 @@ 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 @@ -627,29 +626,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], From f8cdc78ce10f32a46390799e7ac16d231fb35803 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 09:25:27 -0800 Subject: [PATCH 5/7] remove deprecated total_seconds --- CHANGES.rst | 2 ++ src/flask/helpers.py | 22 ---------------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2358e159..3560dc65 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,6 +15,8 @@ Unreleased - ``json`` functions no longer take an ``encoding`` parameter. - ``safe_join`` is removed, use ``werkzeug.utils.safe_join`` instead. + - ``total_seconds`` is removed, use ``timedelta.total_seconds`` + instead. Version 2.0.2 diff --git a/src/flask/helpers.py b/src/flask/helpers.py index df6a14cb..20701e2a 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -5,7 +5,6 @@ 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 @@ -761,27 +760,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. From 48f2afbf900560af061d50c73e4d3f9422ce4dbe Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 09:37:13 -0800 Subject: [PATCH 6/7] same blueprint cannot be registered with same name --- CHANGES.rst | 2 ++ src/flask/blueprints.py | 22 ++++++---------------- tests/test_blueprints.py | 4 ++-- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3560dc65..4d6fa62d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -17,6 +17,8 @@ Unreleased instead. - ``total_seconds`` is removed, use ``timedelta.total_seconds`` instead. + - The same blueprint cannot be registered with the same name. Use + ``name=`` when registering to specify a unique name. Version 2.0.2 diff --git a/src/flask/blueprints.py b/src/flask/blueprints.py index 5c23a735..47465ad7 100644 --- a/src/flask/blueprints.py +++ b/src/flask/blueprints.py @@ -299,24 +299,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 diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index e02cd4be..fbe9eeee 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -954,8 +954,8 @@ def test_unique_blueprint_names(app, client) -> None: app.register_blueprint(bp) - with pytest.warns(UserWarning): - app.register_blueprint(bp) # same bp, same name, warning + with pytest.raises(ValueError): + app.register_blueprint(bp) # same bp, same name, error app.register_blueprint(bp, name="again") # same bp, different name, ok From 15a3e82823c894185771486510027a65295bcf94 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 12 Nov 2021 09:21:09 -0800 Subject: [PATCH 7/7] extend deprecation for renamed send_file params --- CHANGES.rst | 9 +++++++++ src/flask/helpers.py | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4d6fa62d..7c8d279b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,15 @@ Unreleased - The same blueprint cannot be registered with the same name. Use ``name=`` when registering to specify a unique name. +- Some parameters in ``send_file`` and ``send_from_directory`` were + renamed in 2.0. The deprecation period for the old names is extended + to 2.2. Be sure to test with deprecation warnings visible. + + - ``attachment_filename`` is renamed to ``download_name``. + - ``cache_timeout`` is renamed to ``max_age``. + - ``add_etags`` is renamed to ``etag``. + - ``filename`` is renamed to ``path``. + Version 2.0.2 ------------- diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 20701e2a..d67e59e2 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -452,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, ) @@ -461,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, ) @@ -470,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, ) @@ -666,7 +666,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, )