Implemented simplified CLI interface

This commit is contained in:
Armin Ronacher 2016-05-26 20:07:52 +02:00
parent 92f63a1c1d
commit 523e271183
5 changed files with 51 additions and 91 deletions

View file

@ -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
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
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
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):