Added click support to Flask
This commit is contained in:
parent
0ac492a0ac
commit
3bdb90f06b
4 changed files with 392 additions and 251 deletions
|
|
@ -11,5 +11,5 @@
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from run import main
|
||||
from cli import main
|
||||
main(as_module=True)
|
||||
|
|
|
|||
47
flask/app.py
47
flask/app.py
|
|
@ -34,6 +34,7 @@ from .templating import DispatchingJinjaLoader, Environment, \
|
|||
_default_template_ctx_processor
|
||||
from .signals import request_started, request_finished, got_request_exception, \
|
||||
request_tearing_down, appcontext_tearing_down
|
||||
from .cli import make_default_cli
|
||||
from ._compat import reraise, string_types, text_type, integer_types
|
||||
|
||||
# a lock used for logger initialization
|
||||
|
|
@ -476,6 +477,12 @@ class Flask(_PackageBoundObject):
|
|||
None: [_default_template_ctx_processor]
|
||||
}
|
||||
|
||||
#: A list of shell context processor functions that should be run
|
||||
#: when a shell context is created.
|
||||
#:
|
||||
#: .. versionadded:: 1.0
|
||||
self.shell_context_processors = []
|
||||
|
||||
#: all the attached blueprints in a dictionary by name. Blueprints
|
||||
#: can be attached multiple times so this dictionary does not tell
|
||||
#: you how often they got attached.
|
||||
|
|
@ -531,6 +538,14 @@ class Flask(_PackageBoundObject):
|
|||
endpoint='static',
|
||||
view_func=self.send_static_file)
|
||||
|
||||
#: The click command line context for this application. Commands
|
||||
#: registered here show up in the ``flask`` command once the
|
||||
#: application has been discovered. The default commands are
|
||||
#: provided by Flask itself and can be overridden.
|
||||
#:
|
||||
#: This is an instance of a :class:`click.Group` object.
|
||||
self.cli = make_default_cli(self)
|
||||
|
||||
def _get_error_handlers(self):
|
||||
from warnings import warn
|
||||
warn(DeprecationWarning('error_handlers is deprecated, use the '
|
||||
|
|
@ -748,6 +763,18 @@ class Flask(_PackageBoundObject):
|
|||
# existing views.
|
||||
context.update(orig_ctx)
|
||||
|
||||
def make_shell_context(self):
|
||||
"""Returns the shell context for an interactive shell for this
|
||||
application. This runs all the registered shell context
|
||||
processors.
|
||||
|
||||
.. versionadded:: 1.0
|
||||
"""
|
||||
rv = {'app': self, 'g': g}
|
||||
for processor in self.shell_context_processors:
|
||||
rv.update(processor())
|
||||
return rv
|
||||
|
||||
def run(self, host=None, port=None, debug=None, **options):
|
||||
"""Runs the application on a local development server. If the
|
||||
:attr:`debug` flag is set the server will automatically reload
|
||||
|
|
@ -758,6 +785,11 @@ class Flask(_PackageBoundObject):
|
|||
``use_evalex=False`` as parameter. This will keep the debugger's
|
||||
traceback screen active, but disable code execution.
|
||||
|
||||
It is not recommended to use this function for development with
|
||||
automatic reloading as this is badly supported. Instead you should
|
||||
be using the ``flask`` command line script's ``runserver``
|
||||
support.
|
||||
|
||||
.. admonition:: Keep in Mind
|
||||
|
||||
Flask will suppress any server error with a generic error page
|
||||
|
|
@ -1091,7 +1123,7 @@ class Flask(_PackageBoundObject):
|
|||
Use :meth:`register_error_handler` instead of modifying
|
||||
:attr:`error_handler_spec` directly, for application wide error
|
||||
handlers.
|
||||
|
||||
|
||||
.. versionadded:: 0.7
|
||||
One can now additionally also register custom exception types
|
||||
that do not necessarily have to be a subclass of the
|
||||
|
|
@ -1325,6 +1357,15 @@ class Flask(_PackageBoundObject):
|
|||
self.template_context_processors[None].append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def shell_context_processor(self, f):
|
||||
"""Registers a shell context processor function.
|
||||
|
||||
.. versionadded:: 1.0
|
||||
"""
|
||||
self.shell_context_processors.append(f)
|
||||
return f
|
||||
|
||||
@setupmethod
|
||||
def url_value_preprocessor(self, f):
|
||||
"""Registers a function as URL value preprocessor for all view
|
||||
|
|
@ -1609,7 +1650,8 @@ class Flask(_PackageBoundObject):
|
|||
# some extra logic involved when creating these objects with
|
||||
# specific values (like default content type selection).
|
||||
if isinstance(rv, (text_type, bytes, bytearray)):
|
||||
rv = self.response_class(rv, headers=headers, status=status_or_headers)
|
||||
rv = self.response_class(rv, headers=headers,
|
||||
status=status_or_headers)
|
||||
headers = status_or_headers = None
|
||||
else:
|
||||
rv = self.response_class.force_type(rv, request.environ)
|
||||
|
|
@ -1624,7 +1666,6 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
return rv
|
||||
|
||||
|
||||
def create_url_adapter(self, request):
|
||||
"""Creates a URL adapter for the given request. The URL adapter
|
||||
is created at a point where the request context is not yet set up
|
||||
|
|
|
|||
347
flask/cli.py
Normal file
347
flask/cli.py
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.run
|
||||
~~~~~~~~~
|
||||
|
||||
A simple command line application to run flask apps.
|
||||
|
||||
:copyright: (c) 2014 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from threading import Lock
|
||||
from contextlib import contextmanager
|
||||
|
||||
import click
|
||||
|
||||
from ._compat import iteritems
|
||||
|
||||
|
||||
class NoAppException(click.UsageError):
|
||||
"""Raised if an application cannot be found or loaded."""
|
||||
|
||||
|
||||
def find_best_app(module):
|
||||
"""Given a module instance this tries to find the best possible
|
||||
application in the module or raises an exception.
|
||||
"""
|
||||
from . import Flask
|
||||
|
||||
# Search for the most common names first.
|
||||
for attr_name in 'app', 'application':
|
||||
app = getattr(module, attr_name, None)
|
||||
if app is not None and 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)]
|
||||
|
||||
if matches:
|
||||
if len(matches) > 1:
|
||||
raise NoAppException('More than one possible Flask application '
|
||||
'found in module "%s", none of which are called '
|
||||
'"app". Be explicit!' % module.__name__)
|
||||
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__)
|
||||
|
||||
|
||||
def prepare_exec_for_file(filename):
|
||||
module = []
|
||||
|
||||
# Chop off file extensions or package markers
|
||||
if filename.endswith('.py'):
|
||||
filename = filename[:-3]
|
||||
elif os.path.split(filename)[1] == '__init__.py':
|
||||
filename = os.path.dirname(filename)
|
||||
filename = os.path.realpath(filename)
|
||||
|
||||
dirpath = filename
|
||||
while 1:
|
||||
dirpath, extra = os.path.split(dirpath)
|
||||
module.append(extra)
|
||||
if not os.path.isfile(os.path.join(dirpath, '__init__.py')):
|
||||
break
|
||||
|
||||
sys.path.insert(0, dirpath)
|
||||
return '.'.join(module[::-1])
|
||||
|
||||
|
||||
def locate_app(app_id, debug=None):
|
||||
"""Attempts to locate the application."""
|
||||
if ':' in app_id:
|
||||
module, app_obj = app_id.split(':', 1)
|
||||
else:
|
||||
module = app_id
|
||||
app_obj = None
|
||||
|
||||
__import__(module)
|
||||
mod = sys.modules[module]
|
||||
if app_obj is None:
|
||||
app = find_best_app(mod)
|
||||
else:
|
||||
app = getattr(mod, app_obj, None)
|
||||
if app is None:
|
||||
raise RuntimeError('Failed to find application in module "%s"'
|
||||
% module)
|
||||
if debug is not None:
|
||||
app.debug = debug
|
||||
return app
|
||||
|
||||
|
||||
class DispatchingApp(object):
|
||||
"""Special applicationt that dispatches to a flask application which
|
||||
is imported by name on first request. This is safer than importing
|
||||
the application upfront because it means that we can forward all
|
||||
errors for import problems into the browser as error.
|
||||
"""
|
||||
|
||||
def __init__(self, app_id, debug=None, use_eager_loading=False):
|
||||
self.app_id = app_id
|
||||
self.app = None
|
||||
self.debug = debug
|
||||
self._lock = Lock()
|
||||
if use_eager_loading:
|
||||
self._load_unlocked()
|
||||
|
||||
def _load_unlocked(self):
|
||||
self.app = rv = locate_app(self.app_id, self.debug)
|
||||
return rv
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
if self.app is not None:
|
||||
return self.app(environ, start_response)
|
||||
with self._lock:
|
||||
if self.app is not None:
|
||||
rv = self.app
|
||||
else:
|
||||
rv = self._load_unlocked()
|
||||
return rv(environ, start_response)
|
||||
|
||||
|
||||
class ScriptInfo(object):
|
||||
"""Help object to deal with Flask applications. This is usually not
|
||||
necessary to interface with as it's used internally in the dispatching
|
||||
to click.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.app_import_path = None
|
||||
self.debug = None
|
||||
self._loaded_app = None
|
||||
|
||||
def get_app_import_path(self):
|
||||
"""Return the actual application import path or fails if it is
|
||||
not yet set.
|
||||
"""
|
||||
if self.app_import_path is not None:
|
||||
return self.app_import_path
|
||||
raise NoAppException('Could not locate application. '
|
||||
'You did not provide FLASK_APP or the '
|
||||
'--app parameter.')
|
||||
|
||||
def load_app(self):
|
||||
"""Loads the app (if not yet loaded) and returns it."""
|
||||
if self._loaded_app is not None:
|
||||
return self._loaded_app
|
||||
rv = locate_app(self.get_app_import_path(), self.debug)
|
||||
self._loaded_app = rv
|
||||
return rv
|
||||
|
||||
@contextmanager
|
||||
def conditional_context(self, with_context=True):
|
||||
"""Creates an application context or not, depending on the
|
||||
given parameter but always works as context manager.
|
||||
"""
|
||||
if with_context:
|
||||
with self.load_app().app_context() as ctx:
|
||||
yield ctx
|
||||
else:
|
||||
yield None
|
||||
|
||||
|
||||
pass_script_info = click.make_pass_decorator(ScriptInfo)
|
||||
|
||||
|
||||
def without_appcontext(f):
|
||||
"""Marks a click callback so that it does not get a app context
|
||||
created. This only works for commands directly registered to
|
||||
the toplevel system. This really is only useful for very
|
||||
special commands like the runserver one.
|
||||
"""
|
||||
f.__flask_without_appcontext__ = True
|
||||
return f
|
||||
|
||||
|
||||
class FlaskClickGroup(click.Group):
|
||||
"""Special subclass of the a regular click group that supports
|
||||
loading more commands from the configured Flask app.
|
||||
"""
|
||||
|
||||
def __init__(self, help=None):
|
||||
def set_app_id(ctx, value):
|
||||
if value is not None:
|
||||
if os.path.isfile(value) or os.sep in value or \
|
||||
os.altsep is not None and os.altsep in value:
|
||||
value = prepare_exec_for_file(value)
|
||||
ctx.obj.app_import_path = value
|
||||
def set_debug(ctx, value):
|
||||
ctx.obj.debug = value
|
||||
|
||||
click.Group.__init__(self, help=help, params=[
|
||||
click.Option(['-a', '--app'],
|
||||
help='The application to run',
|
||||
callback=set_app_id, is_eager=True),
|
||||
click.Option(['--debug/--no-debug'],
|
||||
help='Enable or disable debug mode.',
|
||||
default=None, callback=set_debug)
|
||||
])
|
||||
|
||||
def get_command(self, ctx, name):
|
||||
info = ctx.find_object(ScriptInfo)
|
||||
# Find the command in the application first, if we can find it.
|
||||
# If the app is not available, we just ignore this silently.
|
||||
try:
|
||||
rv = info.load_app().cli.get_command(ctx, name)
|
||||
if rv is not None:
|
||||
return rv
|
||||
except NoAppException:
|
||||
pass
|
||||
return click.Group.get_command(self, ctx, name)
|
||||
|
||||
def list_commands(self, ctx):
|
||||
# The commands available is the list of both the application (if
|
||||
# available) plus the builtin commands.
|
||||
rv = set(click.Group.list_commands(self, ctx))
|
||||
info = ctx.find_object(ScriptInfo)
|
||||
try:
|
||||
rv.update(info.load_app().cli.list_commands(ctx))
|
||||
except NoAppException:
|
||||
pass
|
||||
return sorted(rv)
|
||||
|
||||
def invoke_subcommand(self, ctx, cmd, cmd_name, args):
|
||||
with_context = cmd.callback is None or \
|
||||
not getattr(cmd.callback, '__flask_without_appcontext__', False)
|
||||
|
||||
with ctx.find_object(ScriptInfo).conditional_context(with_context):
|
||||
return click.Group.invoke_subcommand(
|
||||
self, ctx, cmd, cmd_name, args)
|
||||
|
||||
|
||||
cli = FlaskClickGroup(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.
|
||||
|
||||
The most useful commands are the "run" and "shell" command.
|
||||
|
||||
Example usage:
|
||||
|
||||
flask --app=hello --debug run
|
||||
''')
|
||||
|
||||
|
||||
@cli.command('run', short_help='Runs a development server.')
|
||||
@click.option('--host', '-h', default='127.0.0.1',
|
||||
help='The interface to bind to.')
|
||||
@click.option('--port', '-p', default=5000,
|
||||
help='The port to bind to.')
|
||||
@click.option('--reload/--no-reload', default=None,
|
||||
help='Enable or disable the reloader. By default the reloader '
|
||||
'is active is debug is enabled.')
|
||||
@click.option('--debugger/--no-debugger', default=None,
|
||||
help='Enable or disable the debugger. By default the debugger '
|
||||
'is active if debug is enabled.')
|
||||
@click.option('--eager-loading/--lazy-loader', default=None,
|
||||
help='Enable or disable eager loading. By default eager '
|
||||
'loading is enabled if the reloader is disabled.')
|
||||
@click.option('--with-threads/--without-threads', default=False,
|
||||
help='Enable or disable multithreading.')
|
||||
@without_appcontext
|
||||
@pass_script_info
|
||||
def run_command(info, host, port, reload, debugger, eager_loading,
|
||||
with_threads):
|
||||
"""Runs a local development server for the Flask application."""
|
||||
from werkzeug.serving import run_simple
|
||||
app_id = info.get_app_import_path()
|
||||
if reload is None:
|
||||
reload = info.debug
|
||||
if debugger is None:
|
||||
debugger = info.debug
|
||||
if eager_loading is None:
|
||||
eager_loading = not reload
|
||||
|
||||
# Extra startup messages. This depends a but on Werkzeug internals to
|
||||
# not double execute when the reloader kicks in.
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
||||
print(' * Serving Flask app "%s"' % app_id)
|
||||
if info.debug is not None:
|
||||
print(' * Forcing debug %s' % (info.debug and 'on' or 'off'))
|
||||
|
||||
app = DispatchingApp(app_id, info.debug, eager_loading)
|
||||
run_simple(host, port, app, use_reloader=reload,
|
||||
use_debugger=debugger, threaded=with_threads)
|
||||
|
||||
|
||||
@cli.command('shell', short_help='Runs a shell in the app context.')
|
||||
def shell_command():
|
||||
"""Runs an interactive Python shell in the context of a given
|
||||
Flask application. The application will populate the default
|
||||
namespace of this shell according to it's configuration.
|
||||
|
||||
This is useful for executing small snippets of management code
|
||||
without having to manually configuring the application.
|
||||
"""
|
||||
import code
|
||||
from flask.globals import _app_ctx_stack
|
||||
app = _app_ctx_stack.top.app
|
||||
banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % (
|
||||
sys.version,
|
||||
sys.platform,
|
||||
app.import_name,
|
||||
app.debug and ' [debug]' or '',
|
||||
app.instance_path,
|
||||
)
|
||||
code.interact(banner=banner, local=app.make_shell_context())
|
||||
|
||||
|
||||
def make_default_cli(app):
|
||||
"""Creates the default click object for the app itself. Currently
|
||||
there are no default commands registered because all builtin commands
|
||||
are registered on the actual cmd object here.
|
||||
"""
|
||||
return click.Group()
|
||||
|
||||
|
||||
def main(as_module=False):
|
||||
this_module = __package__ + '.cli'
|
||||
args = sys.argv[1:]
|
||||
|
||||
if as_module:
|
||||
if sys.version_info >= (2, 7):
|
||||
name = 'python -m ' + this_module.rsplit('.', 1)[0]
|
||||
else:
|
||||
name = 'python -m ' + this_module
|
||||
|
||||
# This module is always executed as "python -m flask.run" and as such
|
||||
# we need to ensure that we restore the actual command line so that
|
||||
# the reloader can properly operate.
|
||||
sys.argv = ['-m', this_module] + sys.argv[1:]
|
||||
else:
|
||||
name = 'flask'
|
||||
|
||||
cli.main(args=args, prog_name=name, obj=ScriptInfo(),
|
||||
auto_envvar_prefix='FLASK')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(as_module=True)
|
||||
247
flask/run.py
247
flask/run.py
|
|
@ -1,247 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
flask.run
|
||||
~~~~~~~~~
|
||||
|
||||
A simple command line application to run flask apps.
|
||||
|
||||
:copyright: (c) 2014 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from threading import Lock
|
||||
from optparse import OptionParser
|
||||
|
||||
from werkzeug.serving import run_simple
|
||||
|
||||
from ._compat import iteritems
|
||||
|
||||
|
||||
def find_best_app(module):
|
||||
"""Given a module instance this tries to find the best possible application
|
||||
in the module or raises a RuntimeError.
|
||||
"""
|
||||
from flask import Flask
|
||||
|
||||
# The app name wins, even if it's not a flask object.
|
||||
app = getattr(module, 'app', None)
|
||||
if app is not None and callable(app):
|
||||
return app
|
||||
|
||||
# Otherwise find the first object named Flask
|
||||
matches = []
|
||||
for key, value in iteritems(module.__dict__):
|
||||
if isinstance(value, Flask):
|
||||
matches.append(value)
|
||||
|
||||
if matches:
|
||||
if len(matches) > 1:
|
||||
raise RuntimeError('More than one possible Flask application '
|
||||
'found in module "%s", none of which are called '
|
||||
'"app". Be explicit!' % module)
|
||||
return matches[0]
|
||||
|
||||
raise RuntimeError('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)
|
||||
|
||||
|
||||
def prepare_exec_for_file(filename):
|
||||
module = []
|
||||
|
||||
# Chop off file extensions or package markers
|
||||
if filename.endswith('.py'):
|
||||
filename = filename[:-3]
|
||||
elif os.path.split(filename)[1] == '__init__.py':
|
||||
filename = os.path.dirname(filename)
|
||||
filename = os.path.realpath(filename)
|
||||
|
||||
dirpath = filename
|
||||
while 1:
|
||||
dirpath, extra = os.path.split(dirpath)
|
||||
module.append(extra)
|
||||
if not os.path.isfile(os.path.join(dirpath, '__init__.py')):
|
||||
break
|
||||
|
||||
sys.path.insert(0, dirpath)
|
||||
return '.'.join(module[::-1])
|
||||
|
||||
|
||||
def locate_app(app_id, debug=None):
|
||||
"""Attempts to locate the application."""
|
||||
if ':' in app_id:
|
||||
module, app_obj = app_id.split(':', 1)
|
||||
else:
|
||||
module = app_id
|
||||
app_obj = None
|
||||
|
||||
__import__(module)
|
||||
mod = sys.modules[module]
|
||||
if app_obj is None:
|
||||
app = find_best_app(mod)
|
||||
else:
|
||||
app = getattr(mod, app_obj, None)
|
||||
if app is None:
|
||||
raise RuntimeError('Failed to find application in module "%s"'
|
||||
% module)
|
||||
if debug is not None:
|
||||
app.debug = debug
|
||||
return app
|
||||
|
||||
|
||||
class DispatchingApp(object):
|
||||
"""Special applicationt that dispatches to a flask application which
|
||||
is imported by name on first request. This is safer than importing
|
||||
the application upfront because it means that we can forward all
|
||||
errors for import problems into the browser as error.
|
||||
"""
|
||||
|
||||
def __init__(self, app_id, debug=None, use_eager_loading=False):
|
||||
self.app_id = app_id
|
||||
self.app = None
|
||||
self.debug = debug
|
||||
self._lock = Lock()
|
||||
if use_eager_loading:
|
||||
self._load_unlocked()
|
||||
|
||||
def _load_unlocked(self):
|
||||
self.app = rv = locate_app(self.app_id, self.debug)
|
||||
return rv
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
if self.app is not None:
|
||||
return self.app(environ, start_response)
|
||||
with self._lock:
|
||||
if self.app is not None:
|
||||
rv = self.app
|
||||
else:
|
||||
rv = self._load_unlocked()
|
||||
return rv(environ, start_response)
|
||||
|
||||
|
||||
def run_application(app_id, host='127.0.0.1', port=5000, debug=None,
|
||||
use_reloader=False, use_debugger=False,
|
||||
use_eager_loading=None, magic_app_id=True,
|
||||
**options):
|
||||
"""Useful function to start a Werkzeug server for an application that
|
||||
is known by it's import name. By default the app ID can also be a
|
||||
full file name in which case Flask attempts to reconstruct the import
|
||||
name from it and do the right thing.
|
||||
|
||||
:param app_id: the import name of the application module. If a colon
|
||||
is provided, everything afterwards is the application
|
||||
object name. In case the magic app id is enabled this
|
||||
can also be a filename.
|
||||
:param host: the host to bind to.
|
||||
:param port: the port to bind to.
|
||||
:param debug: if set to something other than None then the application's
|
||||
debug flag will be set to this.
|
||||
:param use_reloader: enables or disables the reloader.
|
||||
:param use_debugger: enables or disables the builtin debugger.
|
||||
:param use_eager_loading: enables or disables eager loading. This is
|
||||
normally conditional to the reloader.
|
||||
:param magic_app_id: if this is enabled then the app id can also be a
|
||||
filename instead of an import module and Flask
|
||||
will attempt to reconstruct the import name.
|
||||
:param options: the options to be forwarded to the underlying
|
||||
Werkzeug server. See
|
||||
:func:`werkzeug.serving.run_simple` for more
|
||||
information.
|
||||
"""
|
||||
if magic_app_id:
|
||||
if os.path.isfile(app_id) or os.sep in app_id or \
|
||||
os.altsep is not None and os.altsep in app_id:
|
||||
app_id = prepare_exec_for_file(app_id)
|
||||
|
||||
if use_eager_loading is None:
|
||||
use_eager_loading = not use_reloader
|
||||
|
||||
# Extra startup messages. This depends a but on Werkzeug internals to
|
||||
# not double execute when the reloader kicks in.
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
||||
print ' * Serving Flask app "%s"' % app_id
|
||||
if debug is not None:
|
||||
print ' * Forcing debug %s' % (debug and 'on' or 'off')
|
||||
|
||||
app = DispatchingApp(app_id, debug, use_eager_loading)
|
||||
run_simple(host, port, app, use_reloader=use_reloader,
|
||||
use_debugger=use_debugger, **options)
|
||||
|
||||
|
||||
def main(as_module=False):
|
||||
this_module = __package__ + '.run'
|
||||
|
||||
if as_module:
|
||||
if sys.version_info >= (2, 7):
|
||||
name = 'python -m ' + this_module.rsplit('.', 1)[0]
|
||||
else:
|
||||
name = 'python -m ' + this_module
|
||||
else:
|
||||
name = 'flask-run'
|
||||
|
||||
parser = OptionParser(usage='%prog [options] module', prog=name)
|
||||
parser.add_option('--debug', action='store_true',
|
||||
dest='debug', help='Flip debug flag on. If enabled '
|
||||
'this also affects debugger and reloader defaults.')
|
||||
parser.add_option('--no-debug', action='store_false',
|
||||
dest='debug', help='Flip debug flag off.')
|
||||
parser.add_option('--host', default='127.0.0.1',
|
||||
help='The host to bind on. (defaults to 127.0.0.1)')
|
||||
parser.add_option('--port', default=5000,
|
||||
help='The port to bind on. (defaults to 5000)')
|
||||
parser.add_option('--with-reloader', action='store_true',
|
||||
dest='with_reloader',
|
||||
help='Enable the reloader.')
|
||||
parser.add_option('--without-reloader', action='store_false',
|
||||
dest='with_reloader',
|
||||
help='Disable the reloader.')
|
||||
parser.add_option('--with-debugger', action='store_true',
|
||||
dest='with_debugger',
|
||||
help='Enable the debugger.')
|
||||
parser.add_option('--without-debugger', action='store_false',
|
||||
dest='with_debugger',
|
||||
help='Disable the debugger.')
|
||||
parser.add_option('--with-eager-loading', action='store_true',
|
||||
dest='with_eager_loading',
|
||||
help='Force enable the eager-loading. This makes the '
|
||||
'application load immediately but makes development '
|
||||
'flows harder. It\'s not recommended to enable eager '
|
||||
'loading when the reloader is enabled as it can lead '
|
||||
'to unexpected crashes.')
|
||||
parser.add_option('--without-eager-loading', action='store_false',
|
||||
dest='with_eager_loading',
|
||||
help='Disable the eager-loading.')
|
||||
parser.add_option('--with-threads', action='store_true',
|
||||
dest='with_threads',
|
||||
help='Enable multi-threading to handle multiple '
|
||||
'requests concurrently.')
|
||||
parser.add_option('--without-threads', action='store_false',
|
||||
dest='with_threads',
|
||||
help='Disables multi-threading. (default)')
|
||||
opts, args = parser.parse_args()
|
||||
if len(args) != 1:
|
||||
parser.error('Expected exactly one argument which is the import '
|
||||
'name of the application.')
|
||||
|
||||
if opts.with_debugger is None:
|
||||
opts.with_debugger = opts.debug
|
||||
if opts.with_reloader is None:
|
||||
opts.with_reloader = opts.debug
|
||||
|
||||
# This module is always executed as "python -m flask.run" and as such
|
||||
# we need to ensure that we restore the actual command line so that
|
||||
# the reloader can properly operate.
|
||||
sys.argv = ['-m', this_module] + sys.argv[1:]
|
||||
|
||||
run_application(args[0], opts.host, opts.port, debug=opts.debug,
|
||||
use_reloader=opts.with_reloader,
|
||||
use_debugger=opts.with_debugger,
|
||||
use_eager_loading=opts.with_eager_loading,
|
||||
threaded=opts.with_threads)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(as_module=True)
|
||||
Loading…
Add table
Add a link
Reference in a new issue