|
|
|
@ -10,31 +10,32 @@
|
|
|
|
|
""" |
|
|
|
|
import os |
|
|
|
|
import sys |
|
|
|
|
from threading import Lock |
|
|
|
|
import warnings |
|
|
|
|
from datetime import timedelta |
|
|
|
|
from itertools import chain |
|
|
|
|
from functools import update_wrapper |
|
|
|
|
from collections import deque |
|
|
|
|
|
|
|
|
|
from werkzeug.datastructures import ImmutableDict |
|
|
|
|
from werkzeug.routing import Map, Rule, RequestRedirect, BuildError |
|
|
|
|
from werkzeug.exceptions import HTTPException, InternalServerError, \ |
|
|
|
|
MethodNotAllowed, BadRequest, default_exceptions |
|
|
|
|
from itertools import chain |
|
|
|
|
from threading import Lock |
|
|
|
|
|
|
|
|
|
from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ |
|
|
|
|
locked_cached_property, _endpoint_from_view_func, find_package, \ |
|
|
|
|
get_debug_flag |
|
|
|
|
from . import json, cli |
|
|
|
|
from .wrappers import Request, Response |
|
|
|
|
from .config import ConfigAttribute, Config |
|
|
|
|
from .ctx import RequestContext, AppContext, _AppCtxGlobals |
|
|
|
|
from .globals import _request_ctx_stack, request, session, g |
|
|
|
|
from werkzeug.datastructures import ImmutableDict, Headers |
|
|
|
|
from werkzeug.exceptions import BadRequest, HTTPException, \ |
|
|
|
|
InternalServerError, MethodNotAllowed, default_exceptions, \ |
|
|
|
|
BadRequestKeyError |
|
|
|
|
from werkzeug.routing import BuildError, Map, RequestRedirect, Rule |
|
|
|
|
|
|
|
|
|
from . import cli, json |
|
|
|
|
from ._compat import integer_types, reraise, string_types, text_type |
|
|
|
|
from .config import Config, ConfigAttribute |
|
|
|
|
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_debug_flag, \ |
|
|
|
|
get_flashed_messages, locked_cached_property, url_for |
|
|
|
|
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 request_started, request_finished, got_request_exception, \ |
|
|
|
|
request_tearing_down, appcontext_tearing_down |
|
|
|
|
from ._compat import reraise, string_types, text_type, integer_types |
|
|
|
|
_default_template_ctx_processor |
|
|
|
|
from .wrappers import Request, Response |
|
|
|
|
|
|
|
|
|
# a lock used for logger initialization |
|
|
|
|
_logger_lock = Lock() |
|
|
|
@ -124,6 +125,9 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
.. versionadded:: 0.11 |
|
|
|
|
The `root_path` parameter was added. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.13 |
|
|
|
|
The `host_matching` and `static_host` parameters were added. |
|
|
|
|
|
|
|
|
|
:param import_name: the name of the application package |
|
|
|
|
:param static_url_path: can be used to specify a different path for the |
|
|
|
|
static files on the web. Defaults to the name |
|
|
|
@ -131,6 +135,11 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
:param static_folder: the folder with static files that should be served |
|
|
|
|
at `static_url_path`. Defaults to the ``'static'`` |
|
|
|
|
folder in the root path of the application. |
|
|
|
|
:param host_matching: sets the app's ``url_map.host_matching`` to the given |
|
|
|
|
given value. Defaults to False. |
|
|
|
|
:param static_host: the host to use when adding the static route. Defaults |
|
|
|
|
to None. Required when using ``host_matching=True`` |
|
|
|
|
with a ``static_folder`` configured. |
|
|
|
|
:param template_folder: the folder that contains the templates that should |
|
|
|
|
be used by the application. Defaults to |
|
|
|
|
``'templates'`` folder in the root path of the |
|
|
|
@ -213,7 +222,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
#: The testing flag. Set this to ``True`` to enable the test mode of |
|
|
|
|
#: Flask extensions (and in the future probably also Flask itself). |
|
|
|
|
#: For example this might activate unittest helpers that have an |
|
|
|
|
#: For example this might activate test helpers that have an |
|
|
|
|
#: additional runtime cost which should not be enabled by default. |
|
|
|
|
#: |
|
|
|
|
#: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the |
|
|
|
@ -300,7 +309,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
'LOGGER_NAME': None, |
|
|
|
|
'LOGGER_HANDLER_POLICY': 'always', |
|
|
|
|
'SERVER_NAME': None, |
|
|
|
|
'APPLICATION_ROOT': None, |
|
|
|
|
'APPLICATION_ROOT': '/', |
|
|
|
|
'SESSION_COOKIE_NAME': 'session', |
|
|
|
|
'SESSION_COOKIE_DOMAIN': None, |
|
|
|
|
'SESSION_COOKIE_PATH': None, |
|
|
|
@ -309,13 +318,13 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
'SESSION_REFRESH_EACH_REQUEST': True, |
|
|
|
|
'MAX_CONTENT_LENGTH': None, |
|
|
|
|
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), |
|
|
|
|
'TRAP_BAD_REQUEST_ERRORS': False, |
|
|
|
|
'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': True, |
|
|
|
|
'JSONIFY_PRETTYPRINT_REGULAR': False, |
|
|
|
|
'JSONIFY_MIMETYPE': 'application/json', |
|
|
|
|
'TEMPLATES_AUTO_RELOAD': None, |
|
|
|
|
}) |
|
|
|
@ -338,7 +347,8 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
session_interface = SecureCookieSessionInterface() |
|
|
|
|
|
|
|
|
|
def __init__(self, import_name, static_path=None, static_url_path=None, |
|
|
|
|
static_folder='static', template_folder='templates', |
|
|
|
|
static_folder='static', static_host=None, |
|
|
|
|
host_matching=False, template_folder='templates', |
|
|
|
|
instance_path=None, instance_relative_config=False, |
|
|
|
|
root_path=None): |
|
|
|
|
_PackageBoundObject.__init__(self, import_name, |
|
|
|
@ -392,7 +402,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
#: is the class for the instance check and the second the error handler |
|
|
|
|
#: function. |
|
|
|
|
#: |
|
|
|
|
#: To register a error handler, use the :meth:`errorhandler` |
|
|
|
|
#: To register an error handler, use the :meth:`errorhandler` |
|
|
|
|
#: decorator. |
|
|
|
|
self.error_handler_spec = {None: self._error_handlers} |
|
|
|
|
|
|
|
|
@ -405,17 +415,16 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
#: .. versionadded:: 0.9 |
|
|
|
|
self.url_build_error_handlers = [] |
|
|
|
|
|
|
|
|
|
#: A dictionary with lists of functions that should be called at the |
|
|
|
|
#: beginning of the request. The key of the dictionary is the name of |
|
|
|
|
#: the blueprint this function is active for, ``None`` for all requests. |
|
|
|
|
#: This can for example be used to open database connections or |
|
|
|
|
#: getting hold of the currently logged in user. To register a |
|
|
|
|
#: function here, use the :meth:`before_request` decorator. |
|
|
|
|
#: A dictionary with lists of functions that will be called at the |
|
|
|
|
#: beginning of each request. The key of the dictionary is the name of |
|
|
|
|
#: the blueprint this function is active for, or ``None`` for all |
|
|
|
|
#: requests. To register a function, use the :meth:`before_request` |
|
|
|
|
#: decorator. |
|
|
|
|
self.before_request_funcs = {} |
|
|
|
|
|
|
|
|
|
#: A lists of functions that should be called at the beginning of the |
|
|
|
|
#: first request to this instance. To register a function here, use |
|
|
|
|
#: the :meth:`before_first_request` decorator. |
|
|
|
|
#: A list of functions that will be called at the beginning of the |
|
|
|
|
#: first request to this instance. To register a function, use the |
|
|
|
|
#: :meth:`before_first_request` decorator. |
|
|
|
|
#: |
|
|
|
|
#: .. versionadded:: 0.8 |
|
|
|
|
self.before_first_request_funcs = [] |
|
|
|
@ -447,12 +456,11 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
#: .. versionadded:: 0.9 |
|
|
|
|
self.teardown_appcontext_funcs = [] |
|
|
|
|
|
|
|
|
|
#: A dictionary with lists of functions that can be used as URL |
|
|
|
|
#: value processor functions. Whenever a URL is built these functions |
|
|
|
|
#: are called to modify the dictionary of values in place. The key |
|
|
|
|
#: ``None`` here is used for application wide |
|
|
|
|
#: callbacks, otherwise the key is the name of the blueprint. |
|
|
|
|
#: Each of these functions has the chance to modify the dictionary |
|
|
|
|
#: A dictionary with lists of functions that are called before the |
|
|
|
|
#: :attr:`before_request_funcs` functions. The key of the dictionary is |
|
|
|
|
#: the name of the blueprint this function is active for, or ``None`` |
|
|
|
|
#: for all requests. To register a function, use |
|
|
|
|
#: :meth:`url_value_preprocessor`. |
|
|
|
|
#: |
|
|
|
|
#: .. versionadded:: 0.7 |
|
|
|
|
self.url_value_preprocessors = {} |
|
|
|
@ -519,26 +527,29 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
#: def to_python(self, value): |
|
|
|
|
#: return value.split(',') |
|
|
|
|
#: def to_url(self, values): |
|
|
|
|
#: return ','.join(BaseConverter.to_url(value) |
|
|
|
|
#: return ','.join(super(ListConverter, self).to_url(value) |
|
|
|
|
#: for value in values) |
|
|
|
|
#: |
|
|
|
|
#: app = Flask(__name__) |
|
|
|
|
#: app.url_map.converters['list'] = ListConverter |
|
|
|
|
self.url_map = Map() |
|
|
|
|
|
|
|
|
|
self.url_map.host_matching = host_matching |
|
|
|
|
|
|
|
|
|
# tracks internally if the application already handled at least one |
|
|
|
|
# request. |
|
|
|
|
self._got_first_request = False |
|
|
|
|
self._before_request_lock = Lock() |
|
|
|
|
|
|
|
|
|
# register the static folder for the application. Do that even |
|
|
|
|
# if the folder does not exist. First of all it might be created |
|
|
|
|
# while the server is running (usually happens during development) |
|
|
|
|
# but also because google appengine stores static files somewhere |
|
|
|
|
# else when mapped with the .yml file. |
|
|
|
|
# Add a static route using the provided static_url_path, static_host, |
|
|
|
|
# and static_folder if there is a configured static_folder. |
|
|
|
|
# Note we do this without checking if static_folder exists. |
|
|
|
|
# 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' |
|
|
|
|
self.add_url_rule(self.static_url_path + '/<path:filename>', |
|
|
|
|
endpoint='static', |
|
|
|
|
endpoint='static', host=static_host, |
|
|
|
|
view_func=self.send_static_file) |
|
|
|
|
|
|
|
|
|
#: The click command line context for this application. Commands |
|
|
|
@ -814,7 +825,8 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
:param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to |
|
|
|
|
have the server available externally as well. Defaults to |
|
|
|
|
``'127.0.0.1'``. |
|
|
|
|
``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config |
|
|
|
|
variable if present. |
|
|
|
|
:param port: the port of the webserver. Defaults to ``5000`` or the |
|
|
|
|
port defined in the ``SERVER_NAME`` config variable if |
|
|
|
|
present. |
|
|
|
@ -825,25 +837,31 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
:func:`werkzeug.serving.run_simple` for more |
|
|
|
|
information. |
|
|
|
|
""" |
|
|
|
|
# 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_SERVER') == '1': |
|
|
|
|
from .debughelpers import explain_ignored_app_run |
|
|
|
|
explain_ignored_app_run() |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
from werkzeug.serving import run_simple |
|
|
|
|
if host is None: |
|
|
|
|
host = '127.0.0.1' |
|
|
|
|
if port is None: |
|
|
|
|
server_name = self.config['SERVER_NAME'] |
|
|
|
|
if server_name and ':' in server_name: |
|
|
|
|
port = int(server_name.rsplit(':', 1)[1]) |
|
|
|
|
else: |
|
|
|
|
port = 5000 |
|
|
|
|
_host = '127.0.0.1' |
|
|
|
|
_port = 5000 |
|
|
|
|
server_name = self.config.get("SERVER_NAME") |
|
|
|
|
sn_host, sn_port = None, None |
|
|
|
|
if server_name: |
|
|
|
|
sn_host, _, sn_port = server_name.partition(':') |
|
|
|
|
host = host or sn_host or _host |
|
|
|
|
port = int(port or sn_port or _port) |
|
|
|
|
if debug is not None: |
|
|
|
|
self.debug = bool(debug) |
|
|
|
|
options.setdefault('use_reloader', self.debug) |
|
|
|
|
options.setdefault('use_debugger', self.debug) |
|
|
|
|
options.setdefault('passthrough_errors', True) |
|
|
|
|
try: |
|
|
|
|
run_simple(host, port, self, **options) |
|
|
|
|
finally: |
|
|
|
|
# reset the first request information if the development server |
|
|
|
|
# resetted normally. This makes it possible to restart the server |
|
|
|
|
# reset normally. This makes it possible to restart the server |
|
|
|
|
# without reloader and that stuff from an interactive shell. |
|
|
|
|
self._got_first_request = False |
|
|
|
|
|
|
|
|
@ -877,9 +895,9 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
from flask.testing import FlaskClient |
|
|
|
|
|
|
|
|
|
class CustomClient(FlaskClient): |
|
|
|
|
def __init__(self, authentication=None, *args, **kwargs): |
|
|
|
|
FlaskClient.__init__(*args, **kwargs) |
|
|
|
|
self._authentication = authentication |
|
|
|
|
def __init__(self, *args, **kwargs): |
|
|
|
|
self._authentication = kwargs.pop("authentication") |
|
|
|
|
super(CustomClient,self).__init__( *args, **kwargs) |
|
|
|
|
|
|
|
|
|
app.test_client_class = CustomClient |
|
|
|
|
client = app.test_client(authentication='Basic ....') |
|
|
|
@ -909,8 +927,17 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
:attr:`secret_key` is set. Instead of overriding this method |
|
|
|
|
we recommend replacing the :class:`session_interface`. |
|
|
|
|
|
|
|
|
|
.. deprecated: 1.0 |
|
|
|
|
Will be removed in 1.1. Use ``session_interface.open_session`` |
|
|
|
|
instead. |
|
|
|
|
|
|
|
|
|
: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.' |
|
|
|
|
)) |
|
|
|
|
return self.session_interface.open_session(self, request) |
|
|
|
|
|
|
|
|
|
def save_session(self, session, response): |
|
|
|
@ -918,19 +945,37 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
implementation, check :meth:`open_session`. Instead of overriding this |
|
|
|
|
method we recommend replacing the :class:`session_interface`. |
|
|
|
|
|
|
|
|
|
.. deprecated: 1.0 |
|
|
|
|
Will be removed in 1.1. Use ``session_interface.save_session`` |
|
|
|
|
instead. |
|
|
|
|
|
|
|
|
|
:param session: the session to be saved (a |
|
|
|
|
:class:`~werkzeug.contrib.securecookie.SecureCookie` |
|
|
|
|
object) |
|
|
|
|
: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.' |
|
|
|
|
)) |
|
|
|
|
return self.session_interface.save_session(self, session, response) |
|
|
|
|
|
|
|
|
|
def make_null_session(self): |
|
|
|
|
"""Creates a new instance of a missing session. Instead of overriding |
|
|
|
|
this method we recommend replacing the :class:`session_interface`. |
|
|
|
|
|
|
|
|
|
.. deprecated: 1.0 |
|
|
|
|
Will be removed in 1.1. Use ``session_interface.make_null_session`` |
|
|
|
|
instead. |
|
|
|
|
|
|
|
|
|
.. 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.' |
|
|
|
|
)) |
|
|
|
|
return self.session_interface.make_null_session(self) |
|
|
|
|
|
|
|
|
|
@setupmethod |
|
|
|
@ -960,7 +1005,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
return iter(self._blueprint_order) |
|
|
|
|
|
|
|
|
|
@setupmethod |
|
|
|
|
def add_url_rule(self, rule, endpoint=None, view_func=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. |
|
|
|
@ -1000,6 +1045,10 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
endpoint |
|
|
|
|
:param view_func: the function to call when serving a request to the |
|
|
|
|
provided endpoint |
|
|
|
|
:param provide_automatic_options: controls whether the ``OPTIONS`` |
|
|
|
|
method should be added automatically. This can also be controlled |
|
|
|
|
by setting the ``view_func.provide_automatic_options = False`` |
|
|
|
|
before adding the rule. |
|
|
|
|
:param options: the options to be forwarded to the underlying |
|
|
|
|
:class:`~werkzeug.routing.Rule` object. A change |
|
|
|
|
to Werkzeug is handling of method options. methods |
|
|
|
@ -1029,8 +1078,9 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
# starting with Flask 0.8 the view_func object can disable and |
|
|
|
|
# force-enable the automatic options handling. |
|
|
|
|
provide_automatic_options = getattr(view_func, |
|
|
|
|
'provide_automatic_options', None) |
|
|
|
|
if provide_automatic_options is None: |
|
|
|
|
provide_automatic_options = getattr(view_func, |
|
|
|
|
'provide_automatic_options', None) |
|
|
|
|
|
|
|
|
|
if provide_automatic_options is None: |
|
|
|
|
if 'OPTIONS' not in methods: |
|
|
|
@ -1116,7 +1166,9 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
@setupmethod |
|
|
|
|
def errorhandler(self, code_or_exception): |
|
|
|
|
"""A decorator that is used to register a function give a given |
|
|
|
|
"""Register a function to handle errors by code or exception class. |
|
|
|
|
|
|
|
|
|
A decorator that is used to register a function given an |
|
|
|
|
error code. Example:: |
|
|
|
|
|
|
|
|
|
@app.errorhandler(404) |
|
|
|
@ -1129,21 +1181,6 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
def special_exception_handler(error): |
|
|
|
|
return 'Database connection failed', 500 |
|
|
|
|
|
|
|
|
|
You can also register a function as error handler without using |
|
|
|
|
the :meth:`errorhandler` decorator. The following example is |
|
|
|
|
equivalent to the one above:: |
|
|
|
|
|
|
|
|
|
def page_not_found(error): |
|
|
|
|
return 'This page does not exist', 404 |
|
|
|
|
app.error_handler_spec[None][404] = page_not_found |
|
|
|
|
|
|
|
|
|
Setting error handlers via assignments to :attr:`error_handler_spec` |
|
|
|
|
however is discouraged as it requires fiddling with nested dictionaries |
|
|
|
|
and the special case for arbitrary exception types. |
|
|
|
|
|
|
|
|
|
The first ``None`` refers to the active blueprint. If the error |
|
|
|
|
handler should be application wide ``None`` shall be used. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.7 |
|
|
|
|
Use :meth:`register_error_handler` instead of modifying |
|
|
|
|
:attr:`error_handler_spec` directly, for application wide error |
|
|
|
@ -1154,13 +1191,15 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
that do not necessarily have to be a subclass of the |
|
|
|
|
:class:`~werkzeug.exceptions.HTTPException` class. |
|
|
|
|
|
|
|
|
|
:param code: the code as integer for the handler |
|
|
|
|
: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 |
|
|
|
|
def register_error_handler(self, code_or_exception, f): |
|
|
|
|
"""Alternative error attach function to the :meth:`errorhandler` |
|
|
|
|
decorator that is more straightforward to use for non decorator |
|
|
|
@ -1179,11 +1218,18 @@ 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) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
exc_class, code = self._get_exc_class_and_code(code_or_exception) |
|
|
|
|
try: |
|
|
|
|
exc_class, code = self._get_exc_class_and_code(code_or_exception) |
|
|
|
|
except KeyError: |
|
|
|
|
raise KeyError( |
|
|
|
|
"'{0}' is not a recognized HTTP error code. Use a subclass of" |
|
|
|
|
" HTTPException with that code instead.".format(code_or_exception) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) |
|
|
|
|
handlers[exc_class] = f |
|
|
|
@ -1289,10 +1335,12 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
def before_request(self, f): |
|
|
|
|
"""Registers a function to run before each request. |
|
|
|
|
|
|
|
|
|
The function will be called without any arguments. |
|
|
|
|
If the function returns a non-None value, it's handled as |
|
|
|
|
if it was the return value from the view and further |
|
|
|
|
request handling is stopped. |
|
|
|
|
For example, this can be used to open a database connection, or to load |
|
|
|
|
the logged in user from the session. |
|
|
|
|
|
|
|
|
|
The function will be called without any arguments. If it returns a |
|
|
|
|
non-None value, the value is handled as if it was the return value from |
|
|
|
|
the view, and further request handling is stopped. |
|
|
|
|
""" |
|
|
|
|
self.before_request_funcs.setdefault(None, []).append(f) |
|
|
|
|
return f |
|
|
|
@ -1348,7 +1396,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
will have to surround the execution of these code by try/except |
|
|
|
|
statements and log occurring errors. |
|
|
|
|
|
|
|
|
|
When a teardown function was called because of a exception it will |
|
|
|
|
When a teardown function was called because of an exception it will |
|
|
|
|
be passed an error object. |
|
|
|
|
|
|
|
|
|
The return values of teardown functions are ignored. |
|
|
|
@ -1411,9 +1459,17 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
@setupmethod |
|
|
|
|
def url_value_preprocessor(self, f): |
|
|
|
|
"""Registers a function as URL value preprocessor for all view |
|
|
|
|
functions of the application. It's called before the view functions |
|
|
|
|
are called and can modify the url values provided. |
|
|
|
|
"""Register a URL value preprocessor function for all view |
|
|
|
|
functions in the application. These functions will be called before the |
|
|
|
|
:meth:`before_request` functions. |
|
|
|
|
|
|
|
|
|
The function can modify the values captured from the matched url before |
|
|
|
|
they are passed to the view. For example, this can be used to pop a |
|
|
|
|
common language code value and place it in ``g`` rather than pass it to |
|
|
|
|
every view. |
|
|
|
|
|
|
|
|
|
The function is passed the endpoint name and values dict. The return |
|
|
|
|
value is ignored. |
|
|
|
|
""" |
|
|
|
|
self.url_value_preprocessors.setdefault(None, []).append(f) |
|
|
|
|
return f |
|
|
|
@ -1428,42 +1484,31 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
return f |
|
|
|
|
|
|
|
|
|
def _find_error_handler(self, e): |
|
|
|
|
"""Finds a registered error handler for the request’s blueprint. |
|
|
|
|
Otherwise falls back to the app, returns None if not a suitable |
|
|
|
|
handler is found. |
|
|
|
|
"""Find a registered error handler for a request in this order: |
|
|
|
|
blueprint handler for a specific code, app handler for a specific code, |
|
|
|
|
blueprint generic HTTPException handler, app generic HTTPException handler, |
|
|
|
|
and returns None if a suitable handler is not found. |
|
|
|
|
""" |
|
|
|
|
exc_class, code = self._get_exc_class_and_code(type(e)) |
|
|
|
|
|
|
|
|
|
def find_handler(handler_map): |
|
|
|
|
if not handler_map: |
|
|
|
|
return |
|
|
|
|
queue = deque(exc_class.__mro__) |
|
|
|
|
# Protect from geniuses who might create circular references in |
|
|
|
|
# __mro__ |
|
|
|
|
done = set() |
|
|
|
|
|
|
|
|
|
while queue: |
|
|
|
|
cls = queue.popleft() |
|
|
|
|
if cls in done: |
|
|
|
|
continue |
|
|
|
|
done.add(cls) |
|
|
|
|
|
|
|
|
|
for cls in exc_class.__mro__: |
|
|
|
|
handler = handler_map.get(cls) |
|
|
|
|
if handler is not None: |
|
|
|
|
# cache for next time exc_class is raised |
|
|
|
|
handler_map[exc_class] = handler |
|
|
|
|
return handler |
|
|
|
|
|
|
|
|
|
queue.extend(cls.__mro__) |
|
|
|
|
|
|
|
|
|
# try blueprint handlers |
|
|
|
|
handler = find_handler(self.error_handler_spec |
|
|
|
|
.get(request.blueprint, {}) |
|
|
|
|
.get(code)) |
|
|
|
|
if handler is not None: |
|
|
|
|
return handler |
|
|
|
|
# check for any in blueprint or app |
|
|
|
|
for name, c in ((request.blueprint, code), (None, code), |
|
|
|
|
(request.blueprint, None), (None, None)): |
|
|
|
|
handler = find_handler(self.error_handler_spec.get(name, {}).get(c)) |
|
|
|
|
|
|
|
|
|
# fall back to app handlers |
|
|
|
|
return find_handler(self.error_handler_spec[None].get(code)) |
|
|
|
|
if handler: |
|
|
|
|
return handler |
|
|
|
|
|
|
|
|
|
def handle_http_exception(self, e): |
|
|
|
|
"""Handles an HTTP exception. By default this will invoke the |
|
|
|
@ -1494,12 +1539,20 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
traceback. This is helpful for debugging implicitly raised HTTP |
|
|
|
|
exceptions. |
|
|
|
|
|
|
|
|
|
.. versionchanged:: 1.0 |
|
|
|
|
Bad request errors are not trapped by default in debug mode. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.8 |
|
|
|
|
""" |
|
|
|
|
if self.config['TRAP_HTTP_EXCEPTIONS']: |
|
|
|
|
return True |
|
|
|
|
if self.config['TRAP_BAD_REQUEST_ERRORS']: |
|
|
|
|
|
|
|
|
|
trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS'] |
|
|
|
|
|
|
|
|
|
# if unset, trap based on debug mode |
|
|
|
|
if (trap_bad_request is None and self.debug) or trap_bad_request: |
|
|
|
|
return isinstance(e, BadRequest) |
|
|
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def handle_user_exception(self, e): |
|
|
|
@ -1510,16 +1563,30 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
function will either return a response value or reraise the |
|
|
|
|
exception with the same traceback. |
|
|
|
|
|
|
|
|
|
.. versionchanged:: 1.0 |
|
|
|
|
Key errors raised from request data like ``form`` show the the bad |
|
|
|
|
key in debug mode rather than a generic bad request message. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.7 |
|
|
|
|
""" |
|
|
|
|
exc_type, exc_value, tb = sys.exc_info() |
|
|
|
|
assert exc_value is e |
|
|
|
|
|
|
|
|
|
# ensure not to trash sys.exc_info() at that point in case someone |
|
|
|
|
# wants the traceback preserved in handle_http_exception. Of course |
|
|
|
|
# we cannot prevent users from trashing it themselves in a custom |
|
|
|
|
# trap_http_exception method so that's their fault then. |
|
|
|
|
|
|
|
|
|
# MultiDict passes the key to the exception, but that's ignored |
|
|
|
|
# when generating the response message. Set an informative |
|
|
|
|
# description for key errors in debug mode or when trapping errors. |
|
|
|
|
if ( |
|
|
|
|
(self.debug or self.config['TRAP_BAD_REQUEST_ERRORS']) |
|
|
|
|
and isinstance(e, BadRequestKeyError) |
|
|
|
|
# only set it if it's still the default description |
|
|
|
|
and e.description is BadRequestKeyError.description |
|
|
|
|
): |
|
|
|
|
e.description = "KeyError: '{0}'".format(*e.args) |
|
|
|
|
|
|
|
|
|
if isinstance(e, HTTPException) and not self.trap_http_exception(e): |
|
|
|
|
return self.handle_http_exception(e) |
|
|
|
|
|
|
|
|
@ -1556,7 +1623,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
self.log_exception((exc_type, exc_value, tb)) |
|
|
|
|
if handler is None: |
|
|
|
|
return InternalServerError() |
|
|
|
|
return handler(e) |
|
|
|
|
return self.finalize_request(handler(e), from_error_handler=True) |
|
|
|
|
|
|
|
|
|
def log_exception(self, exc_info): |
|
|
|
|
"""Logs an exception. This is called by :meth:`handle_exception` |
|
|
|
@ -1624,9 +1691,30 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
rv = self.dispatch_request() |
|
|
|
|
except Exception as e: |
|
|
|
|
rv = self.handle_user_exception(e) |
|
|
|
|
return self.finalize_request(rv) |
|
|
|
|
|
|
|
|
|
def finalize_request(self, rv, from_error_handler=False): |
|
|
|
|
"""Given the return value from a view function this finalizes |
|
|
|
|
the request by converting it into a response and invoking the |
|
|
|
|
postprocessing functions. This is invoked for both normal |
|
|
|
|
request dispatching as well as error handlers. |
|
|
|
|
|
|
|
|
|
Because this means that it might be called as a result of a |
|
|
|
|
failure a special safe mode is available which can be enabled |
|
|
|
|
with the `from_error_handler` flag. If enabled, failures in |
|
|
|
|
response processing will be logged and otherwise ignored. |
|
|
|
|
|
|
|
|
|
:internal: |
|
|
|
|
""" |
|
|
|
|
response = self.make_response(rv) |
|
|
|
|
response = self.process_response(response) |
|
|
|
|
request_finished.send(self, response=response) |
|
|
|
|
try: |
|
|
|
|
response = self.process_response(response) |
|
|
|
|
request_finished.send(self, response=response) |
|
|
|
|
except Exception: |
|
|
|
|
if not from_error_handler: |
|
|
|
|
raise |
|
|
|
|
self.logger.exception('Request finalizing failed with an ' |
|
|
|
|
'error while handling an error') |
|
|
|
|
return response |
|
|
|
|
|
|
|
|
|
def try_trigger_before_first_request_functions(self): |
|
|
|
@ -1679,62 +1767,106 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
def make_response(self, rv): |
|
|
|
|
"""Converts the return value from a view function to a real |
|
|
|
|
response object that is an instance of :attr:`response_class`. |
|
|
|
|
|
|
|
|
|
The following types are allowed for `rv`: |
|
|
|
|
|
|
|
|
|
.. tabularcolumns:: |p{3.5cm}|p{9.5cm}| |
|
|
|
|
|
|
|
|
|
======================= =========================================== |
|
|
|
|
:attr:`response_class` the object is returned unchanged |
|
|
|
|
:class:`str` a response object is created with the |
|
|
|
|
string as body |
|
|
|
|
:class:`unicode` a response object is created with the |
|
|
|
|
string encoded to utf-8 as body |
|
|
|
|
a WSGI function the function is called as WSGI application |
|
|
|
|
and buffered as response object |
|
|
|
|
:class:`tuple` A tuple in the form ``(response, status, |
|
|
|
|
headers)`` or ``(response, headers)`` |
|
|
|
|
where `response` is any of the |
|
|
|
|
types defined here, `status` is a string |
|
|
|
|
or an integer and `headers` is a list or |
|
|
|
|
a dictionary with header values. |
|
|
|
|
======================= =========================================== |
|
|
|
|
|
|
|
|
|
:param rv: the return value from the view function |
|
|
|
|
"""Convert the return value from a view function to an instance of |
|
|
|
|
:attr:`response_class`. |
|
|
|
|
|
|
|
|
|
:param rv: the return value from the view function. The view function |
|
|
|
|
must return a response. Returning ``None``, or the view ending |
|
|
|
|
without returning, is not allowed. The following types are allowed |
|
|
|
|
for ``view_rv``: |
|
|
|
|
|
|
|
|
|
``str`` (``unicode`` in Python 2) |
|
|
|
|
A response object is created with the string encoded to UTF-8 |
|
|
|
|
as the body. |
|
|
|
|
|
|
|
|
|
``bytes`` (``str`` in Python 2) |
|
|
|
|
A response object is created with the bytes as the body. |
|
|
|
|
|
|
|
|
|
``tuple`` |
|
|
|
|
Either ``(body, status, headers)``, ``(body, status)``, or |
|
|
|
|
``(body, headers)``, where ``body`` is any of the other types |
|
|
|
|
allowed here, ``status`` is a string or an integer, and |
|
|
|
|
``headers`` is a dictionary or a list of ``(key, value)`` |
|
|
|
|
tuples. If ``body`` is a :attr:`response_class` instance, |
|
|
|
|
``status`` overwrites the exiting value and ``headers`` are |
|
|
|
|
extended. |
|
|
|
|
|
|
|
|
|
:attr:`response_class` |
|
|
|
|
The object is returned unchanged. |
|
|
|
|
|
|
|
|
|
other :class:`~werkzeug.wrappers.Response` class |
|
|
|
|
The object is coerced to :attr:`response_class`. |
|
|
|
|
|
|
|
|
|
:func:`callable` |
|
|
|
|
The function is called as a WSGI application. The result is |
|
|
|
|
used to create a response object. |
|
|
|
|
|
|
|
|
|
.. versionchanged:: 0.9 |
|
|
|
|
Previously a tuple was interpreted as the arguments for the |
|
|
|
|
response object. |
|
|
|
|
""" |
|
|
|
|
status_or_headers = headers = None |
|
|
|
|
if isinstance(rv, tuple): |
|
|
|
|
rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) |
|
|
|
|
|
|
|
|
|
if rv is None: |
|
|
|
|
raise ValueError('View function did not return a response') |
|
|
|
|
status = headers = None |
|
|
|
|
|
|
|
|
|
# unpack tuple returns |
|
|
|
|
if isinstance(rv, (tuple, list)): |
|
|
|
|
len_rv = len(rv) |
|
|
|
|
|
|
|
|
|
# a 3-tuple is unpacked directly |
|
|
|
|
if len_rv == 3: |
|
|
|
|
rv, status, headers = rv |
|
|
|
|
# decide if a 2-tuple has status or headers |
|
|
|
|
elif len_rv == 2: |
|
|
|
|
if isinstance(rv[1], (Headers, dict, tuple, list)): |
|
|
|
|
rv, headers = rv |
|
|
|
|
else: |
|
|
|
|
rv, status = rv |
|
|
|
|
# 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).' |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
if isinstance(status_or_headers, (dict, list)): |
|
|
|
|
headers, status_or_headers = status_or_headers, None |
|
|
|
|
# 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.' |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
# make sure the body is an instance of the response class |
|
|
|
|
if not isinstance(rv, self.response_class): |
|
|
|
|
# When we create a response object directly, we let the constructor |
|
|
|
|
# set the headers and status. We do this because there can be |
|
|
|
|
# some extra logic involved when creating these objects with |
|
|
|
|
# specific values (like default content type selection). |
|
|
|
|
if isinstance(rv, (text_type, bytes, bytearray)): |
|
|
|
|
rv = self.response_class(rv, headers=headers, |
|
|
|
|
status=status_or_headers) |
|
|
|
|
headers = status_or_headers = None |
|
|
|
|
# let the response class set the status and headers instead of |
|
|
|
|
# waiting to do it manually, so that the class can handle any |
|
|
|
|
# special logic |
|
|
|
|
rv = self.response_class(rv, status=status, headers=headers) |
|
|
|
|
status = headers = None |
|
|
|
|
else: |
|
|
|
|
rv = self.response_class.force_type(rv, request.environ) |
|
|
|
|
|
|
|
|
|
if status_or_headers is not None: |
|
|
|
|
if isinstance(status_or_headers, string_types): |
|
|
|
|
rv.status = status_or_headers |
|
|
|
|
# evaluate a WSGI callable, or coerce a different response |
|
|
|
|
# class to the correct type |
|
|
|
|
try: |
|
|
|
|
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) |
|
|
|
|
) |
|
|
|
|
reraise(TypeError, new_error, sys.exc_info()[2]) |
|
|
|
|
|
|
|
|
|
# prefer the status if it was provided |
|
|
|
|
if status is not None: |
|
|
|
|
if isinstance(status, (text_type, bytes, bytearray)): |
|
|
|
|
rv.status = status |
|
|
|
|
else: |
|
|
|
|
rv.status_code = status_or_headers |
|
|
|
|
rv.status_code = status |
|
|
|
|
|
|
|
|
|
# extend existing headers with provided headers |
|
|
|
|
if headers: |
|
|
|
|
rv.headers.extend(headers) |
|
|
|
|
|
|
|
|
@ -1759,7 +1891,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
if self.config['SERVER_NAME'] is not None: |
|
|
|
|
return self.url_map.bind( |
|
|
|
|
self.config['SERVER_NAME'], |
|
|
|
|
script_name=self.config['APPLICATION_ROOT'] or '/', |
|
|
|
|
script_name=self.config['APPLICATION_ROOT'], |
|
|
|
|
url_scheme=self.config['PREFERRED_URL_SCHEME']) |
|
|
|
|
|
|
|
|
|
def inject_url_defaults(self, endpoint, values): |
|
|
|
@ -1797,16 +1929,16 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
raise error |
|
|
|
|
|
|
|
|
|
def preprocess_request(self): |
|
|
|
|
"""Called before the actual request dispatching and will |
|
|
|
|
call each :meth:`before_request` decorated function, passing no |
|
|
|
|
arguments. |
|
|
|
|
If any of these functions returns a value, it's handled as |
|
|
|
|
if it was the return value from the view and further |
|
|
|
|
request handling is stopped. |
|
|
|
|
"""Called before the request is dispatched. Calls |
|
|
|
|
:attr:`url_value_preprocessors` registered with the app and the |
|
|
|
|
current blueprint (if any). Then calls :attr:`before_request_funcs` |
|
|
|
|
registered with the app and the blueprint. |
|
|
|
|
|
|
|
|
|
This also triggers the :meth:`url_value_preprocessor` functions before |
|
|
|
|
the actual :meth:`before_request` functions are called. |
|
|
|
|
If any :meth:`before_request` handler returns a non-None value, the |
|
|
|
|
value is handled as if it was the return value from the view, and |
|
|
|
|
further request handling is stopped. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
bp = _request_ctx_stack.top.request.blueprint |
|
|
|
|
|
|
|
|
|
funcs = self.url_value_preprocessors.get(None, ()) |
|
|
|
@ -1846,7 +1978,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
for handler in funcs: |
|
|
|
|
response = handler(response) |
|
|
|
|
if not self.session_interface.is_null_session(ctx.session): |
|
|
|
|
self.save_session(ctx.session, response) |
|
|
|
|
self.session_interface.save_session(self, ctx.session, response) |
|
|
|
|
return response |
|
|
|
|
|
|
|
|
|
def do_teardown_request(self, exc=_sentinel): |
|
|
|
@ -1931,10 +2063,19 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
def test_request_context(self, *args, **kwargs): |
|
|
|
|
"""Creates a WSGI environment from the given values (see |
|
|
|
|
:class:`werkzeug.test.EnvironBuilder` for more information, this |
|
|
|
|
function accepts the same arguments). |
|
|
|
|
function accepts the same arguments plus two additional). |
|
|
|
|
|
|
|
|
|
Additional arguments (only if ``base_url`` is not specified): |
|
|
|
|
|
|
|
|
|
:param subdomain: subdomain to use for route matching |
|
|
|
|
:param url_scheme: scheme for the request, default |
|
|
|
|
``PREFERRED_URL_SCHEME`` or ``http``. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
from flask.testing import make_test_environ_builder |
|
|
|
|
|
|
|
|
|
builder = make_test_environ_builder(self, *args, **kwargs) |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
return self.request_context(builder.get_environ()) |
|
|
|
|
finally: |
|
|
|
@ -1966,14 +2107,17 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
exception context to start the response |
|
|
|
|
""" |
|
|
|
|
ctx = self.request_context(environ) |
|
|
|
|
ctx.push() |
|
|
|
|
error = None |
|
|
|
|
try: |
|
|
|
|
try: |
|
|
|
|
ctx.push() |
|
|
|
|
response = self.full_dispatch_request() |
|
|
|
|
except Exception as e: |
|
|
|
|
error = e |
|
|
|
|
response = self.make_response(self.handle_exception(e)) |
|
|
|
|
response = self.handle_exception(e) |
|
|
|
|
except: |
|
|
|
|
error = sys.exc_info()[1] |
|
|
|
|
raise |
|
|
|
|
return response(environ, start_response) |
|
|
|
|
finally: |
|
|
|
|
if self.should_ignore_error(error): |
|
|
|
|