# -*- coding: utf-8 -*-"""flask.cli~~~~~~~~~A simple command line application to run flask apps.:copyright: © 2010 by the Pallets team.:license: BSD, see LICENSE for more details."""from __future__ import print_functionimport astimport inspectimport osimport reimport sslimport sysimport tracebackfrom functools import update_wrapperfrom operator import attrgetterfrom threading import Lock, Threadimport clickfrom werkzeug.utils import import_stringfrom . import __version__from ._compat import getargspec, iteritems, reraise, text_typefrom .globals import current_appfrom .helpers import get_debug_flag, get_env, get_load_dotenvtry:import dotenvexcept ImportError:dotenv = Noneclass NoAppException(click.UsageError):"""Raised if an application cannot be found or loaded."""def find_best_app(script_info, module):"""Given a module instance this tries to find the best possibleapplication 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 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 len(matches) == 1:return matches[0]elif len(matches) > 1:raise NoAppException('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 functions.for attr_name in ('create_app', 'make_app'):app_factory = getattr(module, attr_name, None)if inspect.isfunction(app_factory):try:app = call_factory(script_info, app_factory)if isinstance(app, Flask):return appexcept TypeError:if not _called_with_wrong_args(app_factory):raiseraise NoAppException('Detected factory "{factory}" in module "{module}", but ''could not call it without arguments. Use ''"FLASK_APP=\'{module}:{factory}(args)\'" to specify ''arguments.'.format(factory=attr_name, module=module.__name__))raise NoAppException('Failed to find Flask application or factory in module "{module}". ''Use "FLASK_APP={module}:name to specify one.'.format(module=module.__name__))def call_factory(script_info, app_factory, arguments=()):"""Takes an app factory, a ``script_info` object and optionally a tupleof arguments. Checks for the existence of a script_info argument and callsthe app_factory depending on that and the arguments provided."""args_spec = getargspec(app_factory)arg_names = args_spec.argsarg_defaults = args_spec.defaultsif 'script_info' in arg_names:return app_factory(*arguments, script_info=script_info)elif arguments:return app_factory(*arguments)elif not arguments and len(arg_names) == 1 and arg_defaults is None:return app_factory(script_info)return app_factory()def _called_with_wrong_args(factory):"""Check whether calling a function raised a ``TypeError`` becausethe call failed or because something in the factory raised theerror.:param factory: the factory function that was called:return: true if the call failed"""tb = sys.exc_info()[2]try:while tb is not None:if tb.tb_frame.f_code is factory.__code__:# in the factory, it was called successfullyreturn Falsetb = tb.tb_next# didn't reach the factoryreturn Truefinally:del tbdef find_app_by_string(script_info, module, app_name):"""Checks if the given string is a variable name or a function. If it is afunction, it checks for specified arguments and whether it takes a``script_info`` argument and calls the function with the appropriatearguments."""from flask import Flaskmatch = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name)if not match:raise NoAppException('"{name}" is not a valid variable name or function ''expression.'.format(name=app_name))name, args = match.groups()try:attr = getattr(module, name)except AttributeError as e:raise NoAppException(e.args[0])if inspect.isfunction(attr):if args:try:args = ast.literal_eval('({args},)'.format(args=args))except (ValueError, SyntaxError)as e:raise NoAppException('Could not parse the arguments in ''"{app_name}".'.format(e=e, app_name=app_name))else:args = ()try:app = call_factory(script_info, attr, args)except TypeError as e:if not _called_with_wrong_args(attr):raiseraise NoAppException('{e}\nThe factory "{app_name}" in module "{module}" could not ''be called with the specified arguments.'.format(e=e, app_name=app_name, module=module.__name__))else:app = attrif isinstance(app, Flask):return appraise NoAppException('A valid Flask application was not obtained from ''"{module}:{app_name}".'.format(module=module.__name__, app_name=app_name))def prepare_import(path):"""Given a filename this will try to calculate the python path, add itto the search path and return the actual module name that is expected."""path = os.path.realpath(path)if os.path.splitext(path)[1] == '.py':path = os.path.splitext(path)[0]if os.path.basename(path) == '__init__':path = os.path.dirname(path)module_name = []# move up until outside package structure (no __init__.py)while True:path, name = os.path.split(path)module_name.append(name)if not os.path.exists(os.path.join(path, '__init__.py')):breakif sys.path[0] != path:sys.path.insert(0, path)return '.'.join(module_name[::-1])def locate_app(script_info, module_name, app_name, raise_if_not_found=True):__traceback_hide__ = Truetry:__import__(module_name)except ImportError:# Reraise the ImportError if it occurred within the imported module.# Determine this by checking whether the trace has a depth > 1.if sys.exc_info()[-1].tb_next:raise NoAppException('While importing "{name}", an ImportError was raised:''\n\n{tb}'.format(name=module_name, tb=traceback.format_exc()))elif raise_if_not_found:raise NoAppException('Could not import "{name}".'.format(name=module_name))else:returnmodule = sys.modules[module_name]if app_name is None:return find_best_app(script_info, module)else:return find_app_by_string(script_info, module, app_name)def get_version(ctx, param, value):if not value or ctx.resilient_parsing:returnmessage = 'Flask %(version)s\nPython %(python_version)s'click.echo(message % {'version': __version__,'python_version': sys.version,}, color=ctx.color)ctx.exit()version_option = click.Option(['--version'],help='Show the flask version',expose_value=False,callback=get_version,is_flag=True,is_eager=True)class DispatchingApp(object):"""Special application that dispatches to a Flask application whichis imported by name in a background thread. If an error happensit is recorded and shown as part of the WSGI handling which in caseof the Werkzeug debugger means that it shows up in the browser."""def __init__(self, loader, use_eager_loading=False):self.loader = loaderself._app = Noneself._lock = Lock()self._bg_loading_exc_info = Noneif use_eager_loading:self._load_unlocked()else:self._load_in_background()def _load_in_background(self):def _load_app():__traceback_hide__ = Truewith self._lock:try:self._load_unlocked()except Exception:self._bg_loading_exc_info = sys.exc_info()t = Thread(target=_load_app, args=())t.start()def _flush_bg_loading_exception(self):__traceback_hide__ = Trueexc_info = self._bg_loading_exc_infoif exc_info is not None:self._bg_loading_exc_info = Nonereraise(*exc_info)def _load_unlocked(self):__traceback_hide__ = Trueself._app = rv = self.loader()self._bg_loading_exc_info = Nonereturn rvdef __call__(self, environ, start_response):__traceback_hide__ = Trueif self._app is not None:return self._app(environ, start_response)self._flush_bg_loading_exception()with self._lock:if self._app is not None:rv = self._appelse:rv = self._load_unlocked()return rv(environ, start_response)class ScriptInfo(object):"""Help object to deal with Flask applications. This is usually notnecessary to interface with as it's used internally in the dispatchingto click. In future versions of Flask this object will most likely playa bigger role. Typically it's created automatically by the:class:`FlaskGroup` but you can also manually create it and pass itonwards as click object."""def __init__(self, app_import_path=None, create_app=None):#: Optionally the import path for the Flask application.self.app_import_path = app_import_path or os.environ.get('FLASK_APP')#: Optionally a function that is passed the script info to create#: the instance of the application.self.create_app = create_app#: A dictionary with arbitrary data that can be associated with#: this script info.self.data = {}self._loaded_app = Nonedef load_app(self):"""Loads the Flask app (if not yet loaded) and returns it. Callingthis multiple times will just result in the already loaded app tobe returned."""__traceback_hide__ = Trueif self._loaded_app is not None:return self._loaded_appapp = Noneif self.create_app is not None:app = call_factory(self, self.create_app)else:if self.app_import_path:path, name = (self.app_import_path.split(':', 1) + [None])[:2]import_name = prepare_import(path)app = locate_app(self, import_name, name)else:for path in ('wsgi.py', 'app.py'):import_name = prepare_import(path)app = locate_app(self, import_name, None,raise_if_not_found=False)if app:breakif not app:raise NoAppException('Could not locate a Flask application. You did not provide ''the "FLASK_APP" environment variable, and a "wsgi.py" or ''"app.py" module was not found in the current directory.')debug = get_debug_flag()# Update the app's debug flag through the descriptor so that other# values repopulate as well.if debug is not None:app.debug = debugself._loaded_app = appreturn apppass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True)def with_appcontext(f):"""Wraps a callback so that it's guaranteed to be executed with thescript's application context. If callbacks are registered directlyto the ``app.cli`` object then they are wrapped with this functionby default unless it's disabled."""@click.pass_contextdef decorator(__ctx, *args, **kwargs):with __ctx.ensure_object(ScriptInfo).load_app().app_context():return __ctx.invoke(f, *args, **kwargs)return update_wrapper(decorator, f)class AppGroup(click.Group):"""This works similar to a regular click :class:`~click.Group` but itchanges the behavior of the :meth:`command` decorator so that itautomatically wraps the functions in :func:`with_appcontext`.Not to be confused with :class:`FlaskGroup`."""def command(self, *args, **kwargs):"""This works exactly like the method of the same name on a regular:class:`click.Group` but it wraps callbacks in :func:`with_appcontext`unless it's disabled by passing ``with_appcontext=False``."""wrap_for_ctx = kwargs.pop('with_appcontext', True)def decorator(f):if wrap_for_ctx:f = with_appcontext(f)return click.Group.command(self, *args, **kwargs)(f)return decoratordef group(self, *args, **kwargs):"""This works exactly like the method of the same name on a regular:class:`click.Group` but it defaults the group class to:class:`AppGroup`."""kwargs.setdefault('cls', AppGroup)return click.Group.group(self, *args, **kwargs)class FlaskGroup(AppGroup):"""Special subclass of the :class:`AppGroup` group that supportsloading more commands from the configured Flask app. Normally adeveloper does not have to interface with this class but there aresome very advanced use cases for which it makes sense to create aninstance of this.For information as of why this is useful see :ref:`custom-scripts`.:param add_default_commands: if this is True then the default run andshell commands wil be added.:param add_version_option: adds the ``--version`` option.:param create_app: an optional callback that is passed the script info andreturns the loaded app.:param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv`files to set environment variables. Will also change the workingdirectory to the directory containing the first file found... versionchanged:: 1.0If installed, python-dotenv will be used to load environment variablesfrom :file:`.env` and :file:`.flaskenv` files."""def __init__(self, add_default_commands=True, create_app=None,add_version_option=True, load_dotenv=True, **extra):params = list(extra.pop('params', None) or ())if add_version_option:params.append(version_option)AppGroup.__init__(self, params=params, **extra)self.create_app = create_appself.load_dotenv = load_dotenvif add_default_commands:self.add_command(run_command)self.add_command(shell_command)self.add_command(routes_command)self._loaded_plugin_commands = Falsedef _load_plugin_commands(self):if self._loaded_plugin_commands:returntry:import pkg_resourcesexcept ImportError:self._loaded_plugin_commands = Truereturnfor ep in pkg_resources.iter_entry_points('flask.commands'):self.add_command(ep.load(), ep.name)self._loaded_plugin_commands = Truedef get_command(self, ctx, name):self._load_plugin_commands()# We load built-in commands first as these should always be the# same no matter what the app does. If the app does want to# override this it needs to make a custom instance of this group# and not attach the default commands.## This also means that the script stays functional in case the# application completely fails.rv = AppGroup.get_command(self, ctx, name)if rv is not None:return rvinfo = ctx.ensure_object(ScriptInfo)try:rv = info.load_app().cli.get_command(ctx, name)if rv is not None:return rvexcept NoAppException:passdef list_commands(self, ctx):self._load_plugin_commands()# 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.ensure_object(ScriptInfo)try:rv.update(info.load_app().cli.list_commands(ctx))except Exception:# Here we intentionally swallow all exceptions as we don't# want the help page to break if the app does not exist.# If someone attempts to use the command we try to create# the app again and this will give us the error.# However, we will not do so silently because that would confuse# users.traceback.print_exc()return sorted(rv)def main(self, *args, **kwargs):# Set a global flag that indicates that we were invoked from the# command line interface. This is detected by Flask.run to make the# call into a no-op. This is necessary to avoid ugly errors when the# script that is loaded here also attempts to start a server.os.environ['FLASK_RUN_FROM_CLI'] = 'true'if get_load_dotenv(self.load_dotenv):load_dotenv()obj = kwargs.get('obj')if obj is None:obj = ScriptInfo(create_app=self.create_app)kwargs['obj'] = objkwargs.setdefault('auto_envvar_prefix', 'FLASK')return super(FlaskGroup, self).main(*args, **kwargs)def _path_is_ancestor(path, other):"""Take ``other`` and remove the length of ``path`` from it. Then join itto ``path``. If it is the original value, ``path`` is an ancestor of``other``."""return os.path.join(path, other[len(path):].lstrip(os.sep)) == otherdef load_dotenv(path=None):"""Load "dotenv" files in order of precedence to set environment variables.If an env var is already set it is not overwritten, so earlier files in thelist are preferred over later files.Changes the current working directory to the location of the first filefound, with the assumption that it is in the top level project directoryand will be where the Python path should import local packages from.This is a no-op if `python-dotenv`_ is not installed... _python-dotenv: https://github.com/theskumar/python-dotenv#readme:param path: Load the file at this location instead of searching.:return: ``True`` if a file was loaded... versionadded:: 1.0"""if dotenv is None:if path or os.path.exists('.env') or os.path.exists('.flaskenv'):click.secho(' * Tip: There are .env files present.'' Do "pip install python-dotenv" to use them.',fg='yellow')returnif path is not None:return dotenv.load_dotenv(path)new_dir = Nonefor name in ('.env', '.flaskenv'):path = dotenv.find_dotenv(name, usecwd=True)if not path:continueif new_dir is None:new_dir = os.path.dirname(path)dotenv.load_dotenv(path)if new_dir and os.getcwd() != new_dir:os.chdir(new_dir)return new_dir is not None # at least one file was located and loadeddef show_server_banner(env, debug, app_import_path, eager_loading):"""Show extra startup messages the first time the server is run,ignoring the reloader."""if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':returnif app_import_path is not None:message = ' * Serving Flask app "{0}"'.format(app_import_path)if not eager_loading:message += ' (lazy loading)'click.echo(message)click.echo(' * Environment: {0}'.format(env))if env == 'production':click.secho(' WARNING: Do not use the development server in a production'' environment.', fg='red')click.secho(' Use a production WSGI server instead.', dim=True)if debug is not None:click.echo(' * Debug mode: {0}'.format('on' if debug else 'off'))class CertParamType(click.ParamType):"""Click option type for the ``--cert`` option. Allows either anexisting file, the string ``'adhoc'``, or an import for a:class:`~ssl.SSLContext` object."""name = 'path'def __init__(self):self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True)def convert(self, value, param, ctx):try:return self.path_type(value, param, ctx)except click.BadParameter:value = click.STRING(value, param, ctx).lower()if value == 'adhoc':try:import OpenSSLexcept ImportError:raise click.BadParameter('Using ad-hoc certificates requires pyOpenSSL.',ctx, param)return valueobj = import_string(value, silent=True)if sys.version_info < (2, 7):if obj:return objelse:if isinstance(obj, ssl.SSLContext):return objraisedef _validate_key(ctx, param, value):"""The ``--key`` option must be specified when ``--cert`` is a file.Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed."""cert = ctx.params.get('cert')is_adhoc = cert == 'adhoc'if sys.version_info < (2, 7):is_context = cert and not isinstance(cert, (text_type, bytes))else:is_context = isinstance(cert, ssl.SSLContext)if value is not None:if is_adhoc:raise click.BadParameter('When "--cert" is "adhoc", "--key" is not used.',ctx, param)if is_context:raise click.BadParameter('When "--cert" is an SSLContext object, "--key is not used.',ctx, param)if not cert:raise click.BadParameter('"--cert" must also be specified.',ctx, param)ctx.params['cert'] = cert, valueelse:if cert and not (is_adhoc or is_context):raise click.BadParameter('Required when using "--cert".',ctx, param)return value@click.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('--cert', type=CertParamType(),help='Specify a certificate file to use HTTPS.')@click.option('--key',type=click.Path(exists=True, dir_okay=False, resolve_path=True),callback=_validate_key, expose_value=False,help='The key file to use when specifying a certificate.')@click.option('--reload/--no-reload', default=None,help='Enable or disable the reloader. By default the reloader ''is active if 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=True,help='Enable or disable multithreading.')@pass_script_infodef run_command(info, host, port, reload, debugger, eager_loading,with_threads, cert):"""Run a local development server.This server is for development purposes only. It does not providethe stability, security, or performance of production WSGI servers.The reloader and debugger are enabled by default ifFLASK_ENV=development or FLASK_DEBUG=1."""debug = get_debug_flag()if reload is None:reload = debugif debugger is None:debugger = debugif eager_loading is None:eager_loading = not reloadshow_server_banner(get_env(), debug, info.app_import_path, eager_loading)app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)from werkzeug.serving import run_simplerun_simple(host, port, app, use_reloader=reload, use_debugger=debugger,threaded=with_threads, ssl_context=cert)@click.command('shell', short_help='Runs a shell in the app context.')@with_appcontextdef shell_command():"""Runs an interactive Python shell in the context of a givenFlask application. The application will populate the defaultnamespace of this shell according to it's configuration.This is useful for executing small snippets of management codewithout having to manually configure the application."""import codefrom flask.globals import _app_ctx_stackapp = _app_ctx_stack.top.appbanner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % (sys.version,sys.platform,app.import_name,app.env,app.instance_path,)ctx = {}# Support the regular Python interpreter startup script if someone# is using it.startup = os.environ.get('PYTHONSTARTUP')if startup and os.path.isfile(startup):with open(startup, 'r') as f:eval(compile(f.read(), startup, 'exec'), ctx)ctx.update(app.make_shell_context())code.interact(banner=banner, local=ctx)@click.command('routes', short_help='Show the routes for the app.')@click.option('--sort', '-s',type=click.Choice(('endpoint', 'methods', 'rule', 'match')),default='endpoint',help=('Method to sort routes by. "match" is the order that Flask will match ''routes when dispatching a request.'))@click.option('--all-methods',is_flag=True,help="Show HEAD and OPTIONS methods.")@with_appcontextdef routes_command(sort, all_methods):"""Show all registered routes with endpoints and methods."""rules = list(current_app.url_map.iter_rules())if not rules:click.echo('No routes were registered.')returnignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))if sort in ('endpoint', 'rule'):rules = sorted(rules, key=attrgetter(sort))elif sort == 'methods':rules = sorted(rules, key=lambda rule: sorted(rule.methods))rule_methods = [', '.join(sorted(rule.methods - ignored_methods)) for rule in rules]headers = ('Endpoint', 'Methods', 'Rule')widths = (max(len(rule.endpoint) for rule in rules),max(len(methods) for methods in rule_methods),max(len(rule.rule) for rule in rules),)widths = [max(len(h), w) for h, w in zip(headers, widths)]row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths)click.echo(row.format(*headers).strip())click.echo(row.format(*('-' * width for width in widths)))for rule, methods in zip(rules, rule_methods):click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip())cli = FlaskGroup(help="""\A general utility script for Flask applications.Provides commands from Flask, extensions, and the application. Loads theapplication defined in the FLASK_APP environment variable, or from a wsgi.pyfile. Setting the FLASK_ENV environment variable to 'development' will enabledebug mode.\b{prefix}{cmd} FLASK_APP=hello.py{prefix}{cmd} FLASK_ENV=development{prefix}flask run""".format(cmd='export' if os.name == 'posix' else 'set',prefix='$ ' if os.name == 'posix' else '> '))def main(as_module=False):args = sys.argv[1:]if as_module:this_module = 'flask'if sys.version_info < (2, 7):this_module += '.cli'name = 'python -m ' + this_module# Python rewrites "python -m flask" to the path to the file in argv.# Restore the original command so that the reloader works.sys.argv = ['-m', this_module] + argselse:name = Nonecli.main(args=args, prog_name=name)if __name__ == '__main__':main(as_module=True)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。