diff --git a/flask/cli.py b/flask/cli.py index 3440d981..a812ae2e 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -77,6 +77,8 @@ def find_best_app(script_info, module): if isinstance(app, Flask): return app except TypeError: + if not _called_with_wrong_args(app_factory): + raise raise NoAppException( 'Detected factory "{factory}" in module "{module}", but ' 'could not call it without arguments. Use ' @@ -113,6 +115,30 @@ def call_factory(script_info, app_factory, arguments=()): return app_factory() +def _called_with_wrong_args(factory): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param factory: the factory function that was called + :return: true if the call failed + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is factory.__code__: + # in the factory, it was called successfully + return False + + tb = tb.tb_next + + # didn't reach the factory + return True + finally: + del tb + + def find_app_by_string(script_info, module, app_name): """Checks if the given string is a variable name or a function. If it is a function, it checks for specified arguments and whether it takes a @@ -150,6 +176,9 @@ def find_app_by_string(script_info, module, app_name): try: app = call_factory(script_info, attr, args) except TypeError as e: + if not _called_with_wrong_args(attr): + raise + raise NoAppException( '{e}\nThe factory "{app_name}" in module "{module}" could not ' 'be called with the specified arguments.'.format( diff --git a/tests/test_cli.py b/tests/test_cli.py index f1e5eba7..3ffb3034 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -135,6 +135,13 @@ def test_find_best_app(test_apps): pytest.raises(NoAppException, find_best_app, script_info, Module) + class Module: + @staticmethod + def create_app(): + raise TypeError('bad bad factory!') + + pytest.raises(TypeError, find_best_app, script_info, Module) + @pytest.mark.parametrize('value,path,result', ( ('test', cwd, 'test'),