forked from orbit-oss/flask
Merge pull request #2319 from HndrkMkt/#2266-support-create-app-without-script-info
Support `create_app` without script_info or with script_info as named argument
This commit is contained in:
commit
39f7aaa416
4 changed files with 69 additions and 28 deletions
5
CHANGES
5
CHANGES
|
|
@ -42,6 +42,10 @@ Major release, unreleased
|
||||||
(`#2282`_)
|
(`#2282`_)
|
||||||
- Auto-detect zero-argument app factory called ``create_app`` or ``make_app``
|
- Auto-detect zero-argument app factory called ``create_app`` or ``make_app``
|
||||||
from ``FLASK_APP``. (`#2297`_)
|
from ``FLASK_APP``. (`#2297`_)
|
||||||
|
- Factory functions are not required to take a ``script_info`` parameter to
|
||||||
|
work with the ``flask`` command. If they take a single parameter or a
|
||||||
|
parameter named ``script_info``, the ``ScriptInfo`` object will be passed.
|
||||||
|
(`#2319`_)
|
||||||
|
|
||||||
.. _#1489: https://github.com/pallets/flask/pull/1489
|
.. _#1489: https://github.com/pallets/flask/pull/1489
|
||||||
.. _#1898: https://github.com/pallets/flask/pull/1898
|
.. _#1898: https://github.com/pallets/flask/pull/1898
|
||||||
|
|
@ -53,6 +57,7 @@ Major release, unreleased
|
||||||
.. _#2259: https://github.com/pallets/flask/pull/2259
|
.. _#2259: https://github.com/pallets/flask/pull/2259
|
||||||
.. _#2282: https://github.com/pallets/flask/pull/2282
|
.. _#2282: https://github.com/pallets/flask/pull/2282
|
||||||
.. _#2297: https://github.com/pallets/flask/pull/2297
|
.. _#2297: https://github.com/pallets/flask/pull/2297
|
||||||
|
.. _#2319: https://github.com/pallets/flask/pull/2319
|
||||||
|
|
||||||
Version 0.12.2
|
Version 0.12.2
|
||||||
--------------
|
--------------
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ if not PY2:
|
||||||
itervalues = lambda d: iter(d.values())
|
itervalues = lambda d: iter(d.values())
|
||||||
iteritems = lambda d: iter(d.items())
|
iteritems = lambda d: iter(d.items())
|
||||||
|
|
||||||
|
from inspect import getfullargspec as getargspec
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
def reraise(tp, value, tb=None):
|
def reraise(tp, value, tb=None):
|
||||||
|
|
@ -43,6 +44,7 @@ else:
|
||||||
itervalues = lambda d: d.itervalues()
|
itervalues = lambda d: d.itervalues()
|
||||||
iteritems = lambda d: d.iteritems()
|
iteritems = lambda d: d.iteritems()
|
||||||
|
|
||||||
|
from inspect import getargspec
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
|
||||||
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||||
|
|
|
||||||
28
flask/cli.py
28
flask/cli.py
|
|
@ -22,13 +22,14 @@ from . import __version__
|
||||||
from ._compat import iteritems, reraise
|
from ._compat import iteritems, reraise
|
||||||
from .globals import current_app
|
from .globals import current_app
|
||||||
from .helpers import get_debug_flag
|
from .helpers import get_debug_flag
|
||||||
|
from ._compat import getargspec
|
||||||
|
|
||||||
|
|
||||||
class NoAppException(click.UsageError):
|
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(module):
|
def find_best_app(script_info, 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.
|
||||||
"""
|
"""
|
||||||
|
|
@ -60,8 +61,7 @@ def find_best_app(module):
|
||||||
|
|
||||||
if callable(app_factory):
|
if callable(app_factory):
|
||||||
try:
|
try:
|
||||||
app = app_factory()
|
app = call_factory(app_factory, script_info)
|
||||||
|
|
||||||
if isinstance(app, Flask):
|
if isinstance(app, Flask):
|
||||||
return app
|
return app
|
||||||
except TypeError:
|
except TypeError:
|
||||||
|
|
@ -79,6 +79,20 @@ def find_best_app(module):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def call_factory(func, script_info):
|
||||||
|
"""Checks if the given app factory function has an argument named
|
||||||
|
``script_info`` or just a single argument and calls the function passing
|
||||||
|
``script_info`` if so. Otherwise, calls the function without any arguments
|
||||||
|
and returns the result.
|
||||||
|
"""
|
||||||
|
arguments = getargspec(func).args
|
||||||
|
if 'script_info' in arguments:
|
||||||
|
return func(script_info=script_info)
|
||||||
|
elif len(arguments) == 1:
|
||||||
|
return func(script_info)
|
||||||
|
return func()
|
||||||
|
|
||||||
|
|
||||||
def prepare_exec_for_file(filename):
|
def prepare_exec_for_file(filename):
|
||||||
"""Given a filename this will try to calculate the python path, add it
|
"""Given a filename this will try to calculate the python path, add it
|
||||||
to the search path and return the actual module name that is expected.
|
to the search path and return the actual module name that is expected.
|
||||||
|
|
@ -108,7 +122,7 @@ def prepare_exec_for_file(filename):
|
||||||
return '.'.join(module[::-1])
|
return '.'.join(module[::-1])
|
||||||
|
|
||||||
|
|
||||||
def locate_app(app_id):
|
def locate_app(script_info, app_id):
|
||||||
"""Attempts to locate the application."""
|
"""Attempts to locate the application."""
|
||||||
__traceback_hide__ = True
|
__traceback_hide__ = True
|
||||||
if ':' in app_id:
|
if ':' in app_id:
|
||||||
|
|
@ -134,7 +148,7 @@ def locate_app(app_id):
|
||||||
|
|
||||||
mod = sys.modules[module]
|
mod = sys.modules[module]
|
||||||
if app_obj is None:
|
if app_obj is None:
|
||||||
app = find_best_app(mod)
|
app = find_best_app(script_info, mod)
|
||||||
else:
|
else:
|
||||||
app = getattr(mod, app_obj, None)
|
app = getattr(mod, app_obj, None)
|
||||||
if app is None:
|
if app is None:
|
||||||
|
|
@ -259,7 +273,7 @@ class ScriptInfo(object):
|
||||||
if self._loaded_app is not None:
|
if self._loaded_app is not None:
|
||||||
return self._loaded_app
|
return self._loaded_app
|
||||||
if self.create_app is not None:
|
if self.create_app is not None:
|
||||||
rv = self.create_app(self)
|
rv = call_factory(self.create_app, self)
|
||||||
else:
|
else:
|
||||||
if not self.app_import_path:
|
if not self.app_import_path:
|
||||||
raise NoAppException(
|
raise NoAppException(
|
||||||
|
|
@ -267,7 +281,7 @@ class ScriptInfo(object):
|
||||||
'the FLASK_APP environment variable.\n\nFor more '
|
'the FLASK_APP environment variable.\n\nFor more '
|
||||||
'information see '
|
'information see '
|
||||||
'http://flask.pocoo.org/docs/latest/quickstart/')
|
'http://flask.pocoo.org/docs/latest/quickstart/')
|
||||||
rv = locate_app(self.app_import_path)
|
rv = locate_app(self, self.app_import_path)
|
||||||
debug = get_debug_flag()
|
debug = get_debug_flag()
|
||||||
if debug is not None:
|
if debug is not None:
|
||||||
rv.debug = debug
|
rv.debug = debug
|
||||||
|
|
|
||||||
|
|
@ -39,60 +39,75 @@ def test_cli_name(test_apps):
|
||||||
|
|
||||||
def test_find_best_app(test_apps):
|
def test_find_best_app(test_apps):
|
||||||
"""Test if `find_best_app` behaves as expected with different combinations of input."""
|
"""Test if `find_best_app` behaves as expected with different combinations of input."""
|
||||||
|
script_info = ScriptInfo()
|
||||||
class Module:
|
class Module:
|
||||||
app = Flask('appname')
|
app = Flask('appname')
|
||||||
assert find_best_app(Module) == Module.app
|
assert find_best_app(script_info, Module) == Module.app
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
application = Flask('appname')
|
application = Flask('appname')
|
||||||
assert find_best_app(Module) == Module.application
|
assert find_best_app(script_info, Module) == Module.application
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
myapp = Flask('appname')
|
myapp = Flask('appname')
|
||||||
assert find_best_app(Module) == Module.myapp
|
assert find_best_app(script_info, Module) == Module.myapp
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_app():
|
def create_app():
|
||||||
return Flask('appname')
|
return Flask('appname')
|
||||||
assert isinstance(find_best_app(Module), Flask)
|
assert isinstance(find_best_app(script_info, Module), Flask)
|
||||||
assert find_best_app(Module).name == 'appname'
|
assert find_best_app(script_info, Module).name == 'appname'
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
@staticmethod
|
||||||
|
def create_app(foo):
|
||||||
|
return Flask('appname')
|
||||||
|
assert isinstance(find_best_app(script_info, Module), Flask)
|
||||||
|
assert find_best_app(script_info, Module).name == 'appname'
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
@staticmethod
|
||||||
|
def create_app(foo=None, script_info=None):
|
||||||
|
return Flask('appname')
|
||||||
|
assert isinstance(find_best_app(script_info, Module), Flask)
|
||||||
|
assert find_best_app(script_info, Module).name == 'appname'
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def make_app():
|
def make_app():
|
||||||
return Flask('appname')
|
return Flask('appname')
|
||||||
assert isinstance(find_best_app(Module), Flask)
|
assert isinstance(find_best_app(script_info, Module), Flask)
|
||||||
assert find_best_app(Module).name == 'appname'
|
assert find_best_app(script_info, Module).name == 'appname'
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
myapp = Flask('appname1')
|
myapp = Flask('appname1')
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_app():
|
def create_app():
|
||||||
return Flask('appname2')
|
return Flask('appname2')
|
||||||
assert find_best_app(Module) == Module.myapp
|
assert find_best_app(script_info, Module) == Module.myapp
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
myapp = Flask('appname1')
|
myapp = Flask('appname1')
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_app(foo):
|
def create_app():
|
||||||
return Flask('appname2')
|
return Flask('appname2')
|
||||||
assert find_best_app(Module) == Module.myapp
|
assert find_best_app(script_info, Module) == Module.myapp
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
pass
|
pass
|
||||||
pytest.raises(NoAppException, find_best_app, Module)
|
pytest.raises(NoAppException, find_best_app, script_info, Module)
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
myapp1 = Flask('appname1')
|
myapp1 = Flask('appname1')
|
||||||
myapp2 = Flask('appname2')
|
myapp2 = Flask('appname2')
|
||||||
pytest.raises(NoAppException, find_best_app, Module)
|
pytest.raises(NoAppException, find_best_app, script_info, Module)
|
||||||
|
|
||||||
class Module:
|
class Module:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_app(foo):
|
def create_app(foo, bar):
|
||||||
return Flask('appname2')
|
return Flask('appname2')
|
||||||
pytest.raises(NoAppException, find_best_app, Module)
|
pytest.raises(NoAppException, find_best_app, script_info, Module)
|
||||||
|
|
||||||
|
|
||||||
def test_prepare_exec_for_file(test_apps):
|
def test_prepare_exec_for_file(test_apps):
|
||||||
|
|
@ -117,13 +132,18 @@ def test_prepare_exec_for_file(test_apps):
|
||||||
|
|
||||||
def test_locate_app(test_apps):
|
def test_locate_app(test_apps):
|
||||||
"""Test of locate_app."""
|
"""Test of locate_app."""
|
||||||
assert locate_app("cliapp.app").name == "testapp"
|
script_info = ScriptInfo()
|
||||||
assert locate_app("cliapp.app:testapp").name == "testapp"
|
assert locate_app(script_info, "cliapp.app").name == "testapp"
|
||||||
assert locate_app("cliapp.multiapp:app1").name == "app1"
|
assert locate_app(script_info, "cliapp.app:testapp").name == "testapp"
|
||||||
pytest.raises(NoAppException, locate_app, "notanpp.py")
|
assert locate_app(script_info, "cliapp.multiapp:app1").name == "app1"
|
||||||
pytest.raises(NoAppException, locate_app, "cliapp/app")
|
pytest.raises(NoAppException, locate_app,
|
||||||
pytest.raises(RuntimeError, locate_app, "cliapp.app:notanapp")
|
script_info, "notanpp.py")
|
||||||
pytest.raises(NoAppException, locate_app, "cliapp.importerrorapp")
|
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")
|
||||||
|
|
||||||
|
|
||||||
def test_find_default_import_path(test_apps, monkeypatch, tmpdir):
|
def test_find_default_import_path(test_apps, monkeypatch, tmpdir):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue