-
-
Notifications
You must be signed in to change notification settings - Fork 16.7k
Description
Description
My create_app factory accepts optional variable length keyword arguments:
def create_app(**kwargs):
pass
We use these to optionally inject pre-initialized service objects into the application, usually for testing purposes. This worked through Flask 1.1.4, but breaks due to changes regarding the script_info parameter made in 2.0.0.
Error: Detected factory 'create_app' in module 'service.main', but could not call it without arguments. Use "FLASK_APP='service.main:create_app(args)'" to specify arguments.
The buggy logic is in call_factory; inspect considers the default value for a **kwargs argument to be empty. This causes the flask code to inject script_info as a positional argument, despite the function signature not accepting any positional args. A check needs to be added to exclude variable length kwargs, for example:
if (
not args
and len(sig.parameters) == 1
and next(iter(sig.parameters.values())).default is inspect.Parameter.empty
and next(iter(sig.parameters.values())).kind is not inspect.Parameter.VAR_KEYWORD
):
Test Case
Here's a test case that reproduces the bug:
class Module:
@staticmethod
def create_app(**kwargs):
return Flask("appname")
app = find_best_app(script_info, Module)
assert isinstance(app, Flask)
assert app.name == "appname"
I was hoping to submit a simple patch for this issue, but the change I described above breaks tests for auto_reload and debug in ways that I don't quite understand.
src/flask/cli.py:75: NoAppException
___ test_flaskgroup_debug[True] ___
runner = <click.testing.CliRunner object at 0x10589f580>, set_debug_flag = True
@pytest.mark.parametrize("set_debug_flag", (True, False))
def test_flaskgroup_debug(runner, set_debug_flag):
def create_app():
app = Flask("flaskgroup")
app.debug = True
return app
@click.group(cls=FlaskGroup, create_app=create_app, set_debug_flag=set_debug_flag)
def cli(**params):
pass
@cli.command()
def test():
click.echo(str(current_app.debug))
result = runner.invoke(cli, ["test"])
assert result.exit_code == 0
> assert result.output == f"{not set_debug_flag}\n"
E AssertionError: assert 'True\n' == 'False\n'
E - False
E + True
tests/test_cli.py:406: AssertionError
___ test_templates_auto_reload_debug_run ___
app = <Flask 'flask_test'>, monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x1057ad9d0>
def test_templates_auto_reload_debug_run(app, monkeypatch):
def run_simple_mock(*args, **kwargs):
pass
monkeypatch.setattr(werkzeug.serving, "run_simple", run_simple_mock)
app.run()
> assert not app.templates_auto_reload
E AssertionError: assert not True
E + where True = <Flask 'flask_test'>.templates_auto_reload
Environment
- Python version: 3.8.6
- Flask version: 2.0.1