remove deprecated script_info factory arg

This commit is contained in:
David Lord 2021-11-12 06:46:54 -08:00
parent ea93a52d7d
commit e21e003f62
No known key found for this signature in database
GPG Key ID: 7A1C87E3F5BC42A8
3 changed files with 34 additions and 102 deletions

View File

@ -5,7 +5,11 @@ Version 2.1.0
Unreleased 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 Version 2.0.2

View File

@ -5,7 +5,6 @@ import platform
import re import re
import sys import sys
import traceback import traceback
import warnings
from functools import update_wrapper from functools import update_wrapper
from operator import attrgetter from operator import attrgetter
from threading import Lock from threading import Lock
@ -34,7 +33,7 @@ class NoAppException(click.UsageError):
"""Raised if an application cannot be found or loaded.""" """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 """Given a module instance this tries to find the best possible
application in the module or raises an exception. application in the module or raises an exception.
""" """
@ -65,7 +64,7 @@ def find_best_app(script_info, module):
if inspect.isfunction(app_factory): if inspect.isfunction(app_factory):
try: try:
app = call_factory(script_info, app_factory) app = app_factory()
if isinstance(app, Flask): if isinstance(app, Flask):
return app 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): def _called_with_wrong_args(f):
"""Check whether calling a function raised a ``TypeError`` because """Check whether calling a function raised a ``TypeError`` because
the call failed or because something in the factory raised the the call failed or because something in the factory raised the
@ -149,7 +112,7 @@ def _called_with_wrong_args(f):
del tb 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 """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. 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): if isinstance(expr, ast.Name):
name = expr.id name = expr.id
args = kwargs = None args = []
kwargs = {}
elif isinstance(expr, ast.Call): elif isinstance(expr, ast.Call):
# Ensure the function name is an attribute name only. # Ensure the function name is an attribute name only.
if not isinstance(expr.func, ast.Name): 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. # to get the real application.
if inspect.isfunction(attr): if inspect.isfunction(attr):
try: try:
app = call_factory(script_info, attr, args, kwargs) app = attr(*args, **kwargs)
except TypeError as e: except TypeError as e:
if not _called_with_wrong_args(attr): if not _called_with_wrong_args(attr):
raise raise
@ -253,7 +217,7 @@ def prepare_import(path):
return ".".join(module_name[::-1]) 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 __traceback_hide__ = True # noqa: F841
try: try:
@ -273,9 +237,9 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
module = sys.modules[module_name] module = sys.modules[module_name]
if app_name is None: if app_name is None:
return find_best_app(script_info, module) return find_best_app(module)
else: 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): def get_version(ctx, param, value):
@ -396,18 +360,18 @@ class ScriptInfo:
return self._loaded_app return self._loaded_app
if self.create_app is not None: if self.create_app is not None:
app = call_factory(self, self.create_app) app = self.create_app()
else: else:
if self.app_import_path: if self.app_import_path:
path, name = ( path, name = (
re.split(r":(?![\\/])", self.app_import_path, 1) + [None] re.split(r":(?![\\/])", self.app_import_path, 1) + [None]
)[:2] )[:2]
import_name = prepare_import(path) import_name = prepare_import(path)
app = locate_app(self, import_name, name) app = locate_app(import_name, name)
else: else:
for path in ("wsgi.py", "app.py"): for path in ("wsgi.py", "app.py"):
import_name = prepare_import(path) 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: if app:
break break
@ -983,15 +947,7 @@ debug mode.
def main() -> None: def main() -> None:
if int(click.__version__[0]) < 8: cli.main()
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:])
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -49,29 +49,27 @@ def test_cli_name(test_apps):
def test_find_best_app(test_apps): def test_find_best_app(test_apps):
script_info = ScriptInfo()
class Module: class Module:
app = Flask("appname") app = Flask("appname")
assert find_best_app(script_info, Module) == Module.app assert find_best_app(Module) == Module.app
class Module: class Module:
application = Flask("appname") application = Flask("appname")
assert find_best_app(script_info, Module) == Module.application assert find_best_app(Module) == Module.application
class Module: class Module:
myapp = Flask("appname") myapp = Flask("appname")
assert find_best_app(script_info, Module) == Module.myapp assert find_best_app(Module) == Module.myapp
class Module: class Module:
@staticmethod @staticmethod
def create_app(): def create_app():
return Flask("appname") return Flask("appname")
app = find_best_app(script_info, Module) app = find_best_app(Module)
assert isinstance(app, Flask) assert isinstance(app, Flask)
assert app.name == "appname" assert app.name == "appname"
@ -80,29 +78,7 @@ def test_find_best_app(test_apps):
def create_app(**kwargs): def create_app(**kwargs):
return Flask("appname") return Flask("appname")
app = find_best_app(script_info, Module) app = find_best_app(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)
assert isinstance(app, Flask) assert isinstance(app, Flask)
assert app.name == "appname" assert app.name == "appname"
@ -111,7 +87,7 @@ def test_find_best_app(test_apps):
def make_app(): def make_app():
return Flask("appname") return Flask("appname")
app = find_best_app(script_info, Module) app = find_best_app(Module)
assert isinstance(app, Flask) assert isinstance(app, Flask)
assert app.name == "appname" assert app.name == "appname"
@ -122,7 +98,7 @@ def test_find_best_app(test_apps):
def create_app(): def create_app():
return Flask("appname2") return Flask("appname2")
assert find_best_app(script_info, Module) == Module.myapp assert find_best_app(Module) == Module.myapp
class Module: class Module:
myapp = Flask("appname1") myapp = Flask("appname1")
@ -131,32 +107,32 @@ def test_find_best_app(test_apps):
def create_app(): def create_app():
return Flask("appname2") return Flask("appname2")
assert find_best_app(script_info, Module) == Module.myapp assert find_best_app(Module) == Module.myapp
class Module: class Module:
pass pass
pytest.raises(NoAppException, find_best_app, script_info, Module) pytest.raises(NoAppException, find_best_app, Module)
class Module: class Module:
myapp1 = Flask("appname1") myapp1 = Flask("appname1")
myapp2 = Flask("appname2") myapp2 = Flask("appname2")
pytest.raises(NoAppException, find_best_app, script_info, Module) pytest.raises(NoAppException, find_best_app, Module)
class Module: class Module:
@staticmethod @staticmethod
def create_app(foo, bar): def create_app(foo, bar):
return Flask("appname2") return Flask("appname2")
pytest.raises(NoAppException, find_best_app, script_info, Module) pytest.raises(NoAppException, find_best_app, Module)
class Module: class Module:
@staticmethod @staticmethod
def create_app(): def create_app():
raise TypeError("bad bad factory!") raise TypeError("bad bad factory!")
pytest.raises(TypeError, find_best_app, script_info, Module) pytest.raises(TypeError, find_best_app, Module)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -220,8 +196,7 @@ def test_prepare_import(request, value, path, result):
), ),
) )
def test_locate_app(test_apps, iname, aname, result): def test_locate_app(test_apps, iname, aname, result):
info = ScriptInfo() assert locate_app(iname, aname).name == result
assert locate_app(info, iname, aname).name == result
@pytest.mark.parametrize( @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): def test_locate_app_raises(test_apps, iname, aname):
info = ScriptInfo()
with pytest.raises(NoAppException): with pytest.raises(NoAppException):
locate_app(info, iname, aname) locate_app(iname, aname)
def test_locate_app_suppress_raise(test_apps): def test_locate_app_suppress_raise(test_apps):
info = ScriptInfo() app = locate_app("notanapp.py", None, raise_if_not_found=False)
app = locate_app(info, "notanapp.py", None, raise_if_not_found=False)
assert app is None assert app is None
# only direct import error is suppressed # only direct import error is suppressed
with pytest.raises(NoAppException): 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): def test_get_version(test_apps, capsys):