forked from orbit-oss/flask
Handle app factory with arguments in FLASK_APP
This commit is contained in:
parent
a4ae7f4d3c
commit
7106fb6357
3 changed files with 84 additions and 15 deletions
52
flask/cli.py
52
flask/cli.py
|
|
@ -9,7 +9,10 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import ast
|
||||
import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from functools import update_wrapper
|
||||
|
|
@ -55,20 +58,20 @@ def find_best_app(script_info, module):
|
|||
' one.'.format(module=module.__name__)
|
||||
)
|
||||
|
||||
# Search for app factory callables.
|
||||
# Search for app factory functions.
|
||||
for attr_name in ('create_app', 'make_app'):
|
||||
app_factory = getattr(module, attr_name, None)
|
||||
|
||||
if callable(app_factory):
|
||||
if inspect.isfunction(app_factory):
|
||||
try:
|
||||
app = call_factory(app_factory, script_info)
|
||||
if isinstance(app, Flask):
|
||||
return app
|
||||
except TypeError:
|
||||
raise NoAppException(
|
||||
'Auto-detected "{callable}()" in module "{module}", but '
|
||||
'Auto-detected "{function}()" in module "{module}", but '
|
||||
'could not call it without specifying arguments.'.format(
|
||||
callable=attr_name, module=module.__name__
|
||||
function=attr_name, module=module.__name__
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -150,10 +153,45 @@ def locate_app(script_info, app_id):
|
|||
if app_obj is None:
|
||||
app = find_best_app(script_info, mod)
|
||||
else:
|
||||
app = getattr(mod, app_obj, None)
|
||||
function_regex = r'^([\w_][\w_\d]*)\((.*)\)$'
|
||||
match = re.match(function_regex, app_obj)
|
||||
try:
|
||||
if match:
|
||||
function_name = match.group(1)
|
||||
arguments = match.group(2)
|
||||
if arguments:
|
||||
arguments = ast.literal_eval(
|
||||
"({arguments}, )".format(arguments=arguments))
|
||||
else:
|
||||
arguments = ()
|
||||
app_factory = getattr(mod, function_name, None)
|
||||
app_factory_arg_names = getargspec(app_factory).args
|
||||
if 'script_info' in app_factory_arg_names:
|
||||
app = app_factory(*arguments, script_info=script_info)
|
||||
elif arguments:
|
||||
app = app_factory(*arguments)
|
||||
elif not arguments and len(app_factory_arg_names) == 1:
|
||||
app = app_factory(script_info)
|
||||
else:
|
||||
app = app_factory()
|
||||
else:
|
||||
attr = getattr(mod, app_obj, None)
|
||||
if inspect.isfunction(attr):
|
||||
app = call_factory(attr, script_info)
|
||||
else:
|
||||
app = attr
|
||||
except TypeError as e:
|
||||
new_error = NoAppException(
|
||||
'{e}\nThe app factory "{factory}" in module "{module}" could'
|
||||
' not be called with the specified arguments (and a'
|
||||
' script_info argument automatically added if applicable).'
|
||||
' Did you make sure to use the right number of arguments as'
|
||||
' well as not using keyword arguments or'
|
||||
' non-literals?'.format(e=e, factory=app_obj, module=module))
|
||||
reraise(NoAppException, new_error, sys.exc_info()[2])
|
||||
if app is None:
|
||||
raise RuntimeError('Failed to find application in module "%s"'
|
||||
% module)
|
||||
raise RuntimeError('Failed to find application in module '
|
||||
'"{name}"'.format(name=module))
|
||||
|
||||
return app
|
||||
|
||||
|
|
|
|||
15
tests/test_apps/cliapp/factory.py
Normal file
15
tests/test_apps/cliapp/factory.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from __future__ import absolute_import, print_function
|
||||
|
||||
from flask import Flask
|
||||
|
||||
|
||||
def create_app():
|
||||
return Flask('create_app')
|
||||
|
||||
|
||||
def create_app2(foo, bar):
|
||||
return Flask("_".join(['create_app2', foo, bar]))
|
||||
|
||||
|
||||
def create_app3(foo, bar, script_info):
|
||||
return Flask("_".join(['create_app3', foo, bar]))
|
||||
|
|
@ -150,15 +150,31 @@ def test_locate_app(test_apps):
|
|||
script_info = ScriptInfo()
|
||||
assert locate_app(script_info, "cliapp.app").name == "testapp"
|
||||
assert locate_app(script_info, "cliapp.app:testapp").name == "testapp"
|
||||
assert locate_app(script_info, "cliapp.factory").name == "create_app"
|
||||
assert locate_app(
|
||||
script_info, "cliapp.factory").name == "create_app"
|
||||
assert locate_app(
|
||||
script_info, "cliapp.factory:create_app").name == "create_app"
|
||||
assert locate_app(
|
||||
script_info, "cliapp.factory:create_app()").name == "create_app"
|
||||
assert locate_app(
|
||||
script_info, "cliapp.factory:create_app2('foo', 'bar')"
|
||||
).name == "create_app2_foo_bar"
|
||||
assert locate_app(
|
||||
script_info, "cliapp.factory:create_app3('baz', 'qux')"
|
||||
).name == "create_app3_baz_qux"
|
||||
assert locate_app(script_info, "cliapp.multiapp:app1").name == "app1"
|
||||
pytest.raises(NoAppException, locate_app,
|
||||
script_info, "notanpp.py")
|
||||
pytest.raises(NoAppException, locate_app,
|
||||
script_info, "cliapp/app")
|
||||
pytest.raises(RuntimeError, locate_app,
|
||||
script_info, "cliapp.app:notanapp")
|
||||
pytest.raises(NoAppException, locate_app,
|
||||
script_info, "cliapp.importerrorapp")
|
||||
pytest.raises(
|
||||
NoAppException, locate_app, script_info, "notanpp.py")
|
||||
pytest.raises(
|
||||
NoAppException, locate_app, script_info, "cliapp/app")
|
||||
pytest.raises(
|
||||
RuntimeError, locate_app, script_info, "cliapp.app:notanapp")
|
||||
pytest.raises(
|
||||
NoAppException, locate_app,
|
||||
script_info, "cliapp.factory:create_app2('foo')")
|
||||
pytest.raises(
|
||||
NoAppException, locate_app, script_info, "cliapp.importerrorapp")
|
||||
|
||||
|
||||
def test_find_default_import_path(test_apps, monkeypatch, tmpdir):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue