Skip to content

Commit

Permalink
Merge pull request #2297 from HndrkMkt/#2265-auto-detect-create-app-f…
Browse files Browse the repository at this point in the history
…unction

Auto-detect create_app() in find_best_app() #2265
  • Loading branch information
davidism authored May 22, 2017
2 parents a693f22 + 01ddf54 commit 0c94908
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Major release, unreleased
- Allow IP address as exact session cookie domain. (`#2282`_)
- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``.
(`#2282`_)
- Auto-detect zero-argument app factory called ``create_app`` or ``make_app``
from ``FLASK_APP``. (`#2297`_)

.. _#1489: https://github.com/pallets/flask/pull/1489
.. _#1898: https://github.com/pallets/flask/pull/1898
Expand All @@ -50,6 +52,7 @@ Major release, unreleased
.. _#2256: https://github.com/pallets/flask/pull/2256
.. _#2259: https://github.com/pallets/flask/pull/2259
.. _#2282: https://github.com/pallets/flask/pull/2282
.. _#2297: https://github.com/pallets/flask/pull/2297

Version 0.12.2
--------------
Expand Down
43 changes: 35 additions & 8 deletions flask/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,48 @@ def find_best_app(module):
from . import Flask

# Search for the most common names first.
for attr_name in 'app', 'application':
for attr_name in ('app', 'application'):
app = getattr(module, attr_name, None)
if app is not None and isinstance(app, Flask):
if isinstance(app, Flask):
return app

# Otherwise find the only object that is a Flask instance.
matches = [v for k, v in iteritems(module.__dict__)
if isinstance(v, Flask)]
matches = [
v for k, v in iteritems(module.__dict__) if isinstance(v, Flask)
]

if len(matches) == 1:
return matches[0]
raise NoAppException('Failed to find application in module "%s". Are '
'you sure it contains a Flask application? Maybe '
'you wrapped it in a WSGI middleware or you are '
'using a factory function.' % module.__name__)
elif len(matches) > 1:
raise NoAppException(
'Auto-detected multiple Flask applications in module "{module}".'
' Use "FLASK_APP={module}:name" to specify the correct'
' one.'.format(module=module.__name__)
)

# Search for app factory callables.
for attr_name in ('create_app', 'make_app'):
app_factory = getattr(module, attr_name, None)

if callable(app_factory):
try:
app = app_factory()

if isinstance(app, Flask):
return app
except TypeError:
raise NoAppException(
'Auto-detected "{callable}()" in module "{module}", but '
'could not call it without specifying arguments.'.format(
callable=attr_name, module=module.__name__
)
)

raise NoAppException(
'Failed to find application in module "{module}". Are you sure '
'it contains a Flask application? Maybe you wrapped it in a WSGI '
'middleware.'.format(module=module.__name__)
)


def prepare_exec_for_file(filename):
Expand Down
34 changes: 34 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,34 @@ class Module:
myapp = Flask('appname')
assert find_best_app(Module) == Module.myapp

class Module:
@staticmethod
def create_app():
return Flask('appname')
assert isinstance(find_best_app(Module), Flask)
assert find_best_app(Module).name == 'appname'

class Module:
@staticmethod
def make_app():
return Flask('appname')
assert isinstance(find_best_app(Module), Flask)
assert find_best_app(Module).name == 'appname'

class Module:
myapp = Flask('appname1')
@staticmethod
def create_app():
return Flask('appname2')
assert find_best_app(Module) == Module.myapp

class Module:
myapp = Flask('appname1')
@staticmethod
def create_app(foo):
return Flask('appname2')
assert find_best_app(Module) == Module.myapp

class Module:
pass
pytest.raises(NoAppException, find_best_app, Module)
Expand All @@ -60,6 +88,12 @@ class Module:
myapp2 = Flask('appname2')
pytest.raises(NoAppException, find_best_app, Module)

class Module:
@staticmethod
def create_app(foo):
return Flask('appname2')
pytest.raises(NoAppException, find_best_app, Module)


def test_prepare_exec_for_file(test_apps):
"""Expect the correct path to be set and the correct module name to be returned.
Expand Down

0 comments on commit 0c94908

Please sign in to comment.