parent
5b309831ec
commit
025589ee76
63 changed files with 3784 additions and 3459 deletions
|
|
@ -10,7 +10,7 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
__version__ = '1.1.dev'
|
||||
__version__ = "1.1.dev"
|
||||
|
||||
# utilities we import from Werkzeug and Jinja2 that are unused
|
||||
# in the module but are exported as public interface.
|
||||
|
|
@ -20,21 +20,48 @@ from jinja2 import Markup, escape
|
|||
|
||||
from .app import Flask, Request, Response
|
||||
from .config import Config
|
||||
from .helpers import url_for, flash, send_file, send_from_directory, \
|
||||
get_flashed_messages, get_template_attribute, make_response, safe_join, \
|
||||
stream_with_context
|
||||
from .globals import current_app, g, request, session, _request_ctx_stack, \
|
||||
_app_ctx_stack
|
||||
from .ctx import has_request_context, has_app_context, \
|
||||
after_this_request, copy_current_request_context
|
||||
from .helpers import (
|
||||
url_for,
|
||||
flash,
|
||||
send_file,
|
||||
send_from_directory,
|
||||
get_flashed_messages,
|
||||
get_template_attribute,
|
||||
make_response,
|
||||
safe_join,
|
||||
stream_with_context,
|
||||
)
|
||||
from .globals import (
|
||||
current_app,
|
||||
g,
|
||||
request,
|
||||
session,
|
||||
_request_ctx_stack,
|
||||
_app_ctx_stack,
|
||||
)
|
||||
from .ctx import (
|
||||
has_request_context,
|
||||
has_app_context,
|
||||
after_this_request,
|
||||
copy_current_request_context,
|
||||
)
|
||||
from .blueprints import Blueprint
|
||||
from .templating import render_template, render_template_string
|
||||
|
||||
# the signals
|
||||
from .signals import signals_available, template_rendered, request_started, \
|
||||
request_finished, got_request_exception, request_tearing_down, \
|
||||
appcontext_tearing_down, appcontext_pushed, \
|
||||
appcontext_popped, message_flashed, before_render_template
|
||||
from .signals import (
|
||||
signals_available,
|
||||
template_rendered,
|
||||
request_started,
|
||||
request_finished,
|
||||
got_request_exception,
|
||||
request_tearing_down,
|
||||
appcontext_tearing_down,
|
||||
appcontext_pushed,
|
||||
appcontext_popped,
|
||||
message_flashed,
|
||||
before_render_template,
|
||||
)
|
||||
|
||||
# We're not exposing the actual json module but a convenient wrapper around
|
||||
# it.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
from .cli import main
|
||||
|
||||
main(as_module=True)
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ else:
|
|||
from cStringIO import StringIO
|
||||
import collections as collections_abc
|
||||
|
||||
exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
|
||||
exec("def reraise(tp, value, tb=None):\n raise tp, value, tb")
|
||||
|
||||
def implements_to_string(cls):
|
||||
cls.__unicode__ = cls.__str__
|
||||
cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
|
||||
cls.__str__ = lambda x: x.__unicode__().encode("utf-8")
|
||||
return cls
|
||||
|
||||
|
||||
|
|
@ -66,7 +66,8 @@ def with_metaclass(meta, *bases):
|
|||
class metaclass(type):
|
||||
def __new__(cls, name, this_bases, d):
|
||||
return meta(name, bases, d)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
return type.__new__(metaclass, "temporary_class", (), {})
|
||||
|
||||
|
||||
# Certain versions of pypy have a bug where clearing the exception stack
|
||||
|
|
@ -81,14 +82,17 @@ def with_metaclass(meta, *bases):
|
|||
#
|
||||
# Ubuntu 14.04 has PyPy 2.2.1, which does exhibit this bug.
|
||||
BROKEN_PYPY_CTXMGR_EXIT = False
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
if hasattr(sys, "pypy_version_info"):
|
||||
|
||||
class _Mgr(object):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args):
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
if hasattr(sys, "exc_clear"):
|
||||
# Python 3 (PyPy3) doesn't have exc_clear
|
||||
sys.exc_clear()
|
||||
|
||||
try:
|
||||
try:
|
||||
with _Mgr():
|
||||
|
|
@ -107,4 +111,4 @@ except ImportError:
|
|||
# Backwards compatibility as proposed in PEP 0519:
|
||||
# https://www.python.org/dev/peps/pep-0519/#backwards-compatibility
|
||||
def fspath(path):
|
||||
return path.__fspath__() if hasattr(path, '__fspath__') else path
|
||||
return path.__fspath__() if hasattr(path, "__fspath__") else path
|
||||
|
|
|
|||
428
flask/app.py
428
flask/app.py
|
|
@ -18,10 +18,15 @@ from itertools import chain
|
|||
from threading import Lock
|
||||
|
||||
from werkzeug.datastructures import Headers, ImmutableDict
|
||||
from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \
|
||||
InternalServerError, MethodNotAllowed, default_exceptions
|
||||
from werkzeug.routing import BuildError, Map, RequestRedirect, \
|
||||
RoutingException, Rule
|
||||
from werkzeug.exceptions import (
|
||||
BadRequest,
|
||||
BadRequestKeyError,
|
||||
HTTPException,
|
||||
InternalServerError,
|
||||
MethodNotAllowed,
|
||||
default_exceptions,
|
||||
)
|
||||
from werkzeug.routing import BuildError, Map, RequestRedirect, RoutingException, Rule
|
||||
|
||||
from . import cli, json
|
||||
from ._compat import integer_types, reraise, string_types, text_type
|
||||
|
|
@ -30,15 +35,29 @@ from .ctx import AppContext, RequestContext, _AppCtxGlobals
|
|||
from .globals import _request_ctx_stack, g, request, session
|
||||
from .helpers import (
|
||||
_PackageBoundObject,
|
||||
_endpoint_from_view_func, find_package, get_env, get_debug_flag,
|
||||
get_flashed_messages, locked_cached_property, url_for, get_load_dotenv
|
||||
_endpoint_from_view_func,
|
||||
find_package,
|
||||
get_env,
|
||||
get_debug_flag,
|
||||
get_flashed_messages,
|
||||
locked_cached_property,
|
||||
url_for,
|
||||
get_load_dotenv,
|
||||
)
|
||||
from .logging import create_logger
|
||||
from .sessions import SecureCookieSessionInterface
|
||||
from .signals import appcontext_tearing_down, got_request_exception, \
|
||||
request_finished, request_started, request_tearing_down
|
||||
from .templating import DispatchingJinjaLoader, Environment, \
|
||||
_default_template_ctx_processor
|
||||
from .signals import (
|
||||
appcontext_tearing_down,
|
||||
got_request_exception,
|
||||
request_finished,
|
||||
request_started,
|
||||
request_tearing_down,
|
||||
)
|
||||
from .templating import (
|
||||
DispatchingJinjaLoader,
|
||||
Environment,
|
||||
_default_template_ctx_processor,
|
||||
)
|
||||
from .wrappers import Request, Response
|
||||
|
||||
# a singleton sentinel value for parameter defaults
|
||||
|
|
@ -55,16 +74,20 @@ def setupmethod(f):
|
|||
"""Wraps a method so that it performs a check in debug mode if the
|
||||
first request was already handled.
|
||||
"""
|
||||
|
||||
def wrapper_func(self, *args, **kwargs):
|
||||
if self.debug and self._got_first_request:
|
||||
raise AssertionError('A setup function was called after the '
|
||||
'first request was handled. This usually indicates a bug '
|
||||
'in the application where a module was not imported '
|
||||
'and decorators or other functionality was called too late.\n'
|
||||
'To fix this make sure to import all your view modules, '
|
||||
'database models and everything related at a central place '
|
||||
'before the application starts serving requests.')
|
||||
raise AssertionError(
|
||||
"A setup function was called after the "
|
||||
"first request was handled. This usually indicates a bug "
|
||||
"in the application where a module was not imported "
|
||||
"and decorators or other functionality was called too late.\n"
|
||||
"To fix this make sure to import all your view modules, "
|
||||
"database models and everything related at a central place "
|
||||
"before the application starts serving requests."
|
||||
)
|
||||
return f(self, *args, **kwargs)
|
||||
|
||||
return update_wrapper(wrapper_func, f)
|
||||
|
||||
|
||||
|
|
@ -217,7 +240,7 @@ class Flask(_PackageBoundObject):
|
|||
#:
|
||||
#: This attribute can also be configured from the config with the
|
||||
#: ``TESTING`` configuration key. Defaults to ``False``.
|
||||
testing = ConfigAttribute('TESTING')
|
||||
testing = ConfigAttribute("TESTING")
|
||||
|
||||
#: If a secret key is set, cryptographic components can use this to
|
||||
#: sign cookies and other things. Set this to a complex random value
|
||||
|
|
@ -225,13 +248,13 @@ class Flask(_PackageBoundObject):
|
|||
#:
|
||||
#: This attribute can also be configured from the config with the
|
||||
#: :data:`SECRET_KEY` configuration key. Defaults to ``None``.
|
||||
secret_key = ConfigAttribute('SECRET_KEY')
|
||||
secret_key = ConfigAttribute("SECRET_KEY")
|
||||
|
||||
#: The secure cookie uses this for the name of the session cookie.
|
||||
#:
|
||||
#: This attribute can also be configured from the config with the
|
||||
#: ``SESSION_COOKIE_NAME`` configuration key. Defaults to ``'session'``
|
||||
session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME')
|
||||
session_cookie_name = ConfigAttribute("SESSION_COOKIE_NAME")
|
||||
|
||||
#: A :class:`~datetime.timedelta` which is used to set the expiration
|
||||
#: date of a permanent session. The default is 31 days which makes a
|
||||
|
|
@ -240,8 +263,9 @@ class Flask(_PackageBoundObject):
|
|||
#: This attribute can also be configured from the config with the
|
||||
#: ``PERMANENT_SESSION_LIFETIME`` configuration key. Defaults to
|
||||
#: ``timedelta(days=31)``
|
||||
permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME',
|
||||
get_converter=_make_timedelta)
|
||||
permanent_session_lifetime = ConfigAttribute(
|
||||
"PERMANENT_SESSION_LIFETIME", get_converter=_make_timedelta
|
||||
)
|
||||
|
||||
#: A :class:`~datetime.timedelta` which is used as default cache_timeout
|
||||
#: for the :func:`send_file` functions. The default is 12 hours.
|
||||
|
|
@ -250,8 +274,9 @@ class Flask(_PackageBoundObject):
|
|||
#: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration
|
||||
#: variable can also be set with an integer value used as seconds.
|
||||
#: Defaults to ``timedelta(hours=12)``
|
||||
send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT',
|
||||
get_converter=_make_timedelta)
|
||||
send_file_max_age_default = ConfigAttribute(
|
||||
"SEND_FILE_MAX_AGE_DEFAULT", get_converter=_make_timedelta
|
||||
)
|
||||
|
||||
#: Enable this if you want to use the X-Sendfile feature. Keep in
|
||||
#: mind that the server has to support this. This only affects files
|
||||
|
|
@ -261,7 +286,7 @@ class Flask(_PackageBoundObject):
|
|||
#:
|
||||
#: This attribute can also be configured from the config with the
|
||||
#: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``.
|
||||
use_x_sendfile = ConfigAttribute('USE_X_SENDFILE')
|
||||
use_x_sendfile = ConfigAttribute("USE_X_SENDFILE")
|
||||
|
||||
#: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`.
|
||||
#:
|
||||
|
|
@ -275,41 +300,43 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
#: Options that are passed directly to the Jinja2 environment.
|
||||
jinja_options = ImmutableDict(
|
||||
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
|
||||
extensions=["jinja2.ext.autoescape", "jinja2.ext.with_"]
|
||||
)
|
||||
|
||||
#: Default configuration parameters.
|
||||
default_config = ImmutableDict({
|
||||
'ENV': None,
|
||||
'DEBUG': None,
|
||||
'TESTING': False,
|
||||
'PROPAGATE_EXCEPTIONS': None,
|
||||
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
|
||||
'SECRET_KEY': None,
|
||||
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
|
||||
'USE_X_SENDFILE': False,
|
||||
'SERVER_NAME': None,
|
||||
'APPLICATION_ROOT': '/',
|
||||
'SESSION_COOKIE_NAME': 'session',
|
||||
'SESSION_COOKIE_DOMAIN': None,
|
||||
'SESSION_COOKIE_PATH': None,
|
||||
'SESSION_COOKIE_HTTPONLY': True,
|
||||
'SESSION_COOKIE_SECURE': False,
|
||||
'SESSION_COOKIE_SAMESITE': None,
|
||||
'SESSION_REFRESH_EACH_REQUEST': True,
|
||||
'MAX_CONTENT_LENGTH': None,
|
||||
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
|
||||
'TRAP_BAD_REQUEST_ERRORS': None,
|
||||
'TRAP_HTTP_EXCEPTIONS': False,
|
||||
'EXPLAIN_TEMPLATE_LOADING': False,
|
||||
'PREFERRED_URL_SCHEME': 'http',
|
||||
'JSON_AS_ASCII': True,
|
||||
'JSON_SORT_KEYS': True,
|
||||
'JSONIFY_PRETTYPRINT_REGULAR': False,
|
||||
'JSONIFY_MIMETYPE': 'application/json',
|
||||
'TEMPLATES_AUTO_RELOAD': None,
|
||||
'MAX_COOKIE_SIZE': 4093,
|
||||
})
|
||||
default_config = ImmutableDict(
|
||||
{
|
||||
"ENV": None,
|
||||
"DEBUG": None,
|
||||
"TESTING": False,
|
||||
"PROPAGATE_EXCEPTIONS": None,
|
||||
"PRESERVE_CONTEXT_ON_EXCEPTION": None,
|
||||
"SECRET_KEY": None,
|
||||
"PERMANENT_SESSION_LIFETIME": timedelta(days=31),
|
||||
"USE_X_SENDFILE": False,
|
||||
"SERVER_NAME": None,
|
||||
"APPLICATION_ROOT": "/",
|
||||
"SESSION_COOKIE_NAME": "session",
|
||||
"SESSION_COOKIE_DOMAIN": None,
|
||||
"SESSION_COOKIE_PATH": None,
|
||||
"SESSION_COOKIE_HTTPONLY": True,
|
||||
"SESSION_COOKIE_SECURE": False,
|
||||
"SESSION_COOKIE_SAMESITE": None,
|
||||
"SESSION_REFRESH_EACH_REQUEST": True,
|
||||
"MAX_CONTENT_LENGTH": None,
|
||||
"SEND_FILE_MAX_AGE_DEFAULT": timedelta(hours=12),
|
||||
"TRAP_BAD_REQUEST_ERRORS": None,
|
||||
"TRAP_HTTP_EXCEPTIONS": False,
|
||||
"EXPLAIN_TEMPLATE_LOADING": False,
|
||||
"PREFERRED_URL_SCHEME": "http",
|
||||
"JSON_AS_ASCII": True,
|
||||
"JSON_SORT_KEYS": True,
|
||||
"JSONIFY_PRETTYPRINT_REGULAR": False,
|
||||
"JSONIFY_MIMETYPE": "application/json",
|
||||
"TEMPLATES_AUTO_RELOAD": None,
|
||||
"MAX_COOKIE_SIZE": 4093,
|
||||
}
|
||||
)
|
||||
|
||||
#: The rule object to use for URL rules created. This is used by
|
||||
#: :meth:`add_url_rule`. Defaults to :class:`werkzeug.routing.Rule`.
|
||||
|
|
@ -355,20 +382,17 @@ class Flask(_PackageBoundObject):
|
|||
self,
|
||||
import_name,
|
||||
static_url_path=None,
|
||||
static_folder='static',
|
||||
static_folder="static",
|
||||
static_host=None,
|
||||
host_matching=False,
|
||||
subdomain_matching=False,
|
||||
template_folder='templates',
|
||||
template_folder="templates",
|
||||
instance_path=None,
|
||||
instance_relative_config=False,
|
||||
root_path=None
|
||||
root_path=None,
|
||||
):
|
||||
_PackageBoundObject.__init__(
|
||||
self,
|
||||
import_name,
|
||||
template_folder=template_folder,
|
||||
root_path=root_path
|
||||
self, import_name, template_folder=template_folder, root_path=root_path
|
||||
)
|
||||
|
||||
if static_url_path is not None:
|
||||
|
|
@ -381,8 +405,8 @@ class Flask(_PackageBoundObject):
|
|||
instance_path = self.auto_find_instance_path()
|
||||
elif not os.path.isabs(instance_path):
|
||||
raise ValueError(
|
||||
'If an instance path is provided it must be absolute.'
|
||||
' A relative path was given instead.'
|
||||
"If an instance path is provided it must be absolute."
|
||||
" A relative path was given instead."
|
||||
)
|
||||
|
||||
#: Holds the path to the instance folder.
|
||||
|
|
@ -490,9 +514,7 @@ class Flask(_PackageBoundObject):
|
|||
#: requests. Each returns a dictionary that the template context is
|
||||
#: updated with. To register a function here, use the
|
||||
#: :meth:`context_processor` decorator.
|
||||
self.template_context_processors = {
|
||||
None: [_default_template_ctx_processor]
|
||||
}
|
||||
self.template_context_processors = {None: [_default_template_ctx_processor]}
|
||||
|
||||
#: A list of shell context processor functions that should be run
|
||||
#: when a shell context is created.
|
||||
|
|
@ -555,12 +577,14 @@ class Flask(_PackageBoundObject):
|
|||
# For one, it might be created while the server is running (e.g. during
|
||||
# development). Also, Google App Engine stores static files somewhere
|
||||
if self.has_static_folder:
|
||||
assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination'
|
||||
assert (
|
||||
bool(static_host) == host_matching
|
||||
), "Invalid static_host/host_matching combination"
|
||||
self.add_url_rule(
|
||||
self.static_url_path + '/<path:filename>',
|
||||
endpoint='static',
|
||||
self.static_url_path + "/<path:filename>",
|
||||
endpoint="static",
|
||||
host=static_host,
|
||||
view_func=self.send_static_file
|
||||
view_func=self.send_static_file,
|
||||
)
|
||||
|
||||
#: The click command line context for this application. Commands
|
||||
|
|
@ -581,10 +605,10 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
if self.import_name == '__main__':
|
||||
fn = getattr(sys.modules['__main__'], '__file__', None)
|
||||
if self.import_name == "__main__":
|
||||
fn = getattr(sys.modules["__main__"], "__file__", None)
|
||||
if fn is None:
|
||||
return '__main__'
|
||||
return "__main__"
|
||||
return os.path.splitext(os.path.basename(fn))[0]
|
||||
return self.import_name
|
||||
|
||||
|
|
@ -595,7 +619,7 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
rv = self.config['PROPAGATE_EXCEPTIONS']
|
||||
rv = self.config["PROPAGATE_EXCEPTIONS"]
|
||||
if rv is not None:
|
||||
return rv
|
||||
return self.testing or self.debug
|
||||
|
|
@ -608,7 +632,7 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.7
|
||||
"""
|
||||
rv = self.config['PRESERVE_CONTEXT_ON_EXCEPTION']
|
||||
rv = self.config["PRESERVE_CONTEXT_ON_EXCEPTION"]
|
||||
if rv is not None:
|
||||
return rv
|
||||
return self.debug
|
||||
|
|
@ -663,8 +687,8 @@ class Flask(_PackageBoundObject):
|
|||
if instance_relative:
|
||||
root_path = self.instance_path
|
||||
defaults = dict(self.default_config)
|
||||
defaults['ENV'] = get_env()
|
||||
defaults['DEBUG'] = get_debug_flag()
|
||||
defaults["ENV"] = get_env()
|
||||
defaults["DEBUG"] = get_debug_flag()
|
||||
return self.config_class(root_path, defaults)
|
||||
|
||||
def auto_find_instance_path(self):
|
||||
|
|
@ -677,10 +701,10 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
prefix, package_path = find_package(self.import_name)
|
||||
if prefix is None:
|
||||
return os.path.join(package_path, 'instance')
|
||||
return os.path.join(prefix, 'var', self.name + '-instance')
|
||||
return os.path.join(package_path, "instance")
|
||||
return os.path.join(prefix, "var", self.name + "-instance")
|
||||
|
||||
def open_instance_resource(self, resource, mode='rb'):
|
||||
def open_instance_resource(self, resource, mode="rb"):
|
||||
"""Opens a resource from the application's instance folder
|
||||
(:attr:`instance_path`). Otherwise works like
|
||||
:meth:`open_resource`. Instance resources can also be opened for
|
||||
|
|
@ -703,11 +727,11 @@ class Flask(_PackageBoundObject):
|
|||
This property was added but the underlying config and behavior
|
||||
already existed.
|
||||
"""
|
||||
rv = self.config['TEMPLATES_AUTO_RELOAD']
|
||||
rv = self.config["TEMPLATES_AUTO_RELOAD"]
|
||||
return rv if rv is not None else self.debug
|
||||
|
||||
def _set_templates_auto_reload(self, value):
|
||||
self.config['TEMPLATES_AUTO_RELOAD'] = value
|
||||
self.config["TEMPLATES_AUTO_RELOAD"] = value
|
||||
|
||||
templates_auto_reload = property(
|
||||
_get_templates_auto_reload, _set_templates_auto_reload
|
||||
|
|
@ -727,11 +751,11 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
options = dict(self.jinja_options)
|
||||
|
||||
if 'autoescape' not in options:
|
||||
options['autoescape'] = self.select_jinja_autoescape
|
||||
if "autoescape" not in options:
|
||||
options["autoescape"] = self.select_jinja_autoescape
|
||||
|
||||
if 'auto_reload' not in options:
|
||||
options['auto_reload'] = self.templates_auto_reload
|
||||
if "auto_reload" not in options:
|
||||
options["auto_reload"] = self.templates_auto_reload
|
||||
|
||||
rv = self.jinja_environment(self, **options)
|
||||
rv.globals.update(
|
||||
|
|
@ -743,9 +767,9 @@ class Flask(_PackageBoundObject):
|
|||
# templates we also want the proxies in there.
|
||||
request=request,
|
||||
session=session,
|
||||
g=g
|
||||
g=g,
|
||||
)
|
||||
rv.filters['tojson'] = json.tojson_filter
|
||||
rv.filters["tojson"] = json.tojson_filter
|
||||
return rv
|
||||
|
||||
def create_global_jinja_loader(self):
|
||||
|
|
@ -769,7 +793,7 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
if filename is None:
|
||||
return True
|
||||
return filename.endswith(('.html', '.htm', '.xml', '.xhtml'))
|
||||
return filename.endswith((".html", ".htm", ".xml", ".xhtml"))
|
||||
|
||||
def update_template_context(self, context):
|
||||
"""Update the template context with some commonly used variables.
|
||||
|
|
@ -803,7 +827,7 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
rv = {'app': self, 'g': g}
|
||||
rv = {"app": self, "g": g}
|
||||
for processor in self.shell_context_processors:
|
||||
rv.update(processor())
|
||||
return rv
|
||||
|
|
@ -817,13 +841,13 @@ class Flask(_PackageBoundObject):
|
|||
#: **Do not enable development when deploying in production.**
|
||||
#:
|
||||
#: Default: ``'production'``
|
||||
env = ConfigAttribute('ENV')
|
||||
env = ConfigAttribute("ENV")
|
||||
|
||||
def _get_debug(self):
|
||||
return self.config['DEBUG']
|
||||
return self.config["DEBUG"]
|
||||
|
||||
def _set_debug(self, value):
|
||||
self.config['DEBUG'] = value
|
||||
self.config["DEBUG"] = value
|
||||
self.jinja_env.auto_reload = self.templates_auto_reload
|
||||
|
||||
#: Whether debug mode is enabled. When using ``flask run`` to start
|
||||
|
|
@ -841,8 +865,7 @@ class Flask(_PackageBoundObject):
|
|||
debug = property(_get_debug, _set_debug)
|
||||
del _get_debug, _set_debug
|
||||
|
||||
def run(self, host=None, port=None, debug=None,
|
||||
load_dotenv=True, **options):
|
||||
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
|
||||
"""Runs the application on a local development server.
|
||||
|
||||
Do not use ``run()`` in a production setting. It is not intended to
|
||||
|
|
@ -902,8 +925,9 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
# Change this into a no-op if the server is invoked from the
|
||||
# command line. Have a look at cli.py for more information.
|
||||
if os.environ.get('FLASK_RUN_FROM_CLI') == 'true':
|
||||
if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
|
||||
from .debughelpers import explain_ignored_app_run
|
||||
|
||||
explain_ignored_app_run()
|
||||
return
|
||||
|
||||
|
|
@ -911,30 +935,30 @@ class Flask(_PackageBoundObject):
|
|||
cli.load_dotenv()
|
||||
|
||||
# if set, let env vars override previous values
|
||||
if 'FLASK_ENV' in os.environ:
|
||||
if "FLASK_ENV" in os.environ:
|
||||
self.env = get_env()
|
||||
self.debug = get_debug_flag()
|
||||
elif 'FLASK_DEBUG' in os.environ:
|
||||
elif "FLASK_DEBUG" in os.environ:
|
||||
self.debug = get_debug_flag()
|
||||
|
||||
# debug passed to method overrides all other sources
|
||||
if debug is not None:
|
||||
self.debug = bool(debug)
|
||||
|
||||
_host = '127.0.0.1'
|
||||
_host = "127.0.0.1"
|
||||
_port = 5000
|
||||
server_name = self.config.get('SERVER_NAME')
|
||||
server_name = self.config.get("SERVER_NAME")
|
||||
sn_host, sn_port = None, None
|
||||
|
||||
if server_name:
|
||||
sn_host, _, sn_port = server_name.partition(':')
|
||||
sn_host, _, sn_port = server_name.partition(":")
|
||||
|
||||
host = host or sn_host or _host
|
||||
port = int(port or sn_port or _port)
|
||||
|
||||
options.setdefault('use_reloader', self.debug)
|
||||
options.setdefault('use_debugger', self.debug)
|
||||
options.setdefault('threaded', True)
|
||||
options.setdefault("use_reloader", self.debug)
|
||||
options.setdefault("use_debugger", self.debug)
|
||||
options.setdefault("threaded", True)
|
||||
|
||||
cli.show_server_banner(self.env, self.debug, self.name, False)
|
||||
|
||||
|
|
@ -1034,10 +1058,12 @@ class Flask(_PackageBoundObject):
|
|||
:param request: an instance of :attr:`request_class`.
|
||||
"""
|
||||
|
||||
warnings.warn(DeprecationWarning(
|
||||
'"open_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.open_session" instead.'
|
||||
))
|
||||
warnings.warn(
|
||||
DeprecationWarning(
|
||||
'"open_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.open_session" instead.'
|
||||
)
|
||||
)
|
||||
return self.session_interface.open_session(self, request)
|
||||
|
||||
def save_session(self, session, response):
|
||||
|
|
@ -1055,10 +1081,12 @@ class Flask(_PackageBoundObject):
|
|||
:param response: an instance of :attr:`response_class`
|
||||
"""
|
||||
|
||||
warnings.warn(DeprecationWarning(
|
||||
'"save_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.save_session" instead.'
|
||||
))
|
||||
warnings.warn(
|
||||
DeprecationWarning(
|
||||
'"save_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.save_session" instead.'
|
||||
)
|
||||
)
|
||||
return self.session_interface.save_session(self, session, response)
|
||||
|
||||
def make_null_session(self):
|
||||
|
|
@ -1072,10 +1100,12 @@ class Flask(_PackageBoundObject):
|
|||
.. versionadded:: 0.7
|
||||
"""
|
||||
|
||||
warnings.warn(DeprecationWarning(
|
||||
'"make_null_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.make_null_session" instead.'
|
||||
))
|
||||
warnings.warn(
|
||||
DeprecationWarning(
|
||||
'"make_null_session" is deprecated and will be removed in 1.1. Use'
|
||||
' "session_interface.make_null_session" instead.'
|
||||
)
|
||||
)
|
||||
return self.session_interface.make_null_session(self)
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1102,11 +1132,10 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
if blueprint.name in self.blueprints:
|
||||
assert self.blueprints[blueprint.name] is blueprint, (
|
||||
'A name collision occurred between blueprints %r and %r. Both'
|
||||
"A name collision occurred between blueprints %r and %r. Both"
|
||||
' share the same name "%s". Blueprints that are created on the'
|
||||
' fly need unique names.' % (
|
||||
blueprint, self.blueprints[blueprint.name], blueprint.name
|
||||
)
|
||||
" fly need unique names."
|
||||
% (blueprint, self.blueprints[blueprint.name], blueprint.name)
|
||||
)
|
||||
else:
|
||||
self.blueprints[blueprint.name] = blueprint
|
||||
|
|
@ -1123,8 +1152,14 @@ class Flask(_PackageBoundObject):
|
|||
return iter(self._blueprint_order)
|
||||
|
||||
@setupmethod
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None,
|
||||
provide_automatic_options=None, **options):
|
||||
def add_url_rule(
|
||||
self,
|
||||
rule,
|
||||
endpoint=None,
|
||||
view_func=None,
|
||||
provide_automatic_options=None,
|
||||
**options
|
||||
):
|
||||
"""Connects a URL rule. Works exactly like the :meth:`route`
|
||||
decorator. If a view_func is provided it will be registered with the
|
||||
endpoint.
|
||||
|
|
@ -1179,32 +1214,35 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
if endpoint is None:
|
||||
endpoint = _endpoint_from_view_func(view_func)
|
||||
options['endpoint'] = endpoint
|
||||
methods = options.pop('methods', None)
|
||||
options["endpoint"] = endpoint
|
||||
methods = options.pop("methods", None)
|
||||
|
||||
# if the methods are not given and the view_func object knows its
|
||||
# methods we can use that instead. If neither exists, we go with
|
||||
# a tuple of only ``GET`` as default.
|
||||
if methods is None:
|
||||
methods = getattr(view_func, 'methods', None) or ('GET',)
|
||||
methods = getattr(view_func, "methods", None) or ("GET",)
|
||||
if isinstance(methods, string_types):
|
||||
raise TypeError('Allowed methods have to be iterables of strings, '
|
||||
'for example: @app.route(..., methods=["POST"])')
|
||||
raise TypeError(
|
||||
"Allowed methods have to be iterables of strings, "
|
||||
'for example: @app.route(..., methods=["POST"])'
|
||||
)
|
||||
methods = set(item.upper() for item in methods)
|
||||
|
||||
# Methods that should always be added
|
||||
required_methods = set(getattr(view_func, 'required_methods', ()))
|
||||
required_methods = set(getattr(view_func, "required_methods", ()))
|
||||
|
||||
# starting with Flask 0.8 the view_func object can disable and
|
||||
# force-enable the automatic options handling.
|
||||
if provide_automatic_options is None:
|
||||
provide_automatic_options = getattr(view_func,
|
||||
'provide_automatic_options', None)
|
||||
provide_automatic_options = getattr(
|
||||
view_func, "provide_automatic_options", None
|
||||
)
|
||||
|
||||
if provide_automatic_options is None:
|
||||
if 'OPTIONS' not in methods:
|
||||
if "OPTIONS" not in methods:
|
||||
provide_automatic_options = True
|
||||
required_methods.add('OPTIONS')
|
||||
required_methods.add("OPTIONS")
|
||||
else:
|
||||
provide_automatic_options = False
|
||||
|
||||
|
|
@ -1218,8 +1256,10 @@ class Flask(_PackageBoundObject):
|
|||
if view_func is not None:
|
||||
old_func = self.view_functions.get(endpoint)
|
||||
if old_func is not None and old_func != view_func:
|
||||
raise AssertionError('View function mapping is overwriting an '
|
||||
'existing endpoint function: %s' % endpoint)
|
||||
raise AssertionError(
|
||||
"View function mapping is overwriting an "
|
||||
"existing endpoint function: %s" % endpoint
|
||||
)
|
||||
self.view_functions[endpoint] = view_func
|
||||
|
||||
def route(self, rule, **options):
|
||||
|
|
@ -1246,10 +1286,12 @@ class Flask(_PackageBoundObject):
|
|||
Starting with Flask 0.6, ``OPTIONS`` is implicitly
|
||||
added and handled by the standard request handling.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
endpoint = options.pop('endpoint', None)
|
||||
endpoint = options.pop("endpoint", None)
|
||||
self.add_url_rule(rule, endpoint, f, **options)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1263,9 +1305,11 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
:param endpoint: the name of the endpoint
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.view_functions[endpoint] = f
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -1313,9 +1357,11 @@ class Flask(_PackageBoundObject):
|
|||
:param code_or_exception: the code as integer for the handler, or
|
||||
an arbitrary exception
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self._register_error_handler(None, code_or_exception, f)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1337,9 +1383,9 @@ class Flask(_PackageBoundObject):
|
|||
"""
|
||||
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
||||
raise ValueError(
|
||||
'Tried to register a handler for an exception instance {0!r}.'
|
||||
' Handlers can only be registered for exception classes or'
|
||||
' HTTP error codes.'.format(code_or_exception)
|
||||
"Tried to register a handler for an exception instance {0!r}."
|
||||
" Handlers can only be registered for exception classes or"
|
||||
" HTTP error codes.".format(code_or_exception)
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
@ -1366,9 +1412,11 @@ class Flask(_PackageBoundObject):
|
|||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_template_filter(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1401,9 +1449,11 @@ class Flask(_PackageBoundObject):
|
|||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_template_test(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1433,9 +1483,11 @@ class Flask(_PackageBoundObject):
|
|||
:param name: the optional name of the global function, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_template_global(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
@setupmethod
|
||||
|
|
@ -1613,8 +1665,10 @@ class Flask(_PackageBoundObject):
|
|||
exc_class, code = self._get_exc_class_and_code(type(e))
|
||||
|
||||
for name, c in (
|
||||
(request.blueprint, code), (None, code),
|
||||
(request.blueprint, None), (None, None)
|
||||
(request.blueprint, code),
|
||||
(None, code),
|
||||
(request.blueprint, None),
|
||||
(None, None),
|
||||
):
|
||||
handler_map = self.error_handler_spec.setdefault(name, {}).get(c)
|
||||
|
||||
|
|
@ -1677,14 +1731,15 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
if self.config['TRAP_HTTP_EXCEPTIONS']:
|
||||
if self.config["TRAP_HTTP_EXCEPTIONS"]:
|
||||
return True
|
||||
|
||||
trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS']
|
||||
trap_bad_request = self.config["TRAP_BAD_REQUEST_ERRORS"]
|
||||
|
||||
# if unset, trap key errors in debug mode
|
||||
if (
|
||||
trap_bad_request is None and self.debug
|
||||
trap_bad_request is None
|
||||
and self.debug
|
||||
and isinstance(e, BadRequestKeyError)
|
||||
):
|
||||
return True
|
||||
|
|
@ -1773,10 +1828,9 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.8
|
||||
"""
|
||||
self.logger.error('Exception on %s [%s]' % (
|
||||
request.path,
|
||||
request.method
|
||||
), exc_info=exc_info)
|
||||
self.logger.error(
|
||||
"Exception on %s [%s]" % (request.path, request.method), exc_info=exc_info
|
||||
)
|
||||
|
||||
def raise_routing_exception(self, request):
|
||||
"""Exceptions that are recording during routing are reraised with
|
||||
|
|
@ -1786,12 +1840,15 @@ class Flask(_PackageBoundObject):
|
|||
|
||||
:internal:
|
||||
"""
|
||||
if not self.debug \
|
||||
or not isinstance(request.routing_exception, RequestRedirect) \
|
||||
or request.method in ('GET', 'HEAD', 'OPTIONS'):
|
||||
if (
|
||||
not self.debug
|
||||
or not isinstance(request.routing_exception, RequestRedirect)
|
||||
or request.method in ("GET", "HEAD", "OPTIONS")
|
||||
):
|
||||
raise request.routing_exception
|
||||
|
||||
from .debughelpers import FormDataRoutingRedirect
|
||||
|
||||
raise FormDataRoutingRedirect(request)
|
||||
|
||||
def dispatch_request(self):
|
||||
|
|
@ -1810,8 +1867,10 @@ class Flask(_PackageBoundObject):
|
|||
rule = req.url_rule
|
||||
# if we provide automatic options for this URL and the
|
||||
# request came with the OPTIONS method, reply automatically
|
||||
if getattr(rule, 'provide_automatic_options', False) \
|
||||
and req.method == 'OPTIONS':
|
||||
if (
|
||||
getattr(rule, "provide_automatic_options", False)
|
||||
and req.method == "OPTIONS"
|
||||
):
|
||||
return self.make_default_options_response()
|
||||
# otherwise dispatch to the handler for that endpoint
|
||||
return self.view_functions[rule.endpoint](**req.view_args)
|
||||
|
|
@ -1853,8 +1912,9 @@ class Flask(_PackageBoundObject):
|
|||
except Exception:
|
||||
if not from_error_handler:
|
||||
raise
|
||||
self.logger.exception('Request finalizing failed with an '
|
||||
'error while handling an error')
|
||||
self.logger.exception(
|
||||
"Request finalizing failed with an " "error while handling an error"
|
||||
)
|
||||
return response
|
||||
|
||||
def try_trigger_before_first_request_functions(self):
|
||||
|
|
@ -1881,13 +1941,13 @@ class Flask(_PackageBoundObject):
|
|||
.. versionadded:: 0.7
|
||||
"""
|
||||
adapter = _request_ctx_stack.top.url_adapter
|
||||
if hasattr(adapter, 'allowed_methods'):
|
||||
if hasattr(adapter, "allowed_methods"):
|
||||
methods = adapter.allowed_methods()
|
||||
else:
|
||||
# fallback for Werkzeug < 0.7
|
||||
methods = []
|
||||
try:
|
||||
adapter.match(method='--')
|
||||
adapter.match(method="--")
|
||||
except MethodNotAllowed as e:
|
||||
methods = e.valid_methods
|
||||
except HTTPException as e:
|
||||
|
|
@ -1964,17 +2024,17 @@ class Flask(_PackageBoundObject):
|
|||
# other sized tuples are not allowed
|
||||
else:
|
||||
raise TypeError(
|
||||
'The view function did not return a valid response tuple.'
|
||||
' The tuple must have the form (body, status, headers),'
|
||||
' (body, status), or (body, headers).'
|
||||
"The view function did not return a valid response tuple."
|
||||
" The tuple must have the form (body, status, headers),"
|
||||
" (body, status), or (body, headers)."
|
||||
)
|
||||
|
||||
# the body must not be None
|
||||
if rv is None:
|
||||
raise TypeError(
|
||||
'The view function did not return a valid response. The'
|
||||
' function either returned None or ended without a return'
|
||||
' statement.'
|
||||
"The view function did not return a valid response. The"
|
||||
" function either returned None or ended without a return"
|
||||
" statement."
|
||||
)
|
||||
|
||||
# make sure the body is an instance of the response class
|
||||
|
|
@ -1992,10 +2052,10 @@ class Flask(_PackageBoundObject):
|
|||
rv = self.response_class.force_type(rv, request.environ)
|
||||
except TypeError as e:
|
||||
new_error = TypeError(
|
||||
'{e}\nThe view function did not return a valid'
|
||||
' response. The return type must be a string, tuple,'
|
||||
' Response instance, or WSGI callable, but it was a'
|
||||
' {rv.__class__.__name__}.'.format(e=e, rv=rv)
|
||||
"{e}\nThe view function did not return a valid"
|
||||
" response. The return type must be a string, tuple,"
|
||||
" Response instance, or WSGI callable, but it was a"
|
||||
" {rv.__class__.__name__}.".format(e=e, rv=rv)
|
||||
)
|
||||
reraise(TypeError, new_error, sys.exc_info()[2])
|
||||
|
||||
|
|
@ -2031,19 +2091,24 @@ class Flask(_PackageBoundObject):
|
|||
# If subdomain matching is disabled (the default), use the
|
||||
# default subdomain in all cases. This should be the default
|
||||
# in Werkzeug but it currently does not have that feature.
|
||||
subdomain = ((self.url_map.default_subdomain or None)
|
||||
if not self.subdomain_matching else None)
|
||||
subdomain = (
|
||||
(self.url_map.default_subdomain or None)
|
||||
if not self.subdomain_matching
|
||||
else None
|
||||
)
|
||||
return self.url_map.bind_to_environ(
|
||||
request.environ,
|
||||
server_name=self.config['SERVER_NAME'],
|
||||
subdomain=subdomain)
|
||||
server_name=self.config["SERVER_NAME"],
|
||||
subdomain=subdomain,
|
||||
)
|
||||
# We need at the very least the server name to be set for this
|
||||
# to work.
|
||||
if self.config['SERVER_NAME'] is not None:
|
||||
if self.config["SERVER_NAME"] is not None:
|
||||
return self.url_map.bind(
|
||||
self.config['SERVER_NAME'],
|
||||
script_name=self.config['APPLICATION_ROOT'],
|
||||
url_scheme=self.config['PREFERRED_URL_SCHEME'])
|
||||
self.config["SERVER_NAME"],
|
||||
script_name=self.config["APPLICATION_ROOT"],
|
||||
url_scheme=self.config["PREFERRED_URL_SCHEME"],
|
||||
)
|
||||
|
||||
def inject_url_defaults(self, endpoint, values):
|
||||
"""Injects the URL defaults for the given endpoint directly into
|
||||
|
|
@ -2053,8 +2118,8 @@ class Flask(_PackageBoundObject):
|
|||
.. versionadded:: 0.7
|
||||
"""
|
||||
funcs = self.url_default_functions.get(None, ())
|
||||
if '.' in endpoint:
|
||||
bp = endpoint.rsplit('.', 1)[0]
|
||||
if "." in endpoint:
|
||||
bp = endpoint.rsplit(".", 1)[0]
|
||||
funcs = chain(funcs, self.url_default_functions.get(bp, ()))
|
||||
for func in funcs:
|
||||
func(endpoint, values)
|
||||
|
|
@ -2327,7 +2392,4 @@ class Flask(_PackageBoundObject):
|
|||
return self.wsgi_app(environ, start_response)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %r>' % (
|
||||
self.__class__.__name__,
|
||||
self.name,
|
||||
)
|
||||
return "<%s %r>" % (self.__class__.__name__, self.name)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class BlueprintSetupState(object):
|
|||
#: out if the blueprint was registered in the past already.
|
||||
self.first_registration = first_registration
|
||||
|
||||
subdomain = self.options.get('subdomain')
|
||||
subdomain = self.options.get("subdomain")
|
||||
if subdomain is None:
|
||||
subdomain = self.blueprint.subdomain
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ class BlueprintSetupState(object):
|
|||
#: otherwise.
|
||||
self.subdomain = subdomain
|
||||
|
||||
url_prefix = self.options.get('url_prefix')
|
||||
url_prefix = self.options.get("url_prefix")
|
||||
if url_prefix is None:
|
||||
url_prefix = self.blueprint.url_prefix
|
||||
#: The prefix that should be used for all URLs defined on the
|
||||
|
|
@ -57,7 +57,7 @@ class BlueprintSetupState(object):
|
|||
#: A dictionary with URL defaults that is added to each and every
|
||||
#: URL that was defined with the blueprint.
|
||||
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
||||
self.url_defaults.update(self.options.get('url_defaults', ()))
|
||||
self.url_defaults.update(self.options.get("url_defaults", ()))
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
"""A helper method to register a rule (and optionally a view function)
|
||||
|
|
@ -66,18 +66,22 @@ class BlueprintSetupState(object):
|
|||
"""
|
||||
if self.url_prefix is not None:
|
||||
if rule:
|
||||
rule = '/'.join((
|
||||
self.url_prefix.rstrip('/'), rule.lstrip('/')))
|
||||
rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
|
||||
else:
|
||||
rule = self.url_prefix
|
||||
options.setdefault('subdomain', self.subdomain)
|
||||
options.setdefault("subdomain", self.subdomain)
|
||||
if endpoint is None:
|
||||
endpoint = _endpoint_from_view_func(view_func)
|
||||
defaults = self.url_defaults
|
||||
if 'defaults' in options:
|
||||
defaults = dict(defaults, **options.pop('defaults'))
|
||||
self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint),
|
||||
view_func, defaults=defaults, **options)
|
||||
if "defaults" in options:
|
||||
defaults = dict(defaults, **options.pop("defaults"))
|
||||
self.app.add_url_rule(
|
||||
rule,
|
||||
"%s.%s" % (self.blueprint.name, endpoint),
|
||||
view_func,
|
||||
defaults=defaults,
|
||||
**options
|
||||
)
|
||||
|
||||
|
||||
class Blueprint(_PackageBoundObject):
|
||||
|
|
@ -115,12 +119,21 @@ class Blueprint(_PackageBoundObject):
|
|||
#: resources contained in the package.
|
||||
root_path = None
|
||||
|
||||
def __init__(self, name, import_name, static_folder=None,
|
||||
static_url_path=None, template_folder=None,
|
||||
url_prefix=None, subdomain=None, url_defaults=None,
|
||||
root_path=None):
|
||||
_PackageBoundObject.__init__(self, import_name, template_folder,
|
||||
root_path=root_path)
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
import_name,
|
||||
static_folder=None,
|
||||
static_url_path=None,
|
||||
template_folder=None,
|
||||
url_prefix=None,
|
||||
subdomain=None,
|
||||
url_defaults=None,
|
||||
root_path=None,
|
||||
):
|
||||
_PackageBoundObject.__init__(
|
||||
self, import_name, template_folder, root_path=root_path
|
||||
)
|
||||
self.name = name
|
||||
self.url_prefix = url_prefix
|
||||
self.subdomain = subdomain
|
||||
|
|
@ -139,9 +152,14 @@ class Blueprint(_PackageBoundObject):
|
|||
"""
|
||||
if self._got_registered_once and self.warn_on_modifications:
|
||||
from warnings import warn
|
||||
warn(Warning('The blueprint was already registered once '
|
||||
'but is getting modified now. These changes '
|
||||
'will not show up.'))
|
||||
|
||||
warn(
|
||||
Warning(
|
||||
"The blueprint was already registered once "
|
||||
"but is getting modified now. These changes "
|
||||
"will not show up."
|
||||
)
|
||||
)
|
||||
self.deferred_functions.append(func)
|
||||
|
||||
def record_once(self, func):
|
||||
|
|
@ -150,9 +168,11 @@ class Blueprint(_PackageBoundObject):
|
|||
blueprint is registered a second time on the application, the
|
||||
function passed is not called.
|
||||
"""
|
||||
|
||||
def wrapper(state):
|
||||
if state.first_registration:
|
||||
func(state)
|
||||
|
||||
return self.record(update_wrapper(wrapper, func))
|
||||
|
||||
def make_setup_state(self, app, options, first_registration=False):
|
||||
|
|
@ -179,8 +199,9 @@ class Blueprint(_PackageBoundObject):
|
|||
|
||||
if self.has_static_folder:
|
||||
state.add_url_rule(
|
||||
self.static_url_path + '/<path:filename>',
|
||||
view_func=self.send_static_file, endpoint='static'
|
||||
self.static_url_path + "/<path:filename>",
|
||||
view_func=self.send_static_file,
|
||||
endpoint="static",
|
||||
)
|
||||
|
||||
for deferred in self.deferred_functions:
|
||||
|
|
@ -190,10 +211,12 @@ class Blueprint(_PackageBoundObject):
|
|||
"""Like :meth:`Flask.route` but for a blueprint. The endpoint for the
|
||||
:func:`url_for` function is prefixed with the name of the blueprint.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
endpoint = options.pop("endpoint", f.__name__)
|
||||
self.add_url_rule(rule, endpoint, f, **options)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
|
|
@ -201,11 +224,12 @@ class Blueprint(_PackageBoundObject):
|
|||
the :func:`url_for` function is prefixed with the name of the blueprint.
|
||||
"""
|
||||
if endpoint:
|
||||
assert '.' not in endpoint, "Blueprint endpoints should not contain dots"
|
||||
if view_func and hasattr(view_func, '__name__'):
|
||||
assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots"
|
||||
self.record(lambda s:
|
||||
s.add_url_rule(rule, endpoint, view_func, **options))
|
||||
assert "." not in endpoint, "Blueprint endpoints should not contain dots"
|
||||
if view_func and hasattr(view_func, "__name__"):
|
||||
assert (
|
||||
"." not in view_func.__name__
|
||||
), "Blueprint view function name should not contain dots"
|
||||
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
||||
|
||||
def endpoint(self, endpoint):
|
||||
"""Like :meth:`Flask.endpoint` but for a blueprint. This does not
|
||||
|
|
@ -214,11 +238,14 @@ class Blueprint(_PackageBoundObject):
|
|||
with a `.` it will be registered to the current blueprint, otherwise
|
||||
it's an application independent endpoint.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
def register_endpoint(state):
|
||||
state.app.view_functions[endpoint] = f
|
||||
|
||||
self.record_once(register_endpoint)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def app_template_filter(self, name=None):
|
||||
|
|
@ -228,9 +255,11 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_app_template_filter(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_filter(self, f, name=None):
|
||||
|
|
@ -241,8 +270,10 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the filter, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state):
|
||||
state.app.jinja_env.filters[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_test(self, name=None):
|
||||
|
|
@ -254,9 +285,11 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_app_template_test(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_test(self, f, name=None):
|
||||
|
|
@ -269,8 +302,10 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the test, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state):
|
||||
state.app.jinja_env.tests[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def app_template_global(self, name=None):
|
||||
|
|
@ -282,9 +317,11 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.add_app_template_global(f, name=name)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def add_app_template_global(self, f, name=None):
|
||||
|
|
@ -297,8 +334,10 @@ class Blueprint(_PackageBoundObject):
|
|||
:param name: the optional name of the global, otherwise the
|
||||
function name will be used.
|
||||
"""
|
||||
|
||||
def register_template(state):
|
||||
state.app.jinja_env.globals[name or f.__name__] = f
|
||||
|
||||
self.record_once(register_template)
|
||||
|
||||
def before_request(self, f):
|
||||
|
|
@ -306,16 +345,18 @@ class Blueprint(_PackageBoundObject):
|
|||
is only executed before each request that is handled by a function of
|
||||
that blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.before_request_funcs.setdefault(self.name, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def before_app_request(self, f):
|
||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||
before each request, even if outside of a blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.before_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def before_app_first_request(self, f):
|
||||
|
|
@ -330,16 +371,18 @@ class Blueprint(_PackageBoundObject):
|
|||
is only executed after each request that is handled by a function of
|
||||
that blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.after_request_funcs.setdefault(self.name, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def after_app_request(self, f):
|
||||
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
||||
is executed after each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.after_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def teardown_request(self, f):
|
||||
|
|
@ -349,8 +392,9 @@ class Blueprint(_PackageBoundObject):
|
|||
when the request context is popped, even when no actual request was
|
||||
performed.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.teardown_request_funcs
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.teardown_request_funcs.setdefault(self.name, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def teardown_app_request(self, f):
|
||||
|
|
@ -358,33 +402,40 @@ class Blueprint(_PackageBoundObject):
|
|||
function is executed when tearing down each request, even if outside of
|
||||
the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.teardown_request_funcs
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a blueprint. This
|
||||
function is only executed for requests handled by a blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.template_context_processors.setdefault(
|
||||
self.name, []
|
||||
).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_context_processor(self, f):
|
||||
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
||||
function is executed each request, even if outside of the blueprint.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.template_context_processors
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_errorhandler(self, code):
|
||||
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
||||
handler is used for all requests, even if outside of the blueprint.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def url_value_preprocessor(self, f):
|
||||
|
|
@ -392,8 +443,9 @@ class Blueprint(_PackageBoundObject):
|
|||
blueprint. It's called before the view functions are called and
|
||||
can modify the url values provided.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_value_preprocessors
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.url_value_preprocessors.setdefault(self.name, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def url_defaults(self, f):
|
||||
|
|
@ -401,22 +453,25 @@ class Blueprint(_PackageBoundObject):
|
|||
with the endpoint and values and should update the values passed
|
||||
in place.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(self.name, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.url_default_functions.setdefault(self.name, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_url_value_preprocessor(self, f):
|
||||
"""Same as :meth:`url_value_preprocessor` but application wide.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_value_preprocessors
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def app_url_defaults(self, f):
|
||||
"""Same as :meth:`url_defaults` but application wide.
|
||||
"""
|
||||
self.record_once(lambda s: s.app.url_default_functions
|
||||
.setdefault(None, []).append(f))
|
||||
self.record_once(
|
||||
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
||||
)
|
||||
return f
|
||||
|
||||
def errorhandler(self, code_or_exception):
|
||||
|
|
@ -430,10 +485,13 @@ class Blueprint(_PackageBoundObject):
|
|||
Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
|
||||
of the :class:`~flask.Flask` object.
|
||||
"""
|
||||
|
||||
def decorator(f):
|
||||
self.record_once(lambda s: s.app._register_error_handler(
|
||||
self.name, code_or_exception, f))
|
||||
self.record_once(
|
||||
lambda s: s.app._register_error_handler(self.name, code_or_exception, f)
|
||||
)
|
||||
return f
|
||||
|
||||
return decorator
|
||||
|
||||
def register_error_handler(self, code_or_exception, f):
|
||||
|
|
@ -444,5 +502,6 @@ class Blueprint(_PackageBoundObject):
|
|||
|
||||
.. versionadded:: 0.11
|
||||
"""
|
||||
self.record_once(lambda s: s.app._register_error_handler(
|
||||
self.name, code_or_exception, f))
|
||||
self.record_once(
|
||||
lambda s: s.app._register_error_handler(self.name, code_or_exception, f)
|
||||
)
|
||||
|
|
|
|||
339
flask/cli.py
339
flask/cli.py
|
|
@ -48,16 +48,14 @@ def find_best_app(script_info, 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 isinstance(app, Flask):
|
||||
return app
|
||||
|
||||
# Otherwise find the only object that is a Flask instance.
|
||||
matches = [
|
||||
v for v in itervalues(module.__dict__) if isinstance(v, Flask)
|
||||
]
|
||||
matches = [v for v in itervalues(module.__dict__) if isinstance(v, Flask)]
|
||||
|
||||
if len(matches) == 1:
|
||||
return matches[0]
|
||||
|
|
@ -65,11 +63,11 @@ def find_best_app(script_info, module):
|
|||
raise NoAppException(
|
||||
'Detected multiple Flask applications in module "{module}". Use '
|
||||
'"FLASK_APP={module}:name" to specify the correct '
|
||||
'one.'.format(module=module.__name__)
|
||||
"one.".format(module=module.__name__)
|
||||
)
|
||||
|
||||
# Search for app factory functions.
|
||||
for attr_name in ('create_app', 'make_app'):
|
||||
for attr_name in ("create_app", "make_app"):
|
||||
app_factory = getattr(module, attr_name, None)
|
||||
|
||||
if inspect.isfunction(app_factory):
|
||||
|
|
@ -83,18 +81,14 @@ def find_best_app(script_info, module):
|
|||
raise
|
||||
raise 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__
|
||||
)
|
||||
"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__
|
||||
)
|
||||
'Use "FLASK_APP={module}:name to specify one.'.format(module=module.__name__)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -107,7 +101,7 @@ def call_factory(script_info, app_factory, arguments=()):
|
|||
arg_names = args_spec.args
|
||||
arg_defaults = args_spec.defaults
|
||||
|
||||
if 'script_info' in arg_names:
|
||||
if "script_info" in arg_names:
|
||||
return app_factory(*arguments, script_info=script_info)
|
||||
elif arguments:
|
||||
return app_factory(*arguments)
|
||||
|
|
@ -148,12 +142,13 @@ def find_app_by_string(script_info, module, app_name):
|
|||
arguments.
|
||||
"""
|
||||
from flask import Flask
|
||||
match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name)
|
||||
|
||||
match = re.match(r"^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$", app_name)
|
||||
|
||||
if not match:
|
||||
raise NoAppException(
|
||||
'"{name}" is not a valid variable name or function '
|
||||
'expression.'.format(name=app_name)
|
||||
"expression.".format(name=app_name)
|
||||
)
|
||||
|
||||
name, args = match.groups()
|
||||
|
|
@ -166,10 +161,10 @@ def find_app_by_string(script_info, module, app_name):
|
|||
if inspect.isfunction(attr):
|
||||
if args:
|
||||
try:
|
||||
args = ast.literal_eval('({args},)'.format(args=args))
|
||||
except (ValueError, SyntaxError)as e:
|
||||
args = ast.literal_eval("({args},)".format(args=args))
|
||||
except (ValueError, SyntaxError) as e:
|
||||
raise NoAppException(
|
||||
'Could not parse the arguments in '
|
||||
"Could not parse the arguments in "
|
||||
'"{app_name}".'.format(e=e, app_name=app_name)
|
||||
)
|
||||
else:
|
||||
|
|
@ -183,7 +178,7 @@ def find_app_by_string(script_info, module, app_name):
|
|||
|
||||
raise NoAppException(
|
||||
'{e}\nThe factory "{app_name}" in module "{module}" could not '
|
||||
'be called with the specified arguments.'.format(
|
||||
"be called with the specified arguments.".format(
|
||||
e=e, app_name=app_name, module=module.__name__
|
||||
)
|
||||
)
|
||||
|
|
@ -194,10 +189,8 @@ def find_app_by_string(script_info, module, app_name):
|
|||
return app
|
||||
|
||||
raise NoAppException(
|
||||
'A valid Flask application was not obtained from '
|
||||
'"{module}:{app_name}".'.format(
|
||||
module=module.__name__, app_name=app_name
|
||||
)
|
||||
"A valid Flask application was not obtained from "
|
||||
'"{module}:{app_name}".'.format(module=module.__name__, app_name=app_name)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -208,10 +201,10 @@ def prepare_import(path):
|
|||
path = os.path.realpath(path)
|
||||
|
||||
fname, ext = os.path.splitext(path)
|
||||
if ext == '.py':
|
||||
if ext == ".py":
|
||||
path = fname
|
||||
|
||||
if os.path.basename(path) == '__init__':
|
||||
if os.path.basename(path) == "__init__":
|
||||
path = os.path.dirname(path)
|
||||
|
||||
module_name = []
|
||||
|
|
@ -221,13 +214,13 @@ def prepare_import(path):
|
|||
path, name = os.path.split(path)
|
||||
module_name.append(name)
|
||||
|
||||
if not os.path.exists(os.path.join(path, '__init__.py')):
|
||||
if not os.path.exists(os.path.join(path, "__init__.py")):
|
||||
break
|
||||
|
||||
if sys.path[0] != path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
return '.'.join(module_name[::-1])
|
||||
return ".".join(module_name[::-1])
|
||||
|
||||
|
||||
def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
|
||||
|
|
@ -241,12 +234,10 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True):
|
|||
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())
|
||||
"\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)
|
||||
)
|
||||
raise NoAppException('Could not import "{name}".'.format(name=module_name))
|
||||
else:
|
||||
return
|
||||
|
||||
|
|
@ -262,26 +253,27 @@ def get_version(ctx, param, value):
|
|||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
import werkzeug
|
||||
message = (
|
||||
'Python %(python)s\n'
|
||||
'Flask %(flask)s\n'
|
||||
'Werkzeug %(werkzeug)s'
|
||||
|
||||
message = "Python %(python)s\n" "Flask %(flask)s\n" "Werkzeug %(werkzeug)s"
|
||||
click.echo(
|
||||
message
|
||||
% {
|
||||
"python": platform.python_version(),
|
||||
"flask": __version__,
|
||||
"werkzeug": werkzeug.__version__,
|
||||
},
|
||||
color=ctx.color,
|
||||
)
|
||||
click.echo(message % {
|
||||
'python': platform.python_version(),
|
||||
'flask': __version__,
|
||||
'werkzeug': werkzeug.__version__,
|
||||
}, color=ctx.color)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
version_option = click.Option(
|
||||
['--version'],
|
||||
help='Show the flask version',
|
||||
["--version"],
|
||||
help="Show the flask version",
|
||||
expose_value=False,
|
||||
callback=get_version,
|
||||
is_flag=True,
|
||||
is_eager=True
|
||||
is_eager=True,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -310,6 +302,7 @@ class DispatchingApp(object):
|
|||
self._load_unlocked()
|
||||
except Exception:
|
||||
self._bg_loading_exc_info = sys.exc_info()
|
||||
|
||||
t = Thread(target=_load_app, args=())
|
||||
t.start()
|
||||
|
||||
|
|
@ -348,10 +341,9 @@ class ScriptInfo(object):
|
|||
onwards as click object.
|
||||
"""
|
||||
|
||||
def __init__(self, app_import_path=None, create_app=None,
|
||||
set_debug_flag=True):
|
||||
def __init__(self, app_import_path=None, create_app=None, set_debug_flag=True):
|
||||
#: Optionally the import path for the Flask application.
|
||||
self.app_import_path = app_import_path or os.environ.get('FLASK_APP')
|
||||
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
|
||||
|
|
@ -377,21 +369,22 @@ class ScriptInfo(object):
|
|||
app = call_factory(self, self.create_app)
|
||||
else:
|
||||
if self.app_import_path:
|
||||
path, name = (re.split(r':(?![\\/])', self.app_import_path, 1) + [None])[:2]
|
||||
path, name = (
|
||||
re.split(r":(?![\\/])", self.app_import_path, 1) + [None]
|
||||
)[:2]
|
||||
import_name = prepare_import(path)
|
||||
app = locate_app(self, import_name, name)
|
||||
else:
|
||||
for path in ('wsgi.py', 'app.py'):
|
||||
for path in ("wsgi.py", "app.py"):
|
||||
import_name = prepare_import(path)
|
||||
app = locate_app(self, import_name, None,
|
||||
raise_if_not_found=False)
|
||||
app = locate_app(self, import_name, None, raise_if_not_found=False)
|
||||
|
||||
if app:
|
||||
break
|
||||
|
||||
if not app:
|
||||
raise NoAppException(
|
||||
'Could not locate a Flask application. You did not provide '
|
||||
"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.'
|
||||
)
|
||||
|
|
@ -414,10 +407,12 @@ def with_appcontext(f):
|
|||
to the ``app.cli`` object then they are wrapped with this function
|
||||
by default unless it's disabled.
|
||||
"""
|
||||
|
||||
@click.pass_context
|
||||
def decorator(__ctx, *args, **kwargs):
|
||||
with __ctx.ensure_object(ScriptInfo).load_app().app_context():
|
||||
return __ctx.invoke(f, *args, **kwargs)
|
||||
|
||||
return update_wrapper(decorator, f)
|
||||
|
||||
|
||||
|
|
@ -434,11 +429,13 @@ class AppGroup(click.Group):
|
|||
: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)
|
||||
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 decorator
|
||||
|
||||
def group(self, *args, **kwargs):
|
||||
|
|
@ -446,7 +443,7 @@ class AppGroup(click.Group):
|
|||
:class:`click.Group` but it defaults the group class to
|
||||
:class:`AppGroup`.
|
||||
"""
|
||||
kwargs.setdefault('cls', AppGroup)
|
||||
kwargs.setdefault("cls", AppGroup)
|
||||
return click.Group.group(self, *args, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -475,10 +472,16 @@ class FlaskGroup(AppGroup):
|
|||
from :file:`.env` and :file:`.flaskenv` files.
|
||||
"""
|
||||
|
||||
def __init__(self, add_default_commands=True, create_app=None,
|
||||
add_version_option=True, load_dotenv=True,
|
||||
set_debug_flag=True, **extra):
|
||||
params = list(extra.pop('params', None) or ())
|
||||
def __init__(
|
||||
self,
|
||||
add_default_commands=True,
|
||||
create_app=None,
|
||||
add_version_option=True,
|
||||
load_dotenv=True,
|
||||
set_debug_flag=True,
|
||||
**extra
|
||||
):
|
||||
params = list(extra.pop("params", None) or ())
|
||||
|
||||
if add_version_option:
|
||||
params.append(version_option)
|
||||
|
|
@ -504,7 +507,7 @@ class FlaskGroup(AppGroup):
|
|||
self._loaded_plugin_commands = True
|
||||
return
|
||||
|
||||
for ep in pkg_resources.iter_entry_points('flask.commands'):
|
||||
for ep in pkg_resources.iter_entry_points("flask.commands"):
|
||||
self.add_command(ep.load(), ep.name)
|
||||
self._loaded_plugin_commands = True
|
||||
|
||||
|
|
@ -554,19 +557,20 @@ class FlaskGroup(AppGroup):
|
|||
# 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'
|
||||
os.environ["FLASK_RUN_FROM_CLI"] = "true"
|
||||
|
||||
if get_load_dotenv(self.load_dotenv):
|
||||
load_dotenv()
|
||||
|
||||
obj = kwargs.get('obj')
|
||||
obj = kwargs.get("obj")
|
||||
|
||||
if obj is None:
|
||||
obj = ScriptInfo(create_app=self.create_app,
|
||||
set_debug_flag=self.set_debug_flag)
|
||||
obj = ScriptInfo(
|
||||
create_app=self.create_app, set_debug_flag=self.set_debug_flag
|
||||
)
|
||||
|
||||
kwargs['obj'] = obj
|
||||
kwargs.setdefault('auto_envvar_prefix', 'FLASK')
|
||||
kwargs["obj"] = obj
|
||||
kwargs.setdefault("auto_envvar_prefix", "FLASK")
|
||||
return super(FlaskGroup, self).main(*args, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -574,7 +578,7 @@ def _path_is_ancestor(path, other):
|
|||
"""Take ``other`` and remove the length of ``path`` from it. Then join it
|
||||
to ``path``. If it is the original value, ``path`` is an ancestor of
|
||||
``other``."""
|
||||
return os.path.join(path, other[len(path):].lstrip(os.sep)) == other
|
||||
return os.path.join(path, other[len(path) :].lstrip(os.sep)) == other
|
||||
|
||||
|
||||
def load_dotenv(path=None):
|
||||
|
|
@ -597,11 +601,12 @@ def load_dotenv(path=None):
|
|||
.. versionadded:: 1.0
|
||||
"""
|
||||
if dotenv is None:
|
||||
if path or os.path.isfile('.env') or os.path.isfile('.flaskenv'):
|
||||
if path or os.path.isfile(".env") or os.path.isfile(".flaskenv"):
|
||||
click.secho(
|
||||
' * Tip: There are .env or .flaskenv files present.'
|
||||
" * Tip: There are .env or .flaskenv files present."
|
||||
' Do "pip install python-dotenv" to use them.',
|
||||
fg='yellow')
|
||||
fg="yellow",
|
||||
)
|
||||
return
|
||||
|
||||
if path is not None:
|
||||
|
|
@ -609,7 +614,7 @@ def load_dotenv(path=None):
|
|||
|
||||
new_dir = None
|
||||
|
||||
for name in ('.env', '.flaskenv'):
|
||||
for name in (".env", ".flaskenv"):
|
||||
path = dotenv.find_dotenv(name, usecwd=True)
|
||||
|
||||
if not path:
|
||||
|
|
@ -630,27 +635,29 @@ def 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':
|
||||
if os.environ.get("WERKZEUG_RUN_MAIN") == "true":
|
||||
return
|
||||
|
||||
if app_import_path is not None:
|
||||
message = ' * Serving Flask app "{0}"'.format(app_import_path)
|
||||
|
||||
if not eager_loading:
|
||||
message += ' (lazy loading)'
|
||||
message += " (lazy loading)"
|
||||
|
||||
click.echo(message)
|
||||
|
||||
click.echo(' * Environment: {0}'.format(env))
|
||||
click.echo(" * Environment: {0}".format(env))
|
||||
|
||||
if env == 'production':
|
||||
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)
|
||||
" 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'))
|
||||
click.echo(" * Debug mode: {0}".format("on" if debug else "off"))
|
||||
|
||||
|
||||
class CertParamType(click.ParamType):
|
||||
|
|
@ -659,11 +666,10 @@ class CertParamType(click.ParamType):
|
|||
:class:`~ssl.SSLContext` object.
|
||||
"""
|
||||
|
||||
name = 'path'
|
||||
name = "path"
|
||||
|
||||
def __init__(self):
|
||||
self.path_type = click.Path(
|
||||
exists=True, dir_okay=False, resolve_path=True)
|
||||
self.path_type = click.Path(exists=True, dir_okay=False, resolve_path=True)
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
|
|
@ -671,13 +677,13 @@ class CertParamType(click.ParamType):
|
|||
except click.BadParameter:
|
||||
value = click.STRING(value, param, ctx).lower()
|
||||
|
||||
if value == 'adhoc':
|
||||
if value == "adhoc":
|
||||
try:
|
||||
import OpenSSL
|
||||
except ImportError:
|
||||
raise click.BadParameter(
|
||||
'Using ad-hoc certificates requires pyOpenSSL.',
|
||||
ctx, param)
|
||||
"Using ad-hoc certificates requires pyOpenSSL.", ctx, param
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
|
@ -697,8 +703,8 @@ def _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'
|
||||
cert = ctx.params.get("cert")
|
||||
is_adhoc = cert == "adhoc"
|
||||
|
||||
if sys.version_info < (2, 7, 9):
|
||||
is_context = cert and not isinstance(cert, (text_type, bytes))
|
||||
|
|
@ -708,55 +714,64 @@ def _validate_key(ctx, param, value):
|
|||
if value is not None:
|
||||
if is_adhoc:
|
||||
raise click.BadParameter(
|
||||
'When "--cert" is "adhoc", "--key" is not used.',
|
||||
ctx, param)
|
||||
'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)
|
||||
'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)
|
||||
raise click.BadParameter('"--cert" must also be specified.', ctx, param)
|
||||
|
||||
ctx.params['cert'] = cert, value
|
||||
ctx.params["cert"] = cert, value
|
||||
|
||||
else:
|
||||
if cert and not (is_adhoc or is_context):
|
||||
raise click.BadParameter(
|
||||
'Required when using "--cert".',
|
||||
ctx, param)
|
||||
raise click.BadParameter('Required when using "--cert".', ctx, param)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@click.command('run', short_help='Run 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.')
|
||||
@click.command("run", short_help="Run 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_info
|
||||
def run_command(info, host, port, reload, debugger, eager_loading,
|
||||
with_threads, cert):
|
||||
def 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 provide
|
||||
|
|
@ -780,11 +795,19 @@ def run_command(info, host, port, reload, debugger, eager_loading,
|
|||
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
|
||||
|
||||
from werkzeug.serving import run_simple
|
||||
run_simple(host, port, app, use_reloader=reload, use_debugger=debugger,
|
||||
threaded=with_threads, ssl_context=cert)
|
||||
|
||||
run_simple(
|
||||
host,
|
||||
port,
|
||||
app,
|
||||
use_reloader=reload,
|
||||
use_debugger=debugger,
|
||||
threaded=with_threads,
|
||||
ssl_context=cert,
|
||||
)
|
||||
|
||||
|
||||
@click.command('shell', short_help='Run a shell in the app context.')
|
||||
@click.command("shell", short_help="Run a shell in the app context.")
|
||||
@with_appcontext
|
||||
def shell_command():
|
||||
"""Run an interactive Python shell in the context of a given
|
||||
|
|
@ -796,8 +819,9 @@ def shell_command():
|
|||
"""
|
||||
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' % (
|
||||
banner = "Python %s on %s\nApp: %s [%s]\nInstance: %s" % (
|
||||
sys.version,
|
||||
sys.platform,
|
||||
app.import_name,
|
||||
|
|
@ -808,68 +832,64 @@ def shell_command():
|
|||
|
||||
# Support the regular Python interpreter startup script if someone
|
||||
# is using it.
|
||||
startup = os.environ.get('PYTHONSTARTUP')
|
||||
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)
|
||||
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.command("routes", short_help="Show the routes for the app.")
|
||||
@click.option(
|
||||
'--sort', '-s',
|
||||
type=click.Choice(('endpoint', 'methods', 'rule', 'match')),
|
||||
default='endpoint',
|
||||
"--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."
|
||||
"routes when dispatching a request."
|
||||
),
|
||||
)
|
||||
@click.option("--all-methods", is_flag=True, help="Show HEAD and OPTIONS methods.")
|
||||
@with_appcontext
|
||||
def 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.')
|
||||
click.echo("No routes were registered.")
|
||||
return
|
||||
|
||||
ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS'))
|
||||
ignored_methods = set(() if all_methods else ("HEAD", "OPTIONS"))
|
||||
|
||||
if sort in ('endpoint', 'rule'):
|
||||
if sort in ("endpoint", "rule"):
|
||||
rules = sorted(rules, key=attrgetter(sort))
|
||||
elif sort == 'methods':
|
||||
elif sort == "methods":
|
||||
rules = sorted(rules, key=lambda rule: sorted(rule.methods))
|
||||
|
||||
rule_methods = [
|
||||
', '.join(sorted(rule.methods - ignored_methods)) for rule in rules
|
||||
]
|
||||
rule_methods = [", ".join(sorted(rule.methods - ignored_methods)) for rule in rules]
|
||||
|
||||
headers = ('Endpoint', 'Methods', 'Rule')
|
||||
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)
|
||||
row = "{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}".format(*widths)
|
||||
|
||||
click.echo(row.format(*headers).strip())
|
||||
click.echo(row.format(*('-' * width for width in widths)))
|
||||
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="""\
|
||||
cli = FlaskGroup(
|
||||
help="""\
|
||||
A general utility script for Flask applications.
|
||||
|
||||
Provides commands from Flask, extensions, and the application. Loads the
|
||||
|
|
@ -882,30 +902,31 @@ debug mode.
|
|||
{prefix}{cmd} FLASK_ENV=development
|
||||
{prefix}flask run
|
||||
""".format(
|
||||
cmd='export' if os.name == 'posix' else 'set',
|
||||
prefix='$ ' if os.name == 'posix' else '> '
|
||||
))
|
||||
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'
|
||||
this_module = "flask"
|
||||
|
||||
if sys.version_info < (2, 7):
|
||||
this_module += '.cli'
|
||||
this_module += ".cli"
|
||||
|
||||
name = 'python -m ' + this_module
|
||||
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] + args
|
||||
sys.argv = ["-m", this_module] + args
|
||||
else:
|
||||
name = None
|
||||
|
||||
cli.main(args=args, prog_name=name)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main(as_module=True)
|
||||
|
|
|
|||
|
|
@ -101,11 +101,12 @@ class Config(dict):
|
|||
if not rv:
|
||||
if silent:
|
||||
return False
|
||||
raise RuntimeError('The environment variable %r is not set '
|
||||
'and as such configuration could not be '
|
||||
'loaded. Set this variable and make it '
|
||||
'point to a configuration file' %
|
||||
variable_name)
|
||||
raise RuntimeError(
|
||||
"The environment variable %r is not set "
|
||||
"and as such configuration could not be "
|
||||
"loaded. Set this variable and make it "
|
||||
"point to a configuration file" % variable_name
|
||||
)
|
||||
return self.from_pyfile(rv, silent=silent)
|
||||
|
||||
def from_pyfile(self, filename, silent=False):
|
||||
|
|
@ -123,17 +124,15 @@ class Config(dict):
|
|||
`silent` parameter.
|
||||
"""
|
||||
filename = os.path.join(self.root_path, filename)
|
||||
d = types.ModuleType('config')
|
||||
d = types.ModuleType("config")
|
||||
d.__file__ = filename
|
||||
try:
|
||||
with open(filename, mode='rb') as config_file:
|
||||
exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
|
||||
with open(filename, mode="rb") as config_file:
|
||||
exec(compile(config_file.read(), filename, "exec"), d.__dict__)
|
||||
except IOError as e:
|
||||
if silent and e.errno in (
|
||||
errno.ENOENT, errno.EISDIR, errno.ENOTDIR
|
||||
):
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR, errno.ENOTDIR):
|
||||
return False
|
||||
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
|
||||
e.strerror = "Unable to load configuration file (%s)" % e.strerror
|
||||
raise
|
||||
self.from_object(d)
|
||||
return True
|
||||
|
|
@ -197,7 +196,7 @@ class Config(dict):
|
|||
except IOError as e:
|
||||
if silent and e.errno in (errno.ENOENT, errno.EISDIR):
|
||||
return False
|
||||
e.strerror = 'Unable to load configuration file (%s)' % e.strerror
|
||||
e.strerror = "Unable to load configuration file (%s)" % e.strerror
|
||||
raise
|
||||
return self.from_mapping(obj)
|
||||
|
||||
|
|
@ -209,13 +208,13 @@ class Config(dict):
|
|||
"""
|
||||
mappings = []
|
||||
if len(mapping) == 1:
|
||||
if hasattr(mapping[0], 'items'):
|
||||
if hasattr(mapping[0], "items"):
|
||||
mappings.append(mapping[0].items())
|
||||
else:
|
||||
mappings.append(mapping[0])
|
||||
elif len(mapping) > 1:
|
||||
raise TypeError(
|
||||
'expected at most 1 positional argument, got %d' % len(mapping)
|
||||
"expected at most 1 positional argument, got %d" % len(mapping)
|
||||
)
|
||||
mappings.append(kwargs.items())
|
||||
for mapping in mappings:
|
||||
|
|
@ -257,7 +256,7 @@ class Config(dict):
|
|||
if not k.startswith(namespace):
|
||||
continue
|
||||
if trim_namespace:
|
||||
key = k[len(namespace):]
|
||||
key = k[len(namespace) :]
|
||||
else:
|
||||
key = k
|
||||
if lowercase:
|
||||
|
|
@ -266,4 +265,4 @@ class Config(dict):
|
|||
return rv
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
|
||||
return "<%s %s>" % (self.__class__.__name__, dict.__repr__(self))
|
||||
|
|
|
|||
51
flask/ctx.py
51
flask/ctx.py
|
|
@ -89,7 +89,7 @@ class _AppCtxGlobals(object):
|
|||
def __repr__(self):
|
||||
top = _app_ctx_stack.top
|
||||
if top is not None:
|
||||
return '<flask.g of %r>' % top.app.name
|
||||
return "<flask.g of %r>" % top.app.name
|
||||
return object.__repr__(self)
|
||||
|
||||
|
||||
|
|
@ -144,13 +144,17 @@ def copy_current_request_context(f):
|
|||
"""
|
||||
top = _request_ctx_stack.top
|
||||
if top is None:
|
||||
raise RuntimeError('This decorator can only be used at local scopes '
|
||||
'when a request context is on the stack. For instance within '
|
||||
'view functions.')
|
||||
raise RuntimeError(
|
||||
"This decorator can only be used at local scopes "
|
||||
"when a request context is on the stack. For instance within "
|
||||
"view functions."
|
||||
)
|
||||
reqctx = top.copy()
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
with reqctx:
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return update_wrapper(wrapper, f)
|
||||
|
||||
|
||||
|
|
@ -217,7 +221,7 @@ class AppContext(object):
|
|||
def push(self):
|
||||
"""Binds the app context to the current context."""
|
||||
self._refcnt += 1
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
if hasattr(sys, "exc_clear"):
|
||||
sys.exc_clear()
|
||||
_app_ctx_stack.push(self)
|
||||
appcontext_pushed.send(self.app)
|
||||
|
|
@ -232,8 +236,7 @@ class AppContext(object):
|
|||
self.app.do_teardown_appcontext(exc)
|
||||
finally:
|
||||
rv = _app_ctx_stack.pop()
|
||||
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
|
||||
% (rv, self)
|
||||
assert rv is self, "Popped wrong app context. (%r instead of %r)" % (rv, self)
|
||||
appcontext_popped.send(self.app)
|
||||
|
||||
def __enter__(self):
|
||||
|
|
@ -314,8 +317,10 @@ class RequestContext(object):
|
|||
|
||||
def _get_g(self):
|
||||
return _app_ctx_stack.top.g
|
||||
|
||||
def _set_g(self, value):
|
||||
_app_ctx_stack.top.g = value
|
||||
|
||||
g = property(_get_g, _set_g)
|
||||
del _get_g, _set_g
|
||||
|
||||
|
|
@ -332,10 +337,11 @@ class RequestContext(object):
|
|||
The current session object is used instead of reloading the original
|
||||
data. This prevents `flask.session` pointing to an out-of-date object.
|
||||
"""
|
||||
return self.__class__(self.app,
|
||||
return self.__class__(
|
||||
self.app,
|
||||
environ=self.request.environ,
|
||||
request=self.request,
|
||||
session=self.session
|
||||
session=self.session,
|
||||
)
|
||||
|
||||
def match_request(self):
|
||||
|
|
@ -343,8 +349,7 @@ class RequestContext(object):
|
|||
of the request.
|
||||
"""
|
||||
try:
|
||||
url_rule, self.request.view_args = \
|
||||
self.url_adapter.match(return_rule=True)
|
||||
url_rule, self.request.view_args = self.url_adapter.match(return_rule=True)
|
||||
self.request.url_rule = url_rule
|
||||
except HTTPException as e:
|
||||
self.request.routing_exception = e
|
||||
|
|
@ -373,7 +378,7 @@ class RequestContext(object):
|
|||
else:
|
||||
self._implicit_app_ctx_stack.append(None)
|
||||
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
if hasattr(sys, "exc_clear"):
|
||||
sys.exc_clear()
|
||||
|
||||
_request_ctx_stack.push(self)
|
||||
|
|
@ -384,9 +389,7 @@ class RequestContext(object):
|
|||
# pushed, otherwise stream_with_context loses the session.
|
||||
if self.session is None:
|
||||
session_interface = self.app.session_interface
|
||||
self.session = session_interface.open_session(
|
||||
self.app, self.request
|
||||
)
|
||||
self.session = session_interface.open_session(self.app, self.request)
|
||||
|
||||
if self.session is None:
|
||||
self.session = session_interface.make_null_session(self.app)
|
||||
|
|
@ -414,10 +417,10 @@ class RequestContext(object):
|
|||
# we do that now. This will only go into effect on Python 2.x,
|
||||
# on 3.x it disappears automatically at the end of the exception
|
||||
# stack.
|
||||
if hasattr(sys, 'exc_clear'):
|
||||
if hasattr(sys, "exc_clear"):
|
||||
sys.exc_clear()
|
||||
|
||||
request_close = getattr(self.request, 'close', None)
|
||||
request_close = getattr(self.request, "close", None)
|
||||
if request_close is not None:
|
||||
request_close()
|
||||
clear_request = True
|
||||
|
|
@ -427,18 +430,20 @@ class RequestContext(object):
|
|||
# get rid of circular dependencies at the end of the request
|
||||
# so that we don't require the GC to be active.
|
||||
if clear_request:
|
||||
rv.request.environ['werkzeug.request'] = None
|
||||
rv.request.environ["werkzeug.request"] = None
|
||||
|
||||
# Get rid of the app as well if necessary.
|
||||
if app_ctx is not None:
|
||||
app_ctx.pop(exc)
|
||||
|
||||
assert rv is self, 'Popped wrong request context. ' \
|
||||
'(%r instead of %r)' % (rv, self)
|
||||
assert (
|
||||
rv is self
|
||||
), "Popped wrong request context. " "(%r instead of %r)" % (rv, self)
|
||||
|
||||
def auto_pop(self, exc):
|
||||
if self.request.environ.get('flask._preserve_context') or \
|
||||
(exc is not None and self.app.preserve_context_on_exception):
|
||||
if self.request.environ.get("flask._preserve_context") or (
|
||||
exc is not None and self.app.preserve_context_on_exception
|
||||
):
|
||||
self.preserved = True
|
||||
self._preserved_exc = exc
|
||||
else:
|
||||
|
|
@ -460,7 +465,7 @@ class RequestContext(object):
|
|||
reraise(exc_type, exc_value, tb)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s \'%s\' [%s] of %s>' % (
|
||||
return "<%s '%s' [%s] of %s>" % (
|
||||
self.__class__.__name__,
|
||||
self.request.url,
|
||||
self.request.method,
|
||||
|
|
|
|||
|
|
@ -32,17 +32,20 @@ class DebugFilesKeyError(KeyError, AssertionError):
|
|||
|
||||
def __init__(self, request, key):
|
||||
form_matches = request.form.getlist(key)
|
||||
buf = ['You tried to access the file "%s" in the request.files '
|
||||
'dictionary but it does not exist. The mimetype for the request '
|
||||
'is "%s" instead of "multipart/form-data" which means that no '
|
||||
'file contents were transmitted. To fix this error you should '
|
||||
'provide enctype="multipart/form-data" in your form.' %
|
||||
(key, request.mimetype)]
|
||||
buf = [
|
||||
'You tried to access the file "%s" in the request.files '
|
||||
"dictionary but it does not exist. The mimetype for the request "
|
||||
'is "%s" instead of "multipart/form-data" which means that no '
|
||||
"file contents were transmitted. To fix this error you should "
|
||||
'provide enctype="multipart/form-data" in your form.'
|
||||
% (key, request.mimetype)
|
||||
]
|
||||
if form_matches:
|
||||
buf.append('\n\nThe browser instead transmitted some file names. '
|
||||
'This was submitted: %s' % ', '.join('"%s"' % x
|
||||
for x in form_matches))
|
||||
self.msg = ''.join(buf)
|
||||
buf.append(
|
||||
"\n\nThe browser instead transmitted some file names. "
|
||||
"This was submitted: %s" % ", ".join('"%s"' % x for x in form_matches)
|
||||
)
|
||||
self.msg = "".join(buf)
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
|
@ -56,23 +59,28 @@ class FormDataRoutingRedirect(AssertionError):
|
|||
|
||||
def __init__(self, request):
|
||||
exc = request.routing_exception
|
||||
buf = ['A request was sent to this URL (%s) but a redirect was '
|
||||
'issued automatically by the routing system to "%s".'
|
||||
% (request.url, exc.new_url)]
|
||||
buf = [
|
||||
"A request was sent to this URL (%s) but a redirect was "
|
||||
'issued automatically by the routing system to "%s".'
|
||||
% (request.url, exc.new_url)
|
||||
]
|
||||
|
||||
# In case just a slash was appended we can be extra helpful
|
||||
if request.base_url + '/' == exc.new_url.split('?')[0]:
|
||||
buf.append(' The URL was defined with a trailing slash so '
|
||||
'Flask will automatically redirect to the URL '
|
||||
'with the trailing slash if it was accessed '
|
||||
'without one.')
|
||||
if request.base_url + "/" == exc.new_url.split("?")[0]:
|
||||
buf.append(
|
||||
" The URL was defined with a trailing slash so "
|
||||
"Flask will automatically redirect to the URL "
|
||||
"with the trailing slash if it was accessed "
|
||||
"without one."
|
||||
)
|
||||
|
||||
buf.append(' Make sure to directly send your %s-request to this URL '
|
||||
'since we can\'t make browsers or HTTP clients redirect '
|
||||
'with form data reliably or without user interaction.' %
|
||||
request.method)
|
||||
buf.append('\n\nNote: this exception is only raised in debug mode')
|
||||
AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
|
||||
buf.append(
|
||||
" Make sure to directly send your %s-request to this URL "
|
||||
"since we can't make browsers or HTTP clients redirect "
|
||||
"with form data reliably or without user interaction." % request.method
|
||||
)
|
||||
buf.append("\n\nNote: this exception is only raised in debug mode")
|
||||
AssertionError.__init__(self, "".join(buf).encode("utf-8"))
|
||||
|
||||
|
||||
def attach_enctype_error_multidict(request):
|
||||
|
|
@ -81,6 +89,7 @@ def attach_enctype_error_multidict(request):
|
|||
object is accessed.
|
||||
"""
|
||||
oldcls = request.files.__class__
|
||||
|
||||
class newcls(oldcls):
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
|
|
@ -89,26 +98,27 @@ def attach_enctype_error_multidict(request):
|
|||
if key not in request.form:
|
||||
raise
|
||||
raise DebugFilesKeyError(request, key)
|
||||
|
||||
newcls.__name__ = oldcls.__name__
|
||||
newcls.__module__ = oldcls.__module__
|
||||
request.files.__class__ = newcls
|
||||
|
||||
|
||||
def _dump_loader_info(loader):
|
||||
yield 'class: %s.%s' % (type(loader).__module__, type(loader).__name__)
|
||||
yield "class: %s.%s" % (type(loader).__module__, type(loader).__name__)
|
||||
for key, value in sorted(loader.__dict__.items()):
|
||||
if key.startswith('_'):
|
||||
if key.startswith("_"):
|
||||
continue
|
||||
if isinstance(value, (tuple, list)):
|
||||
if not all(isinstance(x, (str, text_type)) for x in value):
|
||||
continue
|
||||
yield '%s:' % key
|
||||
yield "%s:" % key
|
||||
for item in value:
|
||||
yield ' - %s' % item
|
||||
yield " - %s" % item
|
||||
continue
|
||||
elif not isinstance(value, (str, text_type, int, float, bool)):
|
||||
continue
|
||||
yield '%s: %r' % (key, value)
|
||||
yield "%s: %r" % (key, value)
|
||||
|
||||
|
||||
def explain_template_loading_attempts(app, template, attempts):
|
||||
|
|
@ -124,45 +134,50 @@ def explain_template_loading_attempts(app, template, attempts):
|
|||
if isinstance(srcobj, Flask):
|
||||
src_info = 'application "%s"' % srcobj.import_name
|
||||
elif isinstance(srcobj, Blueprint):
|
||||
src_info = 'blueprint "%s" (%s)' % (srcobj.name,
|
||||
srcobj.import_name)
|
||||
src_info = 'blueprint "%s" (%s)' % (srcobj.name, srcobj.import_name)
|
||||
else:
|
||||
src_info = repr(srcobj)
|
||||
|
||||
info.append('% 5d: trying loader of %s' % (
|
||||
idx + 1, src_info))
|
||||
info.append("% 5d: trying loader of %s" % (idx + 1, src_info))
|
||||
|
||||
for line in _dump_loader_info(loader):
|
||||
info.append(' %s' % line)
|
||||
info.append(" %s" % line)
|
||||
|
||||
if triple is None:
|
||||
detail = 'no match'
|
||||
detail = "no match"
|
||||
else:
|
||||
detail = 'found (%r)' % (triple[1] or '<string>')
|
||||
detail = "found (%r)" % (triple[1] or "<string>")
|
||||
total_found += 1
|
||||
info.append(' -> %s' % detail)
|
||||
info.append(" -> %s" % detail)
|
||||
|
||||
seems_fishy = False
|
||||
if total_found == 0:
|
||||
info.append('Error: the template could not be found.')
|
||||
info.append("Error: the template could not be found.")
|
||||
seems_fishy = True
|
||||
elif total_found > 1:
|
||||
info.append('Warning: multiple loaders returned a match for the template.')
|
||||
info.append("Warning: multiple loaders returned a match for the template.")
|
||||
seems_fishy = True
|
||||
|
||||
if blueprint is not None and seems_fishy:
|
||||
info.append(' The template was looked up from an endpoint that '
|
||||
'belongs to the blueprint "%s".' % blueprint)
|
||||
info.append(' Maybe you did not place a template in the right folder?')
|
||||
info.append(' See http://flask.pocoo.org/docs/blueprints/#templates')
|
||||
info.append(
|
||||
" The template was looked up from an endpoint that "
|
||||
'belongs to the blueprint "%s".' % blueprint
|
||||
)
|
||||
info.append(" Maybe you did not place a template in the right folder?")
|
||||
info.append(" See http://flask.pocoo.org/docs/blueprints/#templates")
|
||||
|
||||
app.logger.info('\n'.join(info))
|
||||
app.logger.info("\n".join(info))
|
||||
|
||||
|
||||
def explain_ignored_app_run():
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
||||
warn(Warning('Silently ignoring app.run() because the '
|
||||
'application is run from the flask command line '
|
||||
'executable. Consider putting app.run() behind an '
|
||||
'if __name__ == "__main__" guard to silence this '
|
||||
'warning.'), stacklevel=3)
|
||||
if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
|
||||
warn(
|
||||
Warning(
|
||||
"Silently ignoring app.run() because the "
|
||||
"application is run from the flask command line "
|
||||
"executable. Consider putting app.run() behind an "
|
||||
'if __name__ == "__main__" guard to silence this '
|
||||
"warning."
|
||||
),
|
||||
stacklevel=3,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@ from functools import partial
|
|||
from werkzeug.local import LocalStack, LocalProxy
|
||||
|
||||
|
||||
_request_ctx_err_msg = '''\
|
||||
_request_ctx_err_msg = """\
|
||||
Working outside of request context.
|
||||
|
||||
This typically means that you attempted to use functionality that needed
|
||||
an active HTTP request. Consult the documentation on testing for
|
||||
information about how to avoid this problem.\
|
||||
'''
|
||||
_app_ctx_err_msg = '''\
|
||||
"""
|
||||
_app_ctx_err_msg = """\
|
||||
Working outside of application context.
|
||||
|
||||
This typically means that you attempted to use functionality that needed
|
||||
to interface with the current application object in some way. To solve
|
||||
this, set up an application context with app.app_context(). See the
|
||||
documentation for more information.\
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
def _lookup_req_object(name):
|
||||
|
|
@ -56,6 +56,6 @@ def _find_app():
|
|||
_request_ctx_stack = LocalStack()
|
||||
_app_ctx_stack = LocalStack()
|
||||
current_app = LocalProxy(_find_app)
|
||||
request = LocalProxy(partial(_lookup_req_object, 'request'))
|
||||
session = LocalProxy(partial(_lookup_req_object, 'session'))
|
||||
g = LocalProxy(partial(_lookup_app_object, 'g'))
|
||||
request = LocalProxy(partial(_lookup_req_object, "request"))
|
||||
session = LocalProxy(partial(_lookup_req_object, "session"))
|
||||
g = LocalProxy(partial(_lookup_app_object, "g"))
|
||||
|
|
|
|||
289
flask/helpers.py
289
flask/helpers.py
|
|
@ -24,15 +24,13 @@ from functools import update_wrapper
|
|||
|
||||
from werkzeug.urls import url_quote
|
||||
from werkzeug.datastructures import Headers, Range
|
||||
from werkzeug.exceptions import BadRequest, NotFound, \
|
||||
RequestedRangeNotSatisfiable
|
||||
from werkzeug.exceptions import BadRequest, NotFound, RequestedRangeNotSatisfiable
|
||||
|
||||
from werkzeug.wsgi import wrap_file
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
from .signals import message_flashed
|
||||
from .globals import session, _request_ctx_stack, _app_ctx_stack, \
|
||||
current_app, request
|
||||
from .globals import session, _request_ctx_stack, _app_ctx_stack, current_app, request
|
||||
from ._compat import string_types, text_type, PY2, fspath
|
||||
|
||||
# sentinel
|
||||
|
|
@ -42,8 +40,9 @@ _missing = object()
|
|||
# what separators does this operating system provide that are not a slash?
|
||||
# this is used by the send_from_directory function to ensure that nobody is
|
||||
# able to access files from outside the filesystem.
|
||||
_os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep]
|
||||
if sep not in (None, '/'))
|
||||
_os_alt_seps = list(
|
||||
sep for sep in [os.path.sep, os.path.altsep] if sep not in (None, "/")
|
||||
)
|
||||
|
||||
|
||||
def get_env():
|
||||
|
|
@ -51,7 +50,7 @@ def get_env():
|
|||
:envvar:`FLASK_ENV` environment variable. The default is
|
||||
``'production'``.
|
||||
"""
|
||||
return os.environ.get('FLASK_ENV') or 'production'
|
||||
return os.environ.get("FLASK_ENV") or "production"
|
||||
|
||||
|
||||
def get_debug_flag():
|
||||
|
|
@ -60,12 +59,12 @@ def get_debug_flag():
|
|||
``True`` if :func:`.get_env` returns ``'development'``, or ``False``
|
||||
otherwise.
|
||||
"""
|
||||
val = os.environ.get('FLASK_DEBUG')
|
||||
val = os.environ.get("FLASK_DEBUG")
|
||||
|
||||
if not val:
|
||||
return get_env() == 'development'
|
||||
return get_env() == "development"
|
||||
|
||||
return val.lower() not in ('0', 'false', 'no')
|
||||
return val.lower() not in ("0", "false", "no")
|
||||
|
||||
|
||||
def get_load_dotenv(default=True):
|
||||
|
|
@ -75,20 +74,19 @@ def get_load_dotenv(default=True):
|
|||
|
||||
:param default: What to return if the env var isn't set.
|
||||
"""
|
||||
val = os.environ.get('FLASK_SKIP_DOTENV')
|
||||
val = os.environ.get("FLASK_SKIP_DOTENV")
|
||||
|
||||
if not val:
|
||||
return default
|
||||
|
||||
return val.lower() in ('0', 'false', 'no')
|
||||
return val.lower() in ("0", "false", "no")
|
||||
|
||||
|
||||
def _endpoint_from_view_func(view_func):
|
||||
"""Internal helper that returns the default endpoint for a given
|
||||
function. This always is the function name.
|
||||
"""
|
||||
assert view_func is not None, 'expected view func if endpoint ' \
|
||||
'is not provided.'
|
||||
assert view_func is not None, "expected view func if endpoint " "is not provided."
|
||||
return view_func.__name__
|
||||
|
||||
|
||||
|
|
@ -129,16 +127,20 @@ def stream_with_context(generator_or_function):
|
|||
try:
|
||||
gen = iter(generator_or_function)
|
||||
except TypeError:
|
||||
|
||||
def decorator(*args, **kwargs):
|
||||
gen = generator_or_function(*args, **kwargs)
|
||||
return stream_with_context(gen)
|
||||
|
||||
return update_wrapper(decorator, generator_or_function)
|
||||
|
||||
def generator():
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is None:
|
||||
raise RuntimeError('Attempted to stream with context but '
|
||||
'there was no context in the first place to keep around.')
|
||||
raise RuntimeError(
|
||||
"Attempted to stream with context but "
|
||||
"there was no context in the first place to keep around."
|
||||
)
|
||||
with ctx:
|
||||
# Dummy sentinel. Has to be inside the context block or we're
|
||||
# not actually keeping the context around.
|
||||
|
|
@ -152,7 +154,7 @@ def stream_with_context(generator_or_function):
|
|||
for item in gen:
|
||||
yield item
|
||||
finally:
|
||||
if hasattr(gen, 'close'):
|
||||
if hasattr(gen, "close"):
|
||||
gen.close()
|
||||
|
||||
# The trick is to start the generator. Then the code execution runs until
|
||||
|
|
@ -291,9 +293,9 @@ def url_for(endpoint, **values):
|
|||
|
||||
if appctx is None:
|
||||
raise RuntimeError(
|
||||
'Attempted to generate a URL without the application context being'
|
||||
' pushed. This has to be executed when application context is'
|
||||
' available.'
|
||||
"Attempted to generate a URL without the application context being"
|
||||
" pushed. This has to be executed when application context is"
|
||||
" available."
|
||||
)
|
||||
|
||||
# If request specific information is available we have some extra
|
||||
|
|
@ -302,13 +304,13 @@ def url_for(endpoint, **values):
|
|||
url_adapter = reqctx.url_adapter
|
||||
blueprint_name = request.blueprint
|
||||
|
||||
if endpoint[:1] == '.':
|
||||
if endpoint[:1] == ".":
|
||||
if blueprint_name is not None:
|
||||
endpoint = blueprint_name + endpoint
|
||||
else:
|
||||
endpoint = endpoint[1:]
|
||||
|
||||
external = values.pop('_external', False)
|
||||
external = values.pop("_external", False)
|
||||
|
||||
# Otherwise go with the url adapter from the appctx and make
|
||||
# the URLs external by default.
|
||||
|
|
@ -317,16 +319,16 @@ def url_for(endpoint, **values):
|
|||
|
||||
if url_adapter is None:
|
||||
raise RuntimeError(
|
||||
'Application was not able to create a URL adapter for request'
|
||||
' independent URL generation. You might be able to fix this by'
|
||||
' setting the SERVER_NAME config variable.'
|
||||
"Application was not able to create a URL adapter for request"
|
||||
" independent URL generation. You might be able to fix this by"
|
||||
" setting the SERVER_NAME config variable."
|
||||
)
|
||||
|
||||
external = values.pop('_external', True)
|
||||
external = values.pop("_external", True)
|
||||
|
||||
anchor = values.pop('_anchor', None)
|
||||
method = values.pop('_method', None)
|
||||
scheme = values.pop('_scheme', None)
|
||||
anchor = values.pop("_anchor", None)
|
||||
method = values.pop("_method", None)
|
||||
scheme = values.pop("_scheme", None)
|
||||
appctx.app.inject_url_defaults(endpoint, values)
|
||||
|
||||
# This is not the best way to deal with this but currently the
|
||||
|
|
@ -335,28 +337,29 @@ def url_for(endpoint, **values):
|
|||
old_scheme = None
|
||||
if scheme is not None:
|
||||
if not external:
|
||||
raise ValueError('When specifying _scheme, _external must be True')
|
||||
raise ValueError("When specifying _scheme, _external must be True")
|
||||
old_scheme = url_adapter.url_scheme
|
||||
url_adapter.url_scheme = scheme
|
||||
|
||||
try:
|
||||
try:
|
||||
rv = url_adapter.build(endpoint, values, method=method,
|
||||
force_external=external)
|
||||
rv = url_adapter.build(
|
||||
endpoint, values, method=method, force_external=external
|
||||
)
|
||||
finally:
|
||||
if old_scheme is not None:
|
||||
url_adapter.url_scheme = old_scheme
|
||||
except BuildError as error:
|
||||
# We need to inject the values again so that the app callback can
|
||||
# deal with that sort of stuff.
|
||||
values['_external'] = external
|
||||
values['_anchor'] = anchor
|
||||
values['_method'] = method
|
||||
values['_scheme'] = scheme
|
||||
values["_external"] = external
|
||||
values["_anchor"] = anchor
|
||||
values["_method"] = method
|
||||
values["_scheme"] = scheme
|
||||
return appctx.app.handle_url_build_error(error, endpoint, values)
|
||||
|
||||
if anchor is not None:
|
||||
rv += '#' + url_quote(anchor)
|
||||
rv += "#" + url_quote(anchor)
|
||||
return rv
|
||||
|
||||
|
||||
|
|
@ -379,11 +382,10 @@ def get_template_attribute(template_name, attribute):
|
|||
:param template_name: the name of the template
|
||||
:param attribute: the name of the variable of macro to access
|
||||
"""
|
||||
return getattr(current_app.jinja_env.get_template(template_name).module,
|
||||
attribute)
|
||||
return getattr(current_app.jinja_env.get_template(template_name).module, attribute)
|
||||
|
||||
|
||||
def flash(message, category='message'):
|
||||
def flash(message, category="message"):
|
||||
"""Flashes a message to the next request. In order to remove the
|
||||
flashed message from the session and to display it to the user,
|
||||
the template has to call :func:`get_flashed_messages`.
|
||||
|
|
@ -405,11 +407,12 @@ def flash(message, category='message'):
|
|||
# This assumed that changes made to mutable structures in the session are
|
||||
# always in sync with the session object, which is not true for session
|
||||
# implementations that use external storage for keeping their keys/values.
|
||||
flashes = session.get('_flashes', [])
|
||||
flashes = session.get("_flashes", [])
|
||||
flashes.append((category, message))
|
||||
session['_flashes'] = flashes
|
||||
message_flashed.send(current_app._get_current_object(),
|
||||
message=message, category=category)
|
||||
session["_flashes"] = flashes
|
||||
message_flashed.send(
|
||||
current_app._get_current_object(), message=message, category=category
|
||||
)
|
||||
|
||||
|
||||
def get_flashed_messages(with_categories=False, category_filter=[]):
|
||||
|
|
@ -442,8 +445,9 @@ def get_flashed_messages(with_categories=False, category_filter=[]):
|
|||
"""
|
||||
flashes = _request_ctx_stack.top.flashes
|
||||
if flashes is None:
|
||||
_request_ctx_stack.top.flashes = flashes = session.pop('_flashes') \
|
||||
if '_flashes' in session else []
|
||||
_request_ctx_stack.top.flashes = flashes = (
|
||||
session.pop("_flashes") if "_flashes" in session else []
|
||||
)
|
||||
if category_filter:
|
||||
flashes = list(filter(lambda f: f[0] in category_filter, flashes))
|
||||
if not with_categories:
|
||||
|
|
@ -451,9 +455,16 @@ def get_flashed_messages(with_categories=False, category_filter=[]):
|
|||
return flashes
|
||||
|
||||
|
||||
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
||||
attachment_filename=None, add_etags=True,
|
||||
cache_timeout=None, conditional=False, last_modified=None):
|
||||
def send_file(
|
||||
filename_or_fp,
|
||||
mimetype=None,
|
||||
as_attachment=False,
|
||||
attachment_filename=None,
|
||||
add_etags=True,
|
||||
cache_timeout=None,
|
||||
conditional=False,
|
||||
last_modified=None,
|
||||
):
|
||||
"""Sends the contents of a file to the client. This will use the
|
||||
most efficient method available and configured. By default it will
|
||||
try to use the WSGI server's file_wrapper support. Alternatively
|
||||
|
|
@ -545,7 +556,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
mtime = None
|
||||
fsize = None
|
||||
|
||||
if hasattr(filename_or_fp, '__fspath__'):
|
||||
if hasattr(filename_or_fp, "__fspath__"):
|
||||
filename_or_fp = fspath(filename_or_fp)
|
||||
|
||||
if isinstance(filename_or_fp, string_types):
|
||||
|
|
@ -561,62 +572,67 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
|
||||
if mimetype is None:
|
||||
if attachment_filename is not None:
|
||||
mimetype = mimetypes.guess_type(attachment_filename)[0] \
|
||||
or 'application/octet-stream'
|
||||
mimetype = (
|
||||
mimetypes.guess_type(attachment_filename)[0]
|
||||
or "application/octet-stream"
|
||||
)
|
||||
|
||||
if mimetype is None:
|
||||
raise ValueError(
|
||||
'Unable to infer MIME-type because no filename is available. '
|
||||
'Please set either `attachment_filename`, pass a filepath to '
|
||||
'`filename_or_fp` or set your own MIME-type via `mimetype`.'
|
||||
"Unable to infer MIME-type because no filename is available. "
|
||||
"Please set either `attachment_filename`, pass a filepath to "
|
||||
"`filename_or_fp` or set your own MIME-type via `mimetype`."
|
||||
)
|
||||
|
||||
headers = Headers()
|
||||
if as_attachment:
|
||||
if attachment_filename is None:
|
||||
raise TypeError('filename unavailable, required for '
|
||||
'sending as attachment')
|
||||
raise TypeError(
|
||||
"filename unavailable, required for " "sending as attachment"
|
||||
)
|
||||
|
||||
if not isinstance(attachment_filename, text_type):
|
||||
attachment_filename = attachment_filename.decode('utf-8')
|
||||
attachment_filename = attachment_filename.decode("utf-8")
|
||||
|
||||
try:
|
||||
attachment_filename = attachment_filename.encode('ascii')
|
||||
attachment_filename = attachment_filename.encode("ascii")
|
||||
except UnicodeEncodeError:
|
||||
filenames = {
|
||||
'filename': unicodedata.normalize(
|
||||
'NFKD', attachment_filename).encode('ascii', 'ignore'),
|
||||
'filename*': "UTF-8''%s" % url_quote(attachment_filename),
|
||||
"filename": unicodedata.normalize("NFKD", attachment_filename).encode(
|
||||
"ascii", "ignore"
|
||||
),
|
||||
"filename*": "UTF-8''%s" % url_quote(attachment_filename),
|
||||
}
|
||||
else:
|
||||
filenames = {'filename': attachment_filename}
|
||||
filenames = {"filename": attachment_filename}
|
||||
|
||||
headers.add('Content-Disposition', 'attachment', **filenames)
|
||||
headers.add("Content-Disposition", "attachment", **filenames)
|
||||
|
||||
if current_app.use_x_sendfile and filename:
|
||||
if file is not None:
|
||||
file.close()
|
||||
headers['X-Sendfile'] = filename
|
||||
headers["X-Sendfile"] = filename
|
||||
fsize = os.path.getsize(filename)
|
||||
headers['Content-Length'] = fsize
|
||||
headers["Content-Length"] = fsize
|
||||
data = None
|
||||
else:
|
||||
if file is None:
|
||||
file = open(filename, 'rb')
|
||||
file = open(filename, "rb")
|
||||
mtime = os.path.getmtime(filename)
|
||||
fsize = os.path.getsize(filename)
|
||||
headers['Content-Length'] = fsize
|
||||
headers["Content-Length"] = fsize
|
||||
elif isinstance(file, io.BytesIO):
|
||||
try:
|
||||
fsize = file.getbuffer().nbytes
|
||||
except AttributeError:
|
||||
# Python 2 doesn't have getbuffer
|
||||
fsize = len(file.getvalue())
|
||||
headers['Content-Length'] = fsize
|
||||
headers["Content-Length"] = fsize
|
||||
data = wrap_file(request.environ, file)
|
||||
|
||||
rv = current_app.response_class(data, mimetype=mimetype, headers=headers,
|
||||
direct_passthrough=True)
|
||||
rv = current_app.response_class(
|
||||
data, mimetype=mimetype, headers=headers, direct_passthrough=True
|
||||
)
|
||||
|
||||
if last_modified is not None:
|
||||
rv.last_modified = last_modified
|
||||
|
|
@ -634,22 +650,29 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
from warnings import warn
|
||||
|
||||
try:
|
||||
rv.set_etag('%s-%s-%s' % (
|
||||
os.path.getmtime(filename),
|
||||
os.path.getsize(filename),
|
||||
adler32(
|
||||
filename.encode('utf-8') if isinstance(filename, text_type)
|
||||
else filename
|
||||
) & 0xffffffff
|
||||
))
|
||||
rv.set_etag(
|
||||
"%s-%s-%s"
|
||||
% (
|
||||
os.path.getmtime(filename),
|
||||
os.path.getsize(filename),
|
||||
adler32(
|
||||
filename.encode("utf-8")
|
||||
if isinstance(filename, text_type)
|
||||
else filename
|
||||
)
|
||||
& 0xFFFFFFFF,
|
||||
)
|
||||
)
|
||||
except OSError:
|
||||
warn('Access %s failed, maybe it does not exist, so ignore etags in '
|
||||
'headers' % filename, stacklevel=2)
|
||||
warn(
|
||||
"Access %s failed, maybe it does not exist, so ignore etags in "
|
||||
"headers" % filename,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if conditional:
|
||||
try:
|
||||
rv = rv.make_conditional(request, accept_ranges=True,
|
||||
complete_length=fsize)
|
||||
rv = rv.make_conditional(request, accept_ranges=True, complete_length=fsize)
|
||||
except RequestedRangeNotSatisfiable:
|
||||
if file is not None:
|
||||
file.close()
|
||||
|
|
@ -657,7 +680,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
|
|||
# make sure we don't send x-sendfile for servers that
|
||||
# ignore the 304 status code for x-sendfile.
|
||||
if rv.status_code == 304:
|
||||
rv.headers.pop('x-sendfile', None)
|
||||
rv.headers.pop("x-sendfile", None)
|
||||
return rv
|
||||
|
||||
|
||||
|
|
@ -682,14 +705,14 @@ def safe_join(directory, *pathnames):
|
|||
parts = [directory]
|
||||
|
||||
for filename in pathnames:
|
||||
if filename != '':
|
||||
if filename != "":
|
||||
filename = posixpath.normpath(filename)
|
||||
|
||||
if (
|
||||
any(sep in filename for sep in _os_alt_seps)
|
||||
or os.path.isabs(filename)
|
||||
or filename == '..'
|
||||
or filename.startswith('../')
|
||||
or filename == ".."
|
||||
or filename.startswith("../")
|
||||
):
|
||||
raise NotFound()
|
||||
|
||||
|
|
@ -735,7 +758,7 @@ def send_from_directory(directory, filename, **options):
|
|||
raise NotFound()
|
||||
except (TypeError, ValueError):
|
||||
raise BadRequest()
|
||||
options.setdefault('conditional', True)
|
||||
options.setdefault("conditional", True)
|
||||
return send_file(filename, **options)
|
||||
|
||||
|
||||
|
|
@ -747,7 +770,7 @@ def get_root_path(import_name):
|
|||
"""
|
||||
# Module already imported and has a file attribute. Use that first.
|
||||
mod = sys.modules.get(import_name)
|
||||
if mod is not None and hasattr(mod, '__file__'):
|
||||
if mod is not None and hasattr(mod, "__file__"):
|
||||
return os.path.dirname(os.path.abspath(mod.__file__))
|
||||
|
||||
# Next attempt: check the loader.
|
||||
|
|
@ -756,30 +779,32 @@ def get_root_path(import_name):
|
|||
# Loader does not exist or we're referring to an unloaded main module
|
||||
# or a main module without path (interactive sessions), go with the
|
||||
# current working directory.
|
||||
if loader is None or import_name == '__main__':
|
||||
if loader is None or import_name == "__main__":
|
||||
return os.getcwd()
|
||||
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
# Some other loaders might exhibit the same behavior.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
if hasattr(loader, "get_filename"):
|
||||
filepath = loader.get_filename(import_name)
|
||||
else:
|
||||
# Fall back to imports.
|
||||
__import__(import_name)
|
||||
mod = sys.modules[import_name]
|
||||
filepath = getattr(mod, '__file__', None)
|
||||
filepath = getattr(mod, "__file__", None)
|
||||
|
||||
# If we don't have a filepath it might be because we are a
|
||||
# namespace package. In this case we pick the root path from the
|
||||
# first module that is contained in our package.
|
||||
if filepath is None:
|
||||
raise RuntimeError('No root path can be found for the provided '
|
||||
'module "%s". This can happen because the '
|
||||
'module came from an import hook that does '
|
||||
'not provide file name information or because '
|
||||
'it\'s a namespace package. In this case '
|
||||
'the root path needs to be explicitly '
|
||||
'provided.' % import_name)
|
||||
raise RuntimeError(
|
||||
"No root path can be found for the provided "
|
||||
'module "%s". This can happen because the '
|
||||
"module came from an import hook that does "
|
||||
"not provide file name information or because "
|
||||
"it's a namespace package. In this case "
|
||||
"the root path needs to be explicitly "
|
||||
"provided." % import_name
|
||||
)
|
||||
|
||||
# filepath is import_name.py for a module, or __init__.py for a package.
|
||||
return os.path.dirname(os.path.abspath(filepath))
|
||||
|
|
@ -791,21 +816,26 @@ def _matching_loader_thinks_module_is_package(loader, mod_name):
|
|||
"""
|
||||
# If the loader can tell us if something is a package, we can
|
||||
# directly ask the loader.
|
||||
if hasattr(loader, 'is_package'):
|
||||
if hasattr(loader, "is_package"):
|
||||
return loader.is_package(mod_name)
|
||||
# importlib's namespace loaders do not have this functionality but
|
||||
# all the modules it loads are packages, so we can take advantage of
|
||||
# this information.
|
||||
elif (loader.__class__.__module__ == '_frozen_importlib' and
|
||||
loader.__class__.__name__ == 'NamespaceLoader'):
|
||||
elif (
|
||||
loader.__class__.__module__ == "_frozen_importlib"
|
||||
and loader.__class__.__name__ == "NamespaceLoader"
|
||||
):
|
||||
return True
|
||||
# Otherwise we need to fail with an error that explains what went
|
||||
# wrong.
|
||||
raise AttributeError(
|
||||
('%s.is_package() method is missing but is required by Flask of '
|
||||
'PEP 302 import hooks. If you do not use import hooks and '
|
||||
'you encounter this error please file a bug against Flask.') %
|
||||
loader.__class__.__name__)
|
||||
(
|
||||
"%s.is_package() method is missing but is required by Flask of "
|
||||
"PEP 302 import hooks. If you do not use import hooks and "
|
||||
"you encounter this error please file a bug against Flask."
|
||||
)
|
||||
% loader.__class__.__name__
|
||||
)
|
||||
|
||||
|
||||
def find_package(import_name):
|
||||
|
|
@ -816,16 +846,16 @@ def find_package(import_name):
|
|||
import the module. The prefix is the path below which a UNIX like
|
||||
folder structure exists (lib, share etc.).
|
||||
"""
|
||||
root_mod_name = import_name.split('.')[0]
|
||||
root_mod_name = import_name.split(".")[0]
|
||||
loader = pkgutil.get_loader(root_mod_name)
|
||||
if loader is None or import_name == '__main__':
|
||||
if loader is None or import_name == "__main__":
|
||||
# import name is not found, or interactive/main module
|
||||
package_path = os.getcwd()
|
||||
else:
|
||||
# For .egg, zipimporter does not have get_filename until Python 2.7.
|
||||
if hasattr(loader, 'get_filename'):
|
||||
if hasattr(loader, "get_filename"):
|
||||
filename = loader.get_filename(root_mod_name)
|
||||
elif hasattr(loader, 'archive'):
|
||||
elif hasattr(loader, "archive"):
|
||||
# zipimporter's loader.archive points to the .egg or .zip
|
||||
# archive filename is dropped in call to dirname below.
|
||||
filename = loader.archive
|
||||
|
|
@ -841,21 +871,20 @@ def find_package(import_name):
|
|||
# In case the root module is a package we need to chop of the
|
||||
# rightmost part. This needs to go through a helper function
|
||||
# because of python 3.3 namespace packages.
|
||||
if _matching_loader_thinks_module_is_package(
|
||||
loader, root_mod_name):
|
||||
if _matching_loader_thinks_module_is_package(loader, root_mod_name):
|
||||
package_path = os.path.dirname(package_path)
|
||||
|
||||
site_parent, site_folder = os.path.split(package_path)
|
||||
py_prefix = os.path.abspath(sys.prefix)
|
||||
if package_path.startswith(py_prefix):
|
||||
return py_prefix, package_path
|
||||
elif site_folder.lower() == 'site-packages':
|
||||
elif site_folder.lower() == "site-packages":
|
||||
parent, folder = os.path.split(site_parent)
|
||||
# Windows like installations
|
||||
if folder.lower() == 'lib':
|
||||
if folder.lower() == "lib":
|
||||
base_dir = parent
|
||||
# UNIX like installations
|
||||
elif os.path.basename(parent).lower() == 'lib':
|
||||
elif os.path.basename(parent).lower() == "lib":
|
||||
base_dir = os.path.dirname(parent)
|
||||
else:
|
||||
base_dir = site_parent
|
||||
|
|
@ -921,8 +950,9 @@ class _PackageBoundObject(object):
|
|||
self._static_folder = value
|
||||
|
||||
static_folder = property(
|
||||
_get_static_folder, _set_static_folder,
|
||||
doc='The absolute path to the configured static folder.'
|
||||
_get_static_folder,
|
||||
_set_static_folder,
|
||||
doc="The absolute path to the configured static folder.",
|
||||
)
|
||||
del _get_static_folder, _set_static_folder
|
||||
|
||||
|
|
@ -931,14 +961,15 @@ class _PackageBoundObject(object):
|
|||
return self._static_url_path
|
||||
|
||||
if self.static_folder is not None:
|
||||
return '/' + os.path.basename(self.static_folder)
|
||||
return "/" + os.path.basename(self.static_folder)
|
||||
|
||||
def _set_static_url_path(self, value):
|
||||
self._static_url_path = value
|
||||
|
||||
static_url_path = property(
|
||||
_get_static_url_path, _set_static_url_path,
|
||||
doc='The URL prefix that the static route will be registered for.'
|
||||
_get_static_url_path,
|
||||
_set_static_url_path,
|
||||
doc="The URL prefix that the static route will be registered for.",
|
||||
)
|
||||
del _get_static_url_path, _set_static_url_path
|
||||
|
||||
|
|
@ -958,8 +989,7 @@ class _PackageBoundObject(object):
|
|||
.. versionadded:: 0.5
|
||||
"""
|
||||
if self.template_folder is not None:
|
||||
return FileSystemLoader(os.path.join(self.root_path,
|
||||
self.template_folder))
|
||||
return FileSystemLoader(os.path.join(self.root_path, self.template_folder))
|
||||
|
||||
def get_send_file_max_age(self, filename):
|
||||
"""Provides default cache_timeout for the :func:`send_file` functions.
|
||||
|
|
@ -994,14 +1024,15 @@ class _PackageBoundObject(object):
|
|||
.. versionadded:: 0.5
|
||||
"""
|
||||
if not self.has_static_folder:
|
||||
raise RuntimeError('No static folder for this object')
|
||||
raise RuntimeError("No static folder for this object")
|
||||
# Ensure get_send_file_max_age is called in all cases.
|
||||
# Here, we ensure get_send_file_max_age is called for Blueprints.
|
||||
cache_timeout = self.get_send_file_max_age(filename)
|
||||
return send_from_directory(self.static_folder, filename,
|
||||
cache_timeout=cache_timeout)
|
||||
return send_from_directory(
|
||||
self.static_folder, filename, cache_timeout=cache_timeout
|
||||
)
|
||||
|
||||
def open_resource(self, resource, mode='rb'):
|
||||
def open_resource(self, resource, mode="rb"):
|
||||
"""Opens a resource from the application's resource folder. To see
|
||||
how this works, consider the following folder structure::
|
||||
|
||||
|
|
@ -1024,8 +1055,8 @@ class _PackageBoundObject(object):
|
|||
subfolders use forward slashes as separator.
|
||||
:param mode: resource file opening mode, default is 'rb'.
|
||||
"""
|
||||
if mode not in ('r', 'rb'):
|
||||
raise ValueError('Resources can only be opened for reading')
|
||||
if mode not in ("r", "rb"):
|
||||
raise ValueError("Resources can only be opened for reading")
|
||||
return open(os.path.join(self.root_path, resource), mode)
|
||||
|
||||
|
||||
|
|
@ -1052,7 +1083,7 @@ def is_ip(value):
|
|||
:return: True if string is an IP address
|
||||
:rtype: bool
|
||||
"""
|
||||
if PY2 and os.name == 'nt':
|
||||
if PY2 and os.name == "nt":
|
||||
try:
|
||||
socket.inet_aton(value)
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -23,12 +23,20 @@ from itsdangerous import json as _json
|
|||
|
||||
# Figure out if simplejson escapes slashes. This behavior was changed
|
||||
# from one version to another without reason.
|
||||
_slash_escape = '\\/' not in _json.dumps('/')
|
||||
_slash_escape = "\\/" not in _json.dumps("/")
|
||||
|
||||
|
||||
__all__ = ['dump', 'dumps', 'load', 'loads', 'htmlsafe_dump',
|
||||
'htmlsafe_dumps', 'JSONDecoder', 'JSONEncoder',
|
||||
'jsonify']
|
||||
__all__ = [
|
||||
"dump",
|
||||
"dumps",
|
||||
"load",
|
||||
"loads",
|
||||
"htmlsafe_dump",
|
||||
"htmlsafe_dumps",
|
||||
"JSONDecoder",
|
||||
"JSONEncoder",
|
||||
"jsonify",
|
||||
]
|
||||
|
||||
|
||||
def _wrap_reader_for_text(fp, encoding):
|
||||
|
|
@ -39,7 +47,7 @@ def _wrap_reader_for_text(fp, encoding):
|
|||
|
||||
def _wrap_writer_for_text(fp, encoding):
|
||||
try:
|
||||
fp.write('')
|
||||
fp.write("")
|
||||
except TypeError:
|
||||
fp = io.TextIOWrapper(fp, encoding)
|
||||
return fp
|
||||
|
|
@ -76,7 +84,7 @@ class JSONEncoder(_json.JSONEncoder):
|
|||
return http_date(o.timetuple())
|
||||
if isinstance(o, uuid.UUID):
|
||||
return str(o)
|
||||
if hasattr(o, '__html__'):
|
||||
if hasattr(o, "__html__"):
|
||||
return text_type(o.__html__())
|
||||
return _json.JSONEncoder.default(self, o)
|
||||
|
||||
|
|
@ -94,18 +102,17 @@ def _dump_arg_defaults(kwargs):
|
|||
if current_app:
|
||||
bp = current_app.blueprints.get(request.blueprint) if request else None
|
||||
kwargs.setdefault(
|
||||
'cls',
|
||||
bp.json_encoder if bp and bp.json_encoder
|
||||
else current_app.json_encoder
|
||||
"cls",
|
||||
bp.json_encoder if bp and bp.json_encoder else current_app.json_encoder,
|
||||
)
|
||||
|
||||
if not current_app.config['JSON_AS_ASCII']:
|
||||
kwargs.setdefault('ensure_ascii', False)
|
||||
if not current_app.config["JSON_AS_ASCII"]:
|
||||
kwargs.setdefault("ensure_ascii", False)
|
||||
|
||||
kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS'])
|
||||
kwargs.setdefault("sort_keys", current_app.config["JSON_SORT_KEYS"])
|
||||
else:
|
||||
kwargs.setdefault('sort_keys', True)
|
||||
kwargs.setdefault('cls', JSONEncoder)
|
||||
kwargs.setdefault("sort_keys", True)
|
||||
kwargs.setdefault("cls", JSONEncoder)
|
||||
|
||||
|
||||
def _load_arg_defaults(kwargs):
|
||||
|
|
@ -113,12 +120,11 @@ def _load_arg_defaults(kwargs):
|
|||
if current_app:
|
||||
bp = current_app.blueprints.get(request.blueprint) if request else None
|
||||
kwargs.setdefault(
|
||||
'cls',
|
||||
bp.json_decoder if bp and bp.json_decoder
|
||||
else current_app.json_decoder
|
||||
"cls",
|
||||
bp.json_decoder if bp and bp.json_decoder else current_app.json_decoder,
|
||||
)
|
||||
else:
|
||||
kwargs.setdefault('cls', JSONDecoder)
|
||||
kwargs.setdefault("cls", JSONDecoder)
|
||||
|
||||
|
||||
def detect_encoding(data):
|
||||
|
|
@ -134,34 +140,34 @@ def detect_encoding(data):
|
|||
head = data[:4]
|
||||
|
||||
if head[:3] == codecs.BOM_UTF8:
|
||||
return 'utf-8-sig'
|
||||
return "utf-8-sig"
|
||||
|
||||
if b'\x00' not in head:
|
||||
return 'utf-8'
|
||||
if b"\x00" not in head:
|
||||
return "utf-8"
|
||||
|
||||
if head in (codecs.BOM_UTF32_BE, codecs.BOM_UTF32_LE):
|
||||
return 'utf-32'
|
||||
return "utf-32"
|
||||
|
||||
if head[:2] in (codecs.BOM_UTF16_BE, codecs.BOM_UTF16_LE):
|
||||
return 'utf-16'
|
||||
return "utf-16"
|
||||
|
||||
if len(head) == 4:
|
||||
if head[:3] == b'\x00\x00\x00':
|
||||
return 'utf-32-be'
|
||||
if head[:3] == b"\x00\x00\x00":
|
||||
return "utf-32-be"
|
||||
|
||||
if head[::2] == b'\x00\x00':
|
||||
return 'utf-16-be'
|
||||
if head[::2] == b"\x00\x00":
|
||||
return "utf-16-be"
|
||||
|
||||
if head[1:] == b'\x00\x00\x00':
|
||||
return 'utf-32-le'
|
||||
if head[1:] == b"\x00\x00\x00":
|
||||
return "utf-32-le"
|
||||
|
||||
if head[1::2] == b'\x00\x00':
|
||||
return 'utf-16-le'
|
||||
if head[1::2] == b"\x00\x00":
|
||||
return "utf-16-le"
|
||||
|
||||
if len(head) == 2:
|
||||
return 'utf-16-be' if head.startswith(b'\x00') else 'utf-16-le'
|
||||
return "utf-16-be" if head.startswith(b"\x00") else "utf-16-le"
|
||||
|
||||
return 'utf-8'
|
||||
return "utf-8"
|
||||
|
||||
|
||||
def dumps(obj, **kwargs):
|
||||
|
|
@ -175,7 +181,7 @@ def dumps(obj, **kwargs):
|
|||
and can be overridden by the simplejson ``ensure_ascii`` parameter.
|
||||
"""
|
||||
_dump_arg_defaults(kwargs)
|
||||
encoding = kwargs.pop('encoding', None)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
rv = _json.dumps(obj, **kwargs)
|
||||
if encoding is not None and isinstance(rv, text_type):
|
||||
rv = rv.encode(encoding)
|
||||
|
|
@ -185,7 +191,7 @@ def dumps(obj, **kwargs):
|
|||
def dump(obj, fp, **kwargs):
|
||||
"""Like :func:`dumps` but writes into a file object."""
|
||||
_dump_arg_defaults(kwargs)
|
||||
encoding = kwargs.pop('encoding', None)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
if encoding is not None:
|
||||
fp = _wrap_writer_for_text(fp, encoding)
|
||||
_json.dump(obj, fp, **kwargs)
|
||||
|
|
@ -198,7 +204,7 @@ def loads(s, **kwargs):
|
|||
"""
|
||||
_load_arg_defaults(kwargs)
|
||||
if isinstance(s, bytes):
|
||||
encoding = kwargs.pop('encoding', None)
|
||||
encoding = kwargs.pop("encoding", None)
|
||||
if encoding is None:
|
||||
encoding = detect_encoding(s)
|
||||
s = s.decode(encoding)
|
||||
|
|
@ -210,7 +216,7 @@ def load(fp, **kwargs):
|
|||
"""
|
||||
_load_arg_defaults(kwargs)
|
||||
if not PY2:
|
||||
fp = _wrap_reader_for_text(fp, kwargs.pop('encoding', None) or 'utf-8')
|
||||
fp = _wrap_reader_for_text(fp, kwargs.pop("encoding", None) or "utf-8")
|
||||
return _json.load(fp, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -239,13 +245,15 @@ def htmlsafe_dumps(obj, **kwargs):
|
|||
quoted. Always single quote attributes if you use the ``|tojson``
|
||||
filter. Alternatively use ``|tojson|forceescape``.
|
||||
"""
|
||||
rv = dumps(obj, **kwargs) \
|
||||
.replace(u'<', u'\\u003c') \
|
||||
.replace(u'>', u'\\u003e') \
|
||||
.replace(u'&', u'\\u0026') \
|
||||
.replace(u"'", u'\\u0027')
|
||||
rv = (
|
||||
dumps(obj, **kwargs)
|
||||
.replace(u"<", u"\\u003c")
|
||||
.replace(u">", u"\\u003e")
|
||||
.replace(u"&", u"\\u0026")
|
||||
.replace(u"'", u"\\u0027")
|
||||
)
|
||||
if not _slash_escape:
|
||||
rv = rv.replace('\\/', '/')
|
||||
rv = rv.replace("\\/", "/")
|
||||
return rv
|
||||
|
||||
|
||||
|
|
@ -304,22 +312,22 @@ def jsonify(*args, **kwargs):
|
|||
"""
|
||||
|
||||
indent = None
|
||||
separators = (',', ':')
|
||||
separators = (",", ":")
|
||||
|
||||
if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug:
|
||||
if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
|
||||
indent = 2
|
||||
separators = (', ', ': ')
|
||||
separators = (", ", ": ")
|
||||
|
||||
if args and kwargs:
|
||||
raise TypeError('jsonify() behavior undefined when passed both args and kwargs')
|
||||
raise TypeError("jsonify() behavior undefined when passed both args and kwargs")
|
||||
elif len(args) == 1: # single args are passed directly to dumps()
|
||||
data = args[0]
|
||||
else:
|
||||
data = args or kwargs
|
||||
|
||||
return current_app.response_class(
|
||||
dumps(data, indent=indent, separators=separators) + '\n',
|
||||
mimetype=current_app.config['JSONIFY_MIMETYPE']
|
||||
dumps(data, indent=indent, separators=separators) + "\n",
|
||||
mimetype=current_app.config["JSONIFY_MIMETYPE"],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ from flask.json import dumps, loads
|
|||
class JSONTag(object):
|
||||
"""Base class for defining type tags for :class:`TaggedJSONSerializer`."""
|
||||
|
||||
__slots__ = ('serializer',)
|
||||
__slots__ = ("serializer",)
|
||||
|
||||
#: The tag to mark the serialized object with. If ``None``, this tag is
|
||||
#: only used as an intermediate step during tagging.
|
||||
|
|
@ -94,7 +94,7 @@ class TagDict(JSONTag):
|
|||
"""
|
||||
|
||||
__slots__ = ()
|
||||
key = ' di'
|
||||
key = " di"
|
||||
|
||||
def check(self, value):
|
||||
return (
|
||||
|
|
@ -105,7 +105,7 @@ class TagDict(JSONTag):
|
|||
|
||||
def to_json(self, value):
|
||||
key = next(iter(value))
|
||||
return {key + '__': self.serializer.tag(value[key])}
|
||||
return {key + "__": self.serializer.tag(value[key])}
|
||||
|
||||
def to_python(self, value):
|
||||
key = next(iter(value))
|
||||
|
|
@ -128,7 +128,7 @@ class PassDict(JSONTag):
|
|||
|
||||
class TagTuple(JSONTag):
|
||||
__slots__ = ()
|
||||
key = ' t'
|
||||
key = " t"
|
||||
|
||||
def check(self, value):
|
||||
return isinstance(value, tuple)
|
||||
|
|
@ -154,13 +154,13 @@ class PassList(JSONTag):
|
|||
|
||||
class TagBytes(JSONTag):
|
||||
__slots__ = ()
|
||||
key = ' b'
|
||||
key = " b"
|
||||
|
||||
def check(self, value):
|
||||
return isinstance(value, bytes)
|
||||
|
||||
def to_json(self, value):
|
||||
return b64encode(value).decode('ascii')
|
||||
return b64encode(value).decode("ascii")
|
||||
|
||||
def to_python(self, value):
|
||||
return b64decode(value)
|
||||
|
|
@ -172,10 +172,10 @@ class TagMarkup(JSONTag):
|
|||
deserializes to an instance of :class:`~flask.Markup`."""
|
||||
|
||||
__slots__ = ()
|
||||
key = ' m'
|
||||
key = " m"
|
||||
|
||||
def check(self, value):
|
||||
return callable(getattr(value, '__html__', None))
|
||||
return callable(getattr(value, "__html__", None))
|
||||
|
||||
def to_json(self, value):
|
||||
return text_type(value.__html__())
|
||||
|
|
@ -186,7 +186,7 @@ class TagMarkup(JSONTag):
|
|||
|
||||
class TagUUID(JSONTag):
|
||||
__slots__ = ()
|
||||
key = ' u'
|
||||
key = " u"
|
||||
|
||||
def check(self, value):
|
||||
return isinstance(value, UUID)
|
||||
|
|
@ -200,7 +200,7 @@ class TagUUID(JSONTag):
|
|||
|
||||
class TagDateTime(JSONTag):
|
||||
__slots__ = ()
|
||||
key = ' d'
|
||||
key = " d"
|
||||
|
||||
def check(self, value):
|
||||
return isinstance(value, datetime)
|
||||
|
|
@ -227,12 +227,18 @@ class TaggedJSONSerializer(object):
|
|||
* :class:`~datetime.datetime`
|
||||
"""
|
||||
|
||||
__slots__ = ('tags', 'order')
|
||||
__slots__ = ("tags", "order")
|
||||
|
||||
#: Tag classes to bind when creating the serializer. Other tags can be
|
||||
#: added later using :meth:`~register`.
|
||||
default_tags = [
|
||||
TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID,
|
||||
TagDict,
|
||||
PassDict,
|
||||
TagTuple,
|
||||
PassList,
|
||||
TagBytes,
|
||||
TagMarkup,
|
||||
TagUUID,
|
||||
TagDateTime,
|
||||
]
|
||||
|
||||
|
|
@ -293,7 +299,7 @@ class TaggedJSONSerializer(object):
|
|||
|
||||
def dumps(self, value):
|
||||
"""Tag the value and dump it to a compact JSON string."""
|
||||
return dumps(self.tag(value), separators=(',', ':'))
|
||||
return dumps(self.tag(value), separators=(",", ":"))
|
||||
|
||||
def loads(self, value):
|
||||
"""Load data from a JSON string and deserialized any tagged objects."""
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ def wsgi_errors_stream():
|
|||
can't import this directly, you can refer to it as
|
||||
``ext://flask.logging.wsgi_errors_stream``.
|
||||
"""
|
||||
return request.environ['wsgi.errors'] if request else sys.stderr
|
||||
return request.environ["wsgi.errors"] if request else sys.stderr
|
||||
|
||||
|
||||
def has_level_handler(logger):
|
||||
|
|
@ -52,9 +52,9 @@ def has_level_handler(logger):
|
|||
#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format
|
||||
#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``.
|
||||
default_handler = logging.StreamHandler(wsgi_errors_stream)
|
||||
default_handler.setFormatter(logging.Formatter(
|
||||
'[%(asctime)s] %(levelname)s in %(module)s: %(message)s'
|
||||
))
|
||||
default_handler.setFormatter(
|
||||
logging.Formatter("[%(asctime)s] %(levelname)s in %(module)s: %(message)s")
|
||||
)
|
||||
|
||||
|
||||
def create_logger(app):
|
||||
|
|
@ -67,7 +67,7 @@ def create_logger(app):
|
|||
:class:`~logging.StreamHandler` for
|
||||
:func:`~flask.logging.wsgi_errors_stream` with a basic format.
|
||||
"""
|
||||
logger = logging.getLogger('flask.app')
|
||||
logger = logging.getLogger("flask.app")
|
||||
|
||||
if app.debug and logger.level == logging.NOTSET:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ class SessionMixin(collections_abc.MutableMapping):
|
|||
@property
|
||||
def permanent(self):
|
||||
"""This reflects the ``'_permanent'`` key in the dict."""
|
||||
return self.get('_permanent', False)
|
||||
return self.get("_permanent", False)
|
||||
|
||||
@permanent.setter
|
||||
def permanent(self, value):
|
||||
self['_permanent'] = bool(value)
|
||||
self["_permanent"] = bool(value)
|
||||
|
||||
#: Some implementations can detect whether a session is newly
|
||||
#: created, but that is not guaranteed. Use with caution. The mixin
|
||||
|
|
@ -98,11 +98,13 @@ class NullSession(SecureCookieSession):
|
|||
"""
|
||||
|
||||
def _fail(self, *args, **kwargs):
|
||||
raise RuntimeError('The session is unavailable because no secret '
|
||||
'key was set. Set the secret_key on the '
|
||||
'application to something unique and secret.')
|
||||
__setitem__ = __delitem__ = clear = pop = popitem = \
|
||||
update = setdefault = _fail
|
||||
raise RuntimeError(
|
||||
"The session is unavailable because no secret "
|
||||
"key was set. Set the secret_key on the "
|
||||
"application to something unique and secret."
|
||||
)
|
||||
|
||||
__setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail
|
||||
del _fail
|
||||
|
||||
|
||||
|
|
@ -180,52 +182,52 @@ class SessionInterface(object):
|
|||
updated to avoid re-running the logic.
|
||||
"""
|
||||
|
||||
rv = app.config['SESSION_COOKIE_DOMAIN']
|
||||
rv = app.config["SESSION_COOKIE_DOMAIN"]
|
||||
|
||||
# set explicitly, or cached from SERVER_NAME detection
|
||||
# if False, return None
|
||||
if rv is not None:
|
||||
return rv if rv else None
|
||||
|
||||
rv = app.config['SERVER_NAME']
|
||||
rv = app.config["SERVER_NAME"]
|
||||
|
||||
# server name not set, cache False to return none next time
|
||||
if not rv:
|
||||
app.config['SESSION_COOKIE_DOMAIN'] = False
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = False
|
||||
return None
|
||||
|
||||
# chop off the port which is usually not supported by browsers
|
||||
# remove any leading '.' since we'll add that later
|
||||
rv = rv.rsplit(':', 1)[0].lstrip('.')
|
||||
rv = rv.rsplit(":", 1)[0].lstrip(".")
|
||||
|
||||
if '.' not in rv:
|
||||
if "." not in rv:
|
||||
# Chrome doesn't allow names without a '.'
|
||||
# this should only come up with localhost
|
||||
# hack around this by not setting the name, and show a warning
|
||||
warnings.warn(
|
||||
'"{rv}" is not a valid cookie domain, it must contain a ".".'
|
||||
' Add an entry to your hosts file, for example'
|
||||
" Add an entry to your hosts file, for example"
|
||||
' "{rv}.localdomain", and use that instead.'.format(rv=rv)
|
||||
)
|
||||
app.config['SESSION_COOKIE_DOMAIN'] = False
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = False
|
||||
return None
|
||||
|
||||
ip = is_ip(rv)
|
||||
|
||||
if ip:
|
||||
warnings.warn(
|
||||
'The session cookie domain is an IP address. This may not work'
|
||||
' as intended in some browsers. Add an entry to your hosts'
|
||||
"The session cookie domain is an IP address. This may not work"
|
||||
" as intended in some browsers. Add an entry to your hosts"
|
||||
' file, for example "localhost.localdomain", and use that'
|
||||
' instead.'
|
||||
" instead."
|
||||
)
|
||||
|
||||
# if this is not an ip and app is mounted at the root, allow subdomain
|
||||
# matching by adding a '.' prefix
|
||||
if self.get_cookie_path(app) == '/' and not ip:
|
||||
rv = '.' + rv
|
||||
if self.get_cookie_path(app) == "/" and not ip:
|
||||
rv = "." + rv
|
||||
|
||||
app.config['SESSION_COOKIE_DOMAIN'] = rv
|
||||
app.config["SESSION_COOKIE_DOMAIN"] = rv
|
||||
return rv
|
||||
|
||||
def get_cookie_path(self, app):
|
||||
|
|
@ -234,28 +236,27 @@ class SessionInterface(object):
|
|||
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
|
||||
uses ``/`` if it's ``None``.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_PATH'] \
|
||||
or app.config['APPLICATION_ROOT']
|
||||
return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]
|
||||
|
||||
def get_cookie_httponly(self, app):
|
||||
"""Returns True if the session cookie should be httponly. This
|
||||
currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
|
||||
config var.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_HTTPONLY']
|
||||
return app.config["SESSION_COOKIE_HTTPONLY"]
|
||||
|
||||
def get_cookie_secure(self, app):
|
||||
"""Returns True if the cookie should be secure. This currently
|
||||
just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_SECURE']
|
||||
return app.config["SESSION_COOKIE_SECURE"]
|
||||
|
||||
def get_cookie_samesite(self, app):
|
||||
"""Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
|
||||
``SameSite`` attribute. This currently just returns the value of
|
||||
the :data:`SESSION_COOKIE_SAMESITE` setting.
|
||||
"""
|
||||
return app.config['SESSION_COOKIE_SAMESITE']
|
||||
return app.config["SESSION_COOKIE_SAMESITE"]
|
||||
|
||||
def get_expiration_time(self, app, session):
|
||||
"""A helper method that returns an expiration date for the session
|
||||
|
|
@ -279,7 +280,7 @@ class SessionInterface(object):
|
|||
"""
|
||||
|
||||
return session.modified or (
|
||||
session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST']
|
||||
session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
|
||||
)
|
||||
|
||||
def open_session(self, app, request):
|
||||
|
|
@ -306,14 +307,15 @@ class SecureCookieSessionInterface(SessionInterface):
|
|||
"""The default session interface that stores sessions in signed cookies
|
||||
through the :mod:`itsdangerous` module.
|
||||
"""
|
||||
|
||||
#: the salt that should be applied on top of the secret key for the
|
||||
#: signing of cookie based sessions.
|
||||
salt = 'cookie-session'
|
||||
salt = "cookie-session"
|
||||
#: the hash function to use for the signature. The default is sha1
|
||||
digest_method = staticmethod(hashlib.sha1)
|
||||
#: the name of the itsdangerous supported key derivation. The default
|
||||
#: is hmac.
|
||||
key_derivation = 'hmac'
|
||||
key_derivation = "hmac"
|
||||
#: A python serializer for the payload. The default is a compact
|
||||
#: JSON derived serializer with support for some extra Python types
|
||||
#: such as datetime objects or tuples.
|
||||
|
|
@ -324,12 +326,14 @@ class SecureCookieSessionInterface(SessionInterface):
|
|||
if not app.secret_key:
|
||||
return None
|
||||
signer_kwargs = dict(
|
||||
key_derivation=self.key_derivation,
|
||||
digest_method=self.digest_method
|
||||
key_derivation=self.key_derivation, digest_method=self.digest_method
|
||||
)
|
||||
return URLSafeTimedSerializer(
|
||||
app.secret_key,
|
||||
salt=self.salt,
|
||||
serializer=self.serializer,
|
||||
signer_kwargs=signer_kwargs,
|
||||
)
|
||||
return URLSafeTimedSerializer(app.secret_key, salt=self.salt,
|
||||
serializer=self.serializer,
|
||||
signer_kwargs=signer_kwargs)
|
||||
|
||||
def open_session(self, app, request):
|
||||
s = self.get_signing_serializer(app)
|
||||
|
|
@ -354,16 +358,14 @@ class SecureCookieSessionInterface(SessionInterface):
|
|||
if not session:
|
||||
if session.modified:
|
||||
response.delete_cookie(
|
||||
app.session_cookie_name,
|
||||
domain=domain,
|
||||
path=path
|
||||
app.session_cookie_name, domain=domain, path=path
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
# Add a "Vary: Cookie" header if the session was accessed at all.
|
||||
if session.accessed:
|
||||
response.vary.add('Cookie')
|
||||
response.vary.add("Cookie")
|
||||
|
||||
if not self.should_set_cookie(app, session):
|
||||
return
|
||||
|
|
@ -381,5 +383,5 @@ class SecureCookieSessionInterface(SessionInterface):
|
|||
domain=domain,
|
||||
path=path,
|
||||
secure=secure,
|
||||
samesite=samesite
|
||||
samesite=samesite,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@
|
|||
signals_available = False
|
||||
try:
|
||||
from blinker import Namespace
|
||||
|
||||
signals_available = True
|
||||
except ImportError:
|
||||
|
||||
class Namespace(object):
|
||||
def signal(self, name, doc=None):
|
||||
return _FakeSignal(name, doc)
|
||||
|
|
@ -29,15 +31,23 @@ except ImportError:
|
|||
def __init__(self, name, doc=None):
|
||||
self.name = name
|
||||
self.__doc__ = doc
|
||||
|
||||
def _fail(self, *args, **kwargs):
|
||||
raise RuntimeError('signalling support is unavailable '
|
||||
'because the blinker library is '
|
||||
'not installed.')
|
||||
raise RuntimeError(
|
||||
"signalling support is unavailable "
|
||||
"because the blinker library is "
|
||||
"not installed."
|
||||
)
|
||||
|
||||
send = lambda *a, **kw: None
|
||||
connect = disconnect = has_receivers_for = receivers_for = \
|
||||
temporarily_connected_to = connected_to = _fail
|
||||
connect = (
|
||||
disconnect
|
||||
) = (
|
||||
has_receivers_for
|
||||
) = receivers_for = temporarily_connected_to = connected_to = _fail
|
||||
del _fail
|
||||
|
||||
|
||||
# The namespace for code signals. If you are not Flask code, do
|
||||
# not put signals in here. Create your own namespace instead.
|
||||
_signals = Namespace()
|
||||
|
|
@ -45,13 +55,13 @@ _signals = Namespace()
|
|||
|
||||
# Core signals. For usage examples grep the source code or consult
|
||||
# the API documentation in docs/api.rst as well as docs/signals.rst
|
||||
template_rendered = _signals.signal('template-rendered')
|
||||
before_render_template = _signals.signal('before-render-template')
|
||||
request_started = _signals.signal('request-started')
|
||||
request_finished = _signals.signal('request-finished')
|
||||
request_tearing_down = _signals.signal('request-tearing-down')
|
||||
got_request_exception = _signals.signal('got-request-exception')
|
||||
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')
|
||||
appcontext_pushed = _signals.signal('appcontext-pushed')
|
||||
appcontext_popped = _signals.signal('appcontext-popped')
|
||||
message_flashed = _signals.signal('message-flashed')
|
||||
template_rendered = _signals.signal("template-rendered")
|
||||
before_render_template = _signals.signal("before-render-template")
|
||||
request_started = _signals.signal("request-started")
|
||||
request_finished = _signals.signal("request-finished")
|
||||
request_tearing_down = _signals.signal("request-tearing-down")
|
||||
got_request_exception = _signals.signal("got-request-exception")
|
||||
appcontext_tearing_down = _signals.signal("appcontext-tearing-down")
|
||||
appcontext_pushed = _signals.signal("appcontext-pushed")
|
||||
appcontext_popped = _signals.signal("appcontext-popped")
|
||||
message_flashed = _signals.signal("message-flashed")
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@
|
|||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
from jinja2 import BaseLoader, Environment as BaseEnvironment, \
|
||||
TemplateNotFound
|
||||
from jinja2 import BaseLoader, Environment as BaseEnvironment, TemplateNotFound
|
||||
|
||||
from .globals import _request_ctx_stack, _app_ctx_stack
|
||||
from .signals import template_rendered, before_render_template
|
||||
|
|
@ -24,10 +23,10 @@ def _default_template_ctx_processor():
|
|||
appctx = _app_ctx_stack.top
|
||||
rv = {}
|
||||
if appctx is not None:
|
||||
rv['g'] = appctx.g
|
||||
rv["g"] = appctx.g
|
||||
if reqctx is not None:
|
||||
rv['request'] = reqctx.request
|
||||
rv['session'] = reqctx.session
|
||||
rv["request"] = reqctx.request
|
||||
rv["session"] = reqctx.session
|
||||
return rv
|
||||
|
||||
|
||||
|
|
@ -38,8 +37,8 @@ class Environment(BaseEnvironment):
|
|||
"""
|
||||
|
||||
def __init__(self, app, **options):
|
||||
if 'loader' not in options:
|
||||
options['loader'] = app.create_global_jinja_loader()
|
||||
if "loader" not in options:
|
||||
options["loader"] = app.create_global_jinja_loader()
|
||||
BaseEnvironment.__init__(self, **options)
|
||||
self.app = app
|
||||
|
||||
|
|
@ -53,7 +52,7 @@ class DispatchingJinjaLoader(BaseLoader):
|
|||
self.app = app
|
||||
|
||||
def get_source(self, environment, template):
|
||||
if self.app.config['EXPLAIN_TEMPLATE_LOADING']:
|
||||
if self.app.config["EXPLAIN_TEMPLATE_LOADING"]:
|
||||
return self._get_source_explained(environment, template)
|
||||
return self._get_source_fast(environment, template)
|
||||
|
||||
|
|
@ -71,6 +70,7 @@ class DispatchingJinjaLoader(BaseLoader):
|
|||
attempts.append((loader, srcobj, rv))
|
||||
|
||||
from .debughelpers import explain_template_loading_attempts
|
||||
|
||||
explain_template_loading_attempts(self.app, template, attempts)
|
||||
|
||||
if trv is not None:
|
||||
|
|
@ -131,8 +131,11 @@ def render_template(template_name_or_list, **context):
|
|||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(ctx.app.jinja_env.get_or_select_template(template_name_or_list),
|
||||
context, ctx.app)
|
||||
return _render(
|
||||
ctx.app.jinja_env.get_or_select_template(template_name_or_list),
|
||||
context,
|
||||
ctx.app,
|
||||
)
|
||||
|
||||
|
||||
def render_template_string(source, **context):
|
||||
|
|
@ -146,5 +149,4 @@ def render_template_string(source, **context):
|
|||
"""
|
||||
ctx = _app_ctx_stack.top
|
||||
ctx.app.update_template_context(context)
|
||||
return _render(ctx.app.jinja_env.from_string(source),
|
||||
context, ctx.app)
|
||||
return _render(ctx.app.jinja_env.from_string(source), context, ctx.app)
|
||||
|
|
|
|||
|
|
@ -22,8 +22,7 @@ from werkzeug.urls import url_parse
|
|||
|
||||
|
||||
def make_test_environ_builder(
|
||||
app, path='/', base_url=None, subdomain=None, url_scheme=None,
|
||||
*args, **kwargs
|
||||
app, path="/", base_url=None, subdomain=None, url_scheme=None, *args, **kwargs
|
||||
):
|
||||
"""Create a :class:`~werkzeug.test.EnvironBuilder`, taking some
|
||||
defaults from the application.
|
||||
|
|
@ -46,44 +45,41 @@ def make_test_environ_builder(
|
|||
:class:`~werkzeug.test.EnvironBuilder`.
|
||||
"""
|
||||
|
||||
assert (
|
||||
not (base_url or subdomain or url_scheme)
|
||||
or (base_url is not None) != bool(subdomain or url_scheme)
|
||||
assert not (base_url or subdomain or url_scheme) or (base_url is not None) != bool(
|
||||
subdomain or url_scheme
|
||||
), 'Cannot pass "subdomain" or "url_scheme" with "base_url".'
|
||||
|
||||
if base_url is None:
|
||||
http_host = app.config.get('SERVER_NAME') or 'localhost'
|
||||
app_root = app.config['APPLICATION_ROOT']
|
||||
http_host = app.config.get("SERVER_NAME") or "localhost"
|
||||
app_root = app.config["APPLICATION_ROOT"]
|
||||
|
||||
if subdomain:
|
||||
http_host = '{0}.{1}'.format(subdomain, http_host)
|
||||
http_host = "{0}.{1}".format(subdomain, http_host)
|
||||
|
||||
if url_scheme is None:
|
||||
url_scheme = app.config['PREFERRED_URL_SCHEME']
|
||||
url_scheme = app.config["PREFERRED_URL_SCHEME"]
|
||||
|
||||
url = url_parse(path)
|
||||
base_url = '{scheme}://{netloc}/{path}'.format(
|
||||
base_url = "{scheme}://{netloc}/{path}".format(
|
||||
scheme=url.scheme or url_scheme,
|
||||
netloc=url.netloc or http_host,
|
||||
path=app_root.lstrip('/')
|
||||
path=app_root.lstrip("/"),
|
||||
)
|
||||
path = url.path
|
||||
|
||||
if url.query:
|
||||
sep = b'?' if isinstance(url.query, bytes) else '?'
|
||||
sep = b"?" if isinstance(url.query, bytes) else "?"
|
||||
path += sep + url.query
|
||||
|
||||
if 'json' in kwargs:
|
||||
assert 'data' not in kwargs, (
|
||||
"Client cannot provide both 'json' and 'data'."
|
||||
)
|
||||
if "json" in kwargs:
|
||||
assert "data" not in kwargs, "Client cannot provide both 'json' and 'data'."
|
||||
|
||||
# push a context so flask.json can use app's json attributes
|
||||
with app.app_context():
|
||||
kwargs['data'] = json_dumps(kwargs.pop('json'))
|
||||
kwargs["data"] = json_dumps(kwargs.pop("json"))
|
||||
|
||||
if 'content_type' not in kwargs:
|
||||
kwargs['content_type'] = 'application/json'
|
||||
if "content_type" not in kwargs:
|
||||
kwargs["content_type"] = "application/json"
|
||||
|
||||
return EnvironBuilder(path, base_url, *args, **kwargs)
|
||||
|
||||
|
|
@ -109,7 +105,7 @@ class FlaskClient(Client):
|
|||
super(FlaskClient, self).__init__(*args, **kwargs)
|
||||
self.environ_base = {
|
||||
"REMOTE_ADDR": "127.0.0.1",
|
||||
"HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__
|
||||
"HTTP_USER_AGENT": "werkzeug/" + werkzeug.__version__,
|
||||
}
|
||||
|
||||
@contextmanager
|
||||
|
|
@ -131,18 +127,20 @@ class FlaskClient(Client):
|
|||
passed through.
|
||||
"""
|
||||
if self.cookie_jar is None:
|
||||
raise RuntimeError('Session transactions only make sense '
|
||||
'with cookies enabled.')
|
||||
raise RuntimeError(
|
||||
"Session transactions only make sense " "with cookies enabled."
|
||||
)
|
||||
app = self.application
|
||||
environ_overrides = kwargs.setdefault('environ_overrides', {})
|
||||
environ_overrides = kwargs.setdefault("environ_overrides", {})
|
||||
self.cookie_jar.inject_wsgi(environ_overrides)
|
||||
outer_reqctx = _request_ctx_stack.top
|
||||
with app.test_request_context(*args, **kwargs) as c:
|
||||
session_interface = app.session_interface
|
||||
sess = session_interface.open_session(app, c.request)
|
||||
if sess is None:
|
||||
raise RuntimeError('Session backend did not open a session. '
|
||||
'Check the configuration')
|
||||
raise RuntimeError(
|
||||
"Session backend did not open a session. " "Check the configuration"
|
||||
)
|
||||
|
||||
# Since we have to open a new request context for the session
|
||||
# handling we want to make sure that we hide out own context
|
||||
|
|
@ -164,12 +162,13 @@ class FlaskClient(Client):
|
|||
self.cookie_jar.extract_wsgi(c.request.environ, headers)
|
||||
|
||||
def open(self, *args, **kwargs):
|
||||
as_tuple = kwargs.pop('as_tuple', False)
|
||||
buffered = kwargs.pop('buffered', False)
|
||||
follow_redirects = kwargs.pop('follow_redirects', False)
|
||||
as_tuple = kwargs.pop("as_tuple", False)
|
||||
buffered = kwargs.pop("buffered", False)
|
||||
follow_redirects = kwargs.pop("follow_redirects", False)
|
||||
|
||||
if (
|
||||
not kwargs and len(args) == 1
|
||||
not kwargs
|
||||
and len(args) == 1
|
||||
and isinstance(args[0], (EnvironBuilder, dict))
|
||||
):
|
||||
environ = self.environ_base.copy()
|
||||
|
|
@ -179,14 +178,13 @@ class FlaskClient(Client):
|
|||
else:
|
||||
environ.update(args[0])
|
||||
|
||||
environ['flask._preserve_context'] = self.preserve_context
|
||||
environ["flask._preserve_context"] = self.preserve_context
|
||||
else:
|
||||
kwargs.setdefault('environ_overrides', {}) \
|
||||
['flask._preserve_context'] = self.preserve_context
|
||||
kwargs.setdefault('environ_base', self.environ_base)
|
||||
builder = make_test_environ_builder(
|
||||
self.application, *args, **kwargs
|
||||
)
|
||||
kwargs.setdefault("environ_overrides", {})[
|
||||
"flask._preserve_context"
|
||||
] = self.preserve_context
|
||||
kwargs.setdefault("environ_base", self.environ_base)
|
||||
builder = make_test_environ_builder(self.application, *args, **kwargs)
|
||||
|
||||
try:
|
||||
environ = builder.get_environ()
|
||||
|
|
@ -194,15 +192,16 @@ class FlaskClient(Client):
|
|||
builder.close()
|
||||
|
||||
return Client.open(
|
||||
self, environ,
|
||||
self,
|
||||
environ,
|
||||
as_tuple=as_tuple,
|
||||
buffered=buffered,
|
||||
follow_redirects=follow_redirects
|
||||
follow_redirects=follow_redirects,
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
if self.preserve_context:
|
||||
raise RuntimeError('Cannot nest client invocations')
|
||||
raise RuntimeError("Cannot nest client invocations")
|
||||
self.preserve_context = True
|
||||
return self
|
||||
|
||||
|
|
@ -222,6 +221,7 @@ class FlaskCliRunner(CliRunner):
|
|||
CLI commands. Typically created using
|
||||
:meth:`~flask.Flask.test_cli_runner`. See :ref:`testing-cli`.
|
||||
"""
|
||||
|
||||
def __init__(self, app, **kwargs):
|
||||
self.app = app
|
||||
super(FlaskCliRunner, self).__init__(**kwargs)
|
||||
|
|
@ -244,7 +244,7 @@ class FlaskCliRunner(CliRunner):
|
|||
if cli is None:
|
||||
cli = self.app.cli
|
||||
|
||||
if 'obj' not in kwargs:
|
||||
kwargs['obj'] = ScriptInfo(create_app=lambda: self.app)
|
||||
if "obj" not in kwargs:
|
||||
kwargs["obj"] = ScriptInfo(create_app=lambda: self.app)
|
||||
|
||||
return super(FlaskCliRunner, self).invoke(cli, args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ from .globals import request
|
|||
from ._compat import with_metaclass
|
||||
|
||||
|
||||
http_method_funcs = frozenset(['get', 'post', 'head', 'options',
|
||||
'delete', 'put', 'trace', 'patch'])
|
||||
http_method_funcs = frozenset(
|
||||
["get", "post", "head", "options", "delete", "put", "trace", "patch"]
|
||||
)
|
||||
|
||||
|
||||
class View(object):
|
||||
|
|
@ -83,6 +84,7 @@ class View(object):
|
|||
The arguments passed to :meth:`as_view` are forwarded to the
|
||||
constructor of the class.
|
||||
"""
|
||||
|
||||
def view(*args, **kwargs):
|
||||
self = view.view_class(*class_args, **class_kwargs)
|
||||
return self.dispatch_request(*args, **kwargs)
|
||||
|
|
@ -115,7 +117,7 @@ class MethodViewType(type):
|
|||
def __init__(cls, name, bases, d):
|
||||
super(MethodViewType, cls).__init__(name, bases, d)
|
||||
|
||||
if 'methods' not in d:
|
||||
if "methods" not in d:
|
||||
methods = set()
|
||||
|
||||
for key in http_method_funcs:
|
||||
|
|
@ -151,8 +153,8 @@ class MethodView(with_metaclass(MethodViewType, View)):
|
|||
|
||||
# If the request method is HEAD and we don't have a handler for it
|
||||
# retry with GET.
|
||||
if meth is None and request.method == 'HEAD':
|
||||
meth = getattr(self, 'get', None)
|
||||
if meth is None and request.method == "HEAD":
|
||||
meth = getattr(self, "get", None)
|
||||
|
||||
assert meth is not None, 'Unimplemented method %r' % request.method
|
||||
assert meth is not None, "Unimplemented method %r" % request.method
|
||||
return meth(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -34,8 +34,9 @@ class JSONMixin(object):
|
|||
"""
|
||||
mt = self.mimetype
|
||||
return (
|
||||
mt == 'application/json'
|
||||
or (mt.startswith('application/')) and mt.endswith('+json')
|
||||
mt == "application/json"
|
||||
or (mt.startswith("application/"))
|
||||
and mt.endswith("+json")
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
@ -103,7 +104,7 @@ class JSONMixin(object):
|
|||
.. versionadded:: 0.8
|
||||
"""
|
||||
if current_app is not None and current_app.debug:
|
||||
raise BadRequest('Failed to decode JSON object: {0}'.format(e))
|
||||
raise BadRequest("Failed to decode JSON object: {0}".format(e))
|
||||
|
||||
raise BadRequest()
|
||||
|
||||
|
|
@ -146,7 +147,7 @@ class Request(RequestBase, JSONMixin):
|
|||
def max_content_length(self):
|
||||
"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
|
||||
if current_app:
|
||||
return current_app.config['MAX_CONTENT_LENGTH']
|
||||
return current_app.config["MAX_CONTENT_LENGTH"]
|
||||
|
||||
@property
|
||||
def endpoint(self):
|
||||
|
|
@ -161,8 +162,8 @@ class Request(RequestBase, JSONMixin):
|
|||
@property
|
||||
def blueprint(self):
|
||||
"""The name of the current blueprint"""
|
||||
if self.url_rule and '.' in self.url_rule.endpoint:
|
||||
return self.url_rule.endpoint.rsplit('.', 1)[0]
|
||||
if self.url_rule and "." in self.url_rule.endpoint:
|
||||
return self.url_rule.endpoint.rsplit(".", 1)[0]
|
||||
|
||||
def _load_form_data(self):
|
||||
RequestBase._load_form_data(self)
|
||||
|
|
@ -172,10 +173,11 @@ class Request(RequestBase, JSONMixin):
|
|||
if (
|
||||
current_app
|
||||
and current_app.debug
|
||||
and self.mimetype != 'multipart/form-data'
|
||||
and self.mimetype != "multipart/form-data"
|
||||
and not self.files
|
||||
):
|
||||
from .debughelpers import attach_enctype_error_multidict
|
||||
|
||||
attach_enctype_error_multidict(self)
|
||||
|
||||
|
||||
|
|
@ -197,7 +199,7 @@ class Response(ResponseBase, JSONMixin):
|
|||
Added :attr:`max_cookie_size`.
|
||||
"""
|
||||
|
||||
default_mimetype = 'text/html'
|
||||
default_mimetype = "text/html"
|
||||
|
||||
def _get_data_for_json(self, cache):
|
||||
return self.get_data()
|
||||
|
|
@ -210,7 +212,7 @@ class Response(ResponseBase, JSONMixin):
|
|||
Werkzeug's docs.
|
||||
"""
|
||||
if current_app:
|
||||
return current_app.config['MAX_COOKIE_SIZE']
|
||||
return current_app.config["MAX_COOKIE_SIZE"]
|
||||
|
||||
# return Werkzeug's default when not in an app context
|
||||
return super(Response, self).max_cookie_size
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue