|
|
|
@ -17,6 +17,7 @@ from functools import update_wrapper
|
|
|
|
|
import click |
|
|
|
|
|
|
|
|
|
from ._compat import iteritems, reraise |
|
|
|
|
from .helpers import get_debug_flag |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NoAppException(click.UsageError): |
|
|
|
@ -98,6 +99,15 @@ def locate_app(app_id):
|
|
|
|
|
return app |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def find_default_import_path(): |
|
|
|
|
app = os.environ.get('FLASK_APP') |
|
|
|
|
if app is None: |
|
|
|
|
return |
|
|
|
|
if os.path.isfile(app): |
|
|
|
|
return prepare_exec_for_file(app) |
|
|
|
|
return app |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DispatchingApp(object): |
|
|
|
|
"""Special application that dispatches to a flask application which |
|
|
|
|
is imported by name in a background thread. If an error happens |
|
|
|
@ -158,12 +168,13 @@ class ScriptInfo(object):
|
|
|
|
|
to click. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
def __init__(self, app_import_path=None, debug=None, create_app=None): |
|
|
|
|
#: The application import path |
|
|
|
|
def __init__(self, app_import_path=None, create_app=None): |
|
|
|
|
if create_app is None: |
|
|
|
|
if app_import_path is None: |
|
|
|
|
app_import_path = find_default_import_path() |
|
|
|
|
self.app_import_path = app_import_path |
|
|
|
|
#: The debug flag. If this is not None, the application will |
|
|
|
|
#: automatically have it's debug flag overridden with this value. |
|
|
|
|
self.debug = debug |
|
|
|
|
else: |
|
|
|
|
self.app_import_path = None |
|
|
|
|
#: Optionally a function that is passed the script info to create |
|
|
|
|
#: the instance of the application. |
|
|
|
|
self.create_app = create_app |
|
|
|
@ -185,11 +196,12 @@ class ScriptInfo(object):
|
|
|
|
|
else: |
|
|
|
|
if self.app_import_path is None: |
|
|
|
|
raise NoAppException('Could not locate Flask application. ' |
|
|
|
|
'You did not provide FLASK_APP or the ' |
|
|
|
|
'--app parameter.') |
|
|
|
|
'You did not provide the FLASK_APP ' |
|
|
|
|
'environment variable.') |
|
|
|
|
rv = locate_app(self.app_import_path) |
|
|
|
|
if self.debug is not None: |
|
|
|
|
rv.debug = self.debug |
|
|
|
|
debug = get_debug_flag() |
|
|
|
|
if debug is not None: |
|
|
|
|
rv.debug = debug |
|
|
|
|
self._loaded_app = rv |
|
|
|
|
return rv |
|
|
|
|
|
|
|
|
@ -210,29 +222,6 @@ def with_appcontext(f):
|
|
|
|
|
return update_wrapper(decorator, f) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_debug_value(ctx, param, value): |
|
|
|
|
ctx.ensure_object(ScriptInfo).debug = value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def set_app_value(ctx, param, value): |
|
|
|
|
if value is not None: |
|
|
|
|
if os.path.isfile(value): |
|
|
|
|
value = prepare_exec_for_file(value) |
|
|
|
|
elif '.' not in sys.path: |
|
|
|
|
sys.path.insert(0, '.') |
|
|
|
|
ctx.ensure_object(ScriptInfo).app_import_path = value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
debug_option = click.Option(['--debug/--no-debug'], |
|
|
|
|
help='Enable or disable debug mode.', |
|
|
|
|
default=None, callback=set_debug_value) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app_option = click.Option(['-a', '--app'], |
|
|
|
|
help='The application to run', |
|
|
|
|
callback=set_app_value, is_eager=True) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AppGroup(click.Group): |
|
|
|
|
"""This works similar to a regular click :class:`~click.Group` but it |
|
|
|
|
changes the behavior of the :meth:`command` decorator so that it |
|
|
|
@ -273,25 +262,12 @@ class FlaskGroup(AppGroup):
|
|
|
|
|
|
|
|
|
|
:param add_default_commands: if this is True then the default run and |
|
|
|
|
shell commands wil be added. |
|
|
|
|
:param add_app_option: adds the default :option:`--app` option. This gets |
|
|
|
|
automatically disabled if a `create_app` |
|
|
|
|
callback is defined. |
|
|
|
|
:param add_debug_option: adds the default :option:`--debug` option. |
|
|
|
|
:param create_app: an optional callback that is passed the script info |
|
|
|
|
and returns the loaded app. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
def __init__(self, add_default_commands=True, add_app_option=None, |
|
|
|
|
add_debug_option=True, create_app=None, **extra): |
|
|
|
|
params = list(extra.pop('params', None) or ()) |
|
|
|
|
if add_app_option is None: |
|
|
|
|
add_app_option = create_app is None |
|
|
|
|
if add_app_option: |
|
|
|
|
params.append(app_option) |
|
|
|
|
if add_debug_option: |
|
|
|
|
params.append(debug_option) |
|
|
|
|
|
|
|
|
|
AppGroup.__init__(self, params=params, **extra) |
|
|
|
|
def __init__(self, add_default_commands=True, create_app=None, **extra): |
|
|
|
|
AppGroup.__init__(self, **extra) |
|
|
|
|
self.create_app = create_app |
|
|
|
|
|
|
|
|
|
if add_default_commands: |
|
|
|
@ -342,33 +318,6 @@ class FlaskGroup(AppGroup):
|
|
|
|
|
return AppGroup.main(self, *args, **kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def script_info_option(*args, **kwargs): |
|
|
|
|
"""This decorator works exactly like :func:`click.option` but is eager |
|
|
|
|
by default and stores the value in the :attr:`ScriptInfo.data`. This |
|
|
|
|
is useful to further customize an application factory in very complex |
|
|
|
|
situations. |
|
|
|
|
|
|
|
|
|
:param script_info_key: this is a mandatory keyword argument which |
|
|
|
|
defines under which data key the value should |
|
|
|
|
be stored. |
|
|
|
|
""" |
|
|
|
|
try: |
|
|
|
|
key = kwargs.pop('script_info_key') |
|
|
|
|
except LookupError: |
|
|
|
|
raise TypeError('script_info_key not provided.') |
|
|
|
|
|
|
|
|
|
real_callback = kwargs.get('callback') |
|
|
|
|
def callback(ctx, param, value): |
|
|
|
|
if real_callback is not None: |
|
|
|
|
value = real_callback(ctx, value) |
|
|
|
|
ctx.ensure_object(ScriptInfo).data[key] = value |
|
|
|
|
return value |
|
|
|
|
|
|
|
|
|
kwargs['callback'] = callback |
|
|
|
|
kwargs.setdefault('is_eager', True) |
|
|
|
|
return click.option(*args, **kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@click.command('run', short_help='Runs a development server.') |
|
|
|
|
@click.option('--host', '-h', default='127.0.0.1', |
|
|
|
|
help='The interface to bind to.') |
|
|
|
@ -400,10 +349,12 @@ def run_command(info, host, port, reload, debugger, eager_loading,
|
|
|
|
|
Flask is enabled and disabled otherwise. |
|
|
|
|
""" |
|
|
|
|
from werkzeug.serving import run_simple |
|
|
|
|
|
|
|
|
|
debug = get_debug_flag() |
|
|
|
|
if reload is None: |
|
|
|
|
reload = info.debug |
|
|
|
|
reload = bool(debug) |
|
|
|
|
if debugger is None: |
|
|
|
|
debugger = info.debug |
|
|
|
|
debugger = bool(debug) |
|
|
|
|
if eager_loading is None: |
|
|
|
|
eager_loading = not reload |
|
|
|
|
|
|
|
|
@ -418,12 +369,9 @@ def run_command(info, host, port, reload, debugger, eager_loading,
|
|
|
|
|
# we won't print anything. |
|
|
|
|
if info.app_import_path is not None: |
|
|
|
|
print(' * Serving Flask app "%s"' % info.app_import_path) |
|
|
|
|
if info.debug is not None: |
|
|
|
|
print(' * Forcing debug %s' % (info.debug and 'on' or 'off')) |
|
|
|
|
|
|
|
|
|
run_simple(host, port, app, use_reloader=reload, |
|
|
|
|
use_debugger=debugger, threaded=with_threads, |
|
|
|
|
passthrough_errors=True) |
|
|
|
|
use_debugger=debugger, threaded=with_threads) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@click.command('shell', short_help='Runs a shell in the app context.') |
|
|
|
@ -464,15 +412,21 @@ cli = FlaskGroup(help="""\
|
|
|
|
|
This shell command acts as general utility script for Flask applications. |
|
|
|
|
|
|
|
|
|
It loads the application configured (either through the FLASK_APP environment |
|
|
|
|
variable or the --app parameter) and then provides commands either provided |
|
|
|
|
by the application or Flask itself. |
|
|
|
|
variable) and then provides commands either provided by the application or |
|
|
|
|
Flask itself. |
|
|
|
|
|
|
|
|
|
The most useful commands are the "run" and "shell" command. |
|
|
|
|
|
|
|
|
|
Example usage: |
|
|
|
|
|
|
|
|
|
flask --app=hello --debug run |
|
|
|
|
""") |
|
|
|
|
\b |
|
|
|
|
%(prefix)s%(cmd)s FLASK_APP=hello |
|
|
|
|
%(prefix)s%(cmd)s FLASK_DEBUG=1 |
|
|
|
|
%(prefix)sflask run |
|
|
|
|
""" % { |
|
|
|
|
'cmd': os.name == 'posix' and 'export' or 'set', |
|
|
|
|
'prefix': os.name == 'posix' and '$ ' or '', |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(as_module=False): |
|
|
|
|