mirror of https://github.com/pallets/flask.git
extract common Flask/Blueprint API to Scaffold base class
Co-authored-by: Chris Nguyen <chrisngyn99@gmail.com>
This commit is contained in:
parent
216d97c21a
commit
b146a13f63
|
|
@ -37,6 +37,11 @@ Unreleased
|
||||||
For example, this allows setting the ``Content-Type`` for
|
For example, this allows setting the ``Content-Type`` for
|
||||||
``jsonify()``. Use ``response.headers.extend()`` if extending is
|
``jsonify()``. Use ``response.headers.extend()`` if extending is
|
||||||
desired. :issue:`3628`
|
desired. :issue:`3628`
|
||||||
|
- The ``Scaffold`` class provides a common API for the ``Flask`` and
|
||||||
|
``Blueprint`` classes. ``Blueprint`` information is stored in
|
||||||
|
attributes just like ``Flask``, rather than opaque lambda functions.
|
||||||
|
This is intended to improve consistency and maintainability.
|
||||||
|
:issue:`3215`
|
||||||
|
|
||||||
|
|
||||||
Version 1.1.x
|
Version 1.1.x
|
||||||
|
|
|
||||||
367
src/flask/app.py
367
src/flask/app.py
|
|
@ -1,7 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from functools import update_wrapper
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
|
|
@ -9,7 +8,6 @@ from werkzeug.datastructures import Headers
|
||||||
from werkzeug.datastructures import ImmutableDict
|
from werkzeug.datastructures import ImmutableDict
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from werkzeug.exceptions import BadRequestKeyError
|
from werkzeug.exceptions import BadRequestKeyError
|
||||||
from werkzeug.exceptions import default_exceptions
|
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
from werkzeug.exceptions import InternalServerError
|
from werkzeug.exceptions import InternalServerError
|
||||||
from werkzeug.routing import BuildError
|
from werkzeug.routing import BuildError
|
||||||
|
|
@ -30,8 +28,6 @@ from .globals import _request_ctx_stack
|
||||||
from .globals import g
|
from .globals import g
|
||||||
from .globals import request
|
from .globals import request
|
||||||
from .globals import session
|
from .globals import session
|
||||||
from .helpers import _endpoint_from_view_func
|
|
||||||
from .helpers import _PackageBoundObject
|
|
||||||
from .helpers import find_package
|
from .helpers import find_package
|
||||||
from .helpers import get_debug_flag
|
from .helpers import get_debug_flag
|
||||||
from .helpers import get_env
|
from .helpers import get_env
|
||||||
|
|
@ -41,21 +37,21 @@ from .helpers import locked_cached_property
|
||||||
from .helpers import url_for
|
from .helpers import url_for
|
||||||
from .json import jsonify
|
from .json import jsonify
|
||||||
from .logging import create_logger
|
from .logging import create_logger
|
||||||
|
from .scaffold import _endpoint_from_view_func
|
||||||
|
from .scaffold import _sentinel
|
||||||
|
from .scaffold import Scaffold
|
||||||
|
from .scaffold import setupmethod
|
||||||
from .sessions import SecureCookieSessionInterface
|
from .sessions import SecureCookieSessionInterface
|
||||||
from .signals import appcontext_tearing_down
|
from .signals import appcontext_tearing_down
|
||||||
from .signals import got_request_exception
|
from .signals import got_request_exception
|
||||||
from .signals import request_finished
|
from .signals import request_finished
|
||||||
from .signals import request_started
|
from .signals import request_started
|
||||||
from .signals import request_tearing_down
|
from .signals import request_tearing_down
|
||||||
from .templating import _default_template_ctx_processor
|
|
||||||
from .templating import DispatchingJinjaLoader
|
from .templating import DispatchingJinjaLoader
|
||||||
from .templating import Environment
|
from .templating import Environment
|
||||||
from .wrappers import Request
|
from .wrappers import Request
|
||||||
from .wrappers import Response
|
from .wrappers import Response
|
||||||
|
|
||||||
# a singleton sentinel value for parameter defaults
|
|
||||||
_sentinel = object()
|
|
||||||
|
|
||||||
|
|
||||||
def _make_timedelta(value):
|
def _make_timedelta(value):
|
||||||
if not isinstance(value, timedelta):
|
if not isinstance(value, timedelta):
|
||||||
|
|
@ -63,28 +59,7 @@ def _make_timedelta(value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def setupmethod(f):
|
class Flask(Scaffold):
|
||||||
"""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."
|
|
||||||
)
|
|
||||||
return f(self, *args, **kwargs)
|
|
||||||
|
|
||||||
return update_wrapper(wrapper_func, f)
|
|
||||||
|
|
||||||
|
|
||||||
class Flask(_PackageBoundObject):
|
|
||||||
"""The flask object implements a WSGI application and acts as the central
|
"""The flask object implements a WSGI application and acts as the central
|
||||||
object. It is passed the name of the module or package of the
|
object. It is passed the name of the module or package of the
|
||||||
application. Once it is created it will act as a central registry for
|
application. Once it is created it will act as a central registry for
|
||||||
|
|
@ -394,13 +369,14 @@ class Flask(_PackageBoundObject):
|
||||||
instance_relative_config=False,
|
instance_relative_config=False,
|
||||||
root_path=None,
|
root_path=None,
|
||||||
):
|
):
|
||||||
_PackageBoundObject.__init__(
|
super().__init__(
|
||||||
self, import_name, template_folder=template_folder, root_path=root_path
|
import_name=import_name,
|
||||||
|
static_folder=static_folder,
|
||||||
|
static_url_path=static_url_path,
|
||||||
|
template_folder=template_folder,
|
||||||
|
root_path=root_path,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.static_url_path = static_url_path
|
|
||||||
self.static_folder = static_folder
|
|
||||||
|
|
||||||
if instance_path is None:
|
if instance_path is None:
|
||||||
instance_path = self.auto_find_instance_path()
|
instance_path = self.auto_find_instance_path()
|
||||||
elif not os.path.isabs(instance_path):
|
elif not os.path.isabs(instance_path):
|
||||||
|
|
@ -419,24 +395,6 @@ class Flask(_PackageBoundObject):
|
||||||
#: to load a config from files.
|
#: to load a config from files.
|
||||||
self.config = self.make_config(instance_relative_config)
|
self.config = self.make_config(instance_relative_config)
|
||||||
|
|
||||||
#: A dictionary of all view functions registered. The keys will
|
|
||||||
#: be function names which are also used to generate URLs and
|
|
||||||
#: the values are the function objects themselves.
|
|
||||||
#: To register a view function, use the :meth:`route` decorator.
|
|
||||||
self.view_functions = {}
|
|
||||||
|
|
||||||
#: A dictionary of all registered error handlers. The key is ``None``
|
|
||||||
#: for error handlers active on the application, otherwise the key is
|
|
||||||
#: the name of the blueprint. Each key points to another dictionary
|
|
||||||
#: where the key is the status code of the http exception. The
|
|
||||||
#: special key ``None`` points to a list of tuples where the first item
|
|
||||||
#: is the class for the instance check and the second the error handler
|
|
||||||
#: function.
|
|
||||||
#:
|
|
||||||
#: To register an error handler, use the :meth:`errorhandler`
|
|
||||||
#: decorator.
|
|
||||||
self.error_handler_spec = {}
|
|
||||||
|
|
||||||
#: A list of functions that are called when :meth:`url_for` raises a
|
#: A list of functions that are called when :meth:`url_for` raises a
|
||||||
#: :exc:`~werkzeug.routing.BuildError`. Each function registered here
|
#: :exc:`~werkzeug.routing.BuildError`. Each function registered here
|
||||||
#: is called with `error`, `endpoint` and `values`. If a function
|
#: is called with `error`, `endpoint` and `values`. If a function
|
||||||
|
|
@ -446,13 +404,6 @@ class Flask(_PackageBoundObject):
|
||||||
#: .. versionadded:: 0.9
|
#: .. versionadded:: 0.9
|
||||||
self.url_build_error_handlers = []
|
self.url_build_error_handlers = []
|
||||||
|
|
||||||
#: 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 list of functions that will be called at the beginning of the
|
#: A list of functions that will be called at the beginning of the
|
||||||
#: first request to this instance. To register a function, use the
|
#: first request to this instance. To register a function, use the
|
||||||
#: :meth:`before_first_request` decorator.
|
#: :meth:`before_first_request` decorator.
|
||||||
|
|
@ -460,25 +411,6 @@ class Flask(_PackageBoundObject):
|
||||||
#: .. versionadded:: 0.8
|
#: .. versionadded:: 0.8
|
||||||
self.before_first_request_funcs = []
|
self.before_first_request_funcs = []
|
||||||
|
|
||||||
#: A dictionary with lists of functions that should be called after
|
|
||||||
#: each 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 close database connections. To register a function
|
|
||||||
#: here, use the :meth:`after_request` decorator.
|
|
||||||
self.after_request_funcs = {}
|
|
||||||
|
|
||||||
#: A dictionary with lists of functions that are called after
|
|
||||||
#: each request, even if an exception has occurred. The key of the
|
|
||||||
#: dictionary is the name of the blueprint this function is active for,
|
|
||||||
#: ``None`` for all requests. These functions are not allowed to modify
|
|
||||||
#: the request, and their return values are ignored. If an exception
|
|
||||||
#: occurred while processing the request, it gets passed to each
|
|
||||||
#: teardown_request function. To register a function here, use the
|
|
||||||
#: :meth:`teardown_request` decorator.
|
|
||||||
#:
|
|
||||||
#: .. versionadded:: 0.7
|
|
||||||
self.teardown_request_funcs = {}
|
|
||||||
|
|
||||||
#: A list of functions that are called when the application context
|
#: A list of functions that are called when the application context
|
||||||
#: is destroyed. Since the application context is also torn down
|
#: is destroyed. Since the application context is also torn down
|
||||||
#: if the request ends this is the place to store code that disconnects
|
#: if the request ends this is the place to store code that disconnects
|
||||||
|
|
@ -487,35 +419,6 @@ class Flask(_PackageBoundObject):
|
||||||
#: .. versionadded:: 0.9
|
#: .. versionadded:: 0.9
|
||||||
self.teardown_appcontext_funcs = []
|
self.teardown_appcontext_funcs = []
|
||||||
|
|
||||||
#: 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 = {}
|
|
||||||
|
|
||||||
#: A dictionary with lists of functions that can be used as URL value
|
|
||||||
#: preprocessors. 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
|
|
||||||
#: of URL values before they are used as the keyword arguments of the
|
|
||||||
#: view function. For each function registered this one should also
|
|
||||||
#: provide a :meth:`url_defaults` function that adds the parameters
|
|
||||||
#: automatically again that were removed that way.
|
|
||||||
#:
|
|
||||||
#: .. versionadded:: 0.7
|
|
||||||
self.url_default_functions = {}
|
|
||||||
|
|
||||||
#: A dictionary with list of functions that are called without argument
|
|
||||||
#: to populate the template context. The key of the dictionary is the
|
|
||||||
#: name of the blueprint this function is active for, ``None`` for all
|
|
||||||
#: 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]}
|
|
||||||
|
|
||||||
#: A list of shell context processor functions that should be run
|
#: A list of shell context processor functions that should be run
|
||||||
#: when a shell context is created.
|
#: when a shell context is created.
|
||||||
#:
|
#:
|
||||||
|
|
@ -586,6 +489,9 @@ class Flask(_PackageBoundObject):
|
||||||
# the app's commands to another CLI tool.
|
# the app's commands to another CLI tool.
|
||||||
self.cli.name = self.name
|
self.cli.name = self.name
|
||||||
|
|
||||||
|
def _is_setup_finished(self):
|
||||||
|
return self.debug and self._got_first_request
|
||||||
|
|
||||||
@locked_cached_property
|
@locked_cached_property
|
||||||
def name(self):
|
def name(self):
|
||||||
"""The name of the application. This is usually the import name
|
"""The name of the application. This is usually the import name
|
||||||
|
|
@ -1206,152 +1112,6 @@ class Flask(_PackageBoundObject):
|
||||||
)
|
)
|
||||||
self.view_functions[endpoint] = view_func
|
self.view_functions[endpoint] = view_func
|
||||||
|
|
||||||
def route(self, rule, **options):
|
|
||||||
"""A decorator that is used to register a view function for a
|
|
||||||
given URL rule. This does the same thing as :meth:`add_url_rule`
|
|
||||||
but is intended for decorator usage::
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return 'Hello World'
|
|
||||||
|
|
||||||
For more information refer to :ref:`url-route-registrations`.
|
|
||||||
|
|
||||||
:param rule: the URL rule as string
|
|
||||||
:param endpoint: the endpoint for the registered URL rule. Flask
|
|
||||||
itself assumes the name of the view function as
|
|
||||||
endpoint
|
|
||||||
: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
|
|
||||||
is a list of methods this rule should be limited
|
|
||||||
to (``GET``, ``POST`` etc.). By default a rule
|
|
||||||
just listens for ``GET`` (and implicitly ``HEAD``).
|
|
||||||
Starting with Flask 0.6, ``OPTIONS`` is implicitly
|
|
||||||
added and handled by the standard request handling.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def decorator(f):
|
|
||||||
endpoint = options.pop("endpoint", None)
|
|
||||||
self.add_url_rule(rule, endpoint, f, **options)
|
|
||||||
return f
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def endpoint(self, endpoint):
|
|
||||||
"""A decorator to register a function as an endpoint.
|
|
||||||
Example::
|
|
||||||
|
|
||||||
@app.endpoint('example.endpoint')
|
|
||||||
def example():
|
|
||||||
return "example"
|
|
||||||
|
|
||||||
:param endpoint: the name of the endpoint
|
|
||||||
"""
|
|
||||||
|
|
||||||
def decorator(f):
|
|
||||||
self.view_functions[endpoint] = f
|
|
||||||
return f
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _get_exc_class_and_code(exc_class_or_code):
|
|
||||||
"""Get the exception class being handled. For HTTP status codes
|
|
||||||
or ``HTTPException`` subclasses, return both the exception and
|
|
||||||
status code.
|
|
||||||
|
|
||||||
:param exc_class_or_code: Any exception class, or an HTTP status
|
|
||||||
code as an integer.
|
|
||||||
"""
|
|
||||||
if isinstance(exc_class_or_code, int):
|
|
||||||
exc_class = default_exceptions[exc_class_or_code]
|
|
||||||
else:
|
|
||||||
exc_class = exc_class_or_code
|
|
||||||
|
|
||||||
assert issubclass(
|
|
||||||
exc_class, Exception
|
|
||||||
), "Custom exceptions must be subclasses of Exception."
|
|
||||||
|
|
||||||
if issubclass(exc_class, HTTPException):
|
|
||||||
return exc_class, exc_class.code
|
|
||||||
else:
|
|
||||||
return exc_class, None
|
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def errorhandler(self, code_or_exception):
|
|
||||||
"""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)
|
|
||||||
def page_not_found(error):
|
|
||||||
return 'This page does not exist', 404
|
|
||||||
|
|
||||||
You can also register handlers for arbitrary exceptions::
|
|
||||||
|
|
||||||
@app.errorhandler(DatabaseError)
|
|
||||||
def special_exception_handler(error):
|
|
||||||
return 'Database connection failed', 500
|
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
|
||||||
Use :meth:`register_error_handler` instead of modifying
|
|
||||||
:attr:`error_handler_spec` directly, for application wide error
|
|
||||||
handlers.
|
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
|
||||||
One can now additionally also register custom exception types
|
|
||||||
that do not necessarily have to be a subclass of the
|
|
||||||
:class:`~werkzeug.exceptions.HTTPException` class.
|
|
||||||
|
|
||||||
: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
|
|
||||||
usage.
|
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
|
||||||
"""
|
|
||||||
self._register_error_handler(None, code_or_exception, f)
|
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def _register_error_handler(self, key, code_or_exception, f):
|
|
||||||
"""
|
|
||||||
:type key: None|str
|
|
||||||
:type code_or_exception: int|T<=Exception
|
|
||||||
:type f: callable
|
|
||||||
"""
|
|
||||||
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
|
||||||
raise ValueError(
|
|
||||||
"Tried to register a handler for an exception instance"
|
|
||||||
f" {code_or_exception!r}. Handlers can only be"
|
|
||||||
" registered for exception classes or HTTP error codes."
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
exc_class, code = self._get_exc_class_and_code(code_or_exception)
|
|
||||||
except KeyError:
|
|
||||||
raise KeyError(
|
|
||||||
f"'{code_or_exception}' is not a recognized HTTP error"
|
|
||||||
" code. Use a subclass of HTTPException with that code"
|
|
||||||
" instead."
|
|
||||||
)
|
|
||||||
|
|
||||||
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
|
|
||||||
handlers[exc_class] = f
|
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def template_filter(self, name=None):
|
def template_filter(self, name=None):
|
||||||
"""A decorator that is used to register custom template filter.
|
"""A decorator that is used to register custom template filter.
|
||||||
|
|
@ -1455,20 +1215,6 @@ class Flask(_PackageBoundObject):
|
||||||
"""
|
"""
|
||||||
self.jinja_env.globals[name or f.__name__] = f
|
self.jinja_env.globals[name or f.__name__] = f
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def before_request(self, f):
|
|
||||||
"""Registers a function to run before each request.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def before_first_request(self, f):
|
def before_first_request(self, f):
|
||||||
"""Registers a function to be run before the first request to this
|
"""Registers a function to be run before the first request to this
|
||||||
|
|
@ -1482,59 +1228,6 @@ class Flask(_PackageBoundObject):
|
||||||
self.before_first_request_funcs.append(f)
|
self.before_first_request_funcs.append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def after_request(self, f):
|
|
||||||
"""Register a function to be run after each request.
|
|
||||||
|
|
||||||
Your function must take one parameter, an instance of
|
|
||||||
:attr:`response_class` and return a new response object or the
|
|
||||||
same (see :meth:`process_response`).
|
|
||||||
|
|
||||||
As of Flask 0.7 this function might not be executed at the end of the
|
|
||||||
request in case an unhandled exception occurred.
|
|
||||||
"""
|
|
||||||
self.after_request_funcs.setdefault(None, []).append(f)
|
|
||||||
return f
|
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def teardown_request(self, f):
|
|
||||||
"""Register a function to be run at the end of each request,
|
|
||||||
regardless of whether there was an exception or not. These functions
|
|
||||||
are executed when the request context is popped, even if not an
|
|
||||||
actual request was performed.
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
ctx = app.test_request_context()
|
|
||||||
ctx.push()
|
|
||||||
...
|
|
||||||
ctx.pop()
|
|
||||||
|
|
||||||
When ``ctx.pop()`` is executed in the above example, the teardown
|
|
||||||
functions are called just before the request context moves from the
|
|
||||||
stack of active contexts. This becomes relevant if you are using
|
|
||||||
such constructs in tests.
|
|
||||||
|
|
||||||
Generally teardown functions must take every necessary step to avoid
|
|
||||||
that they will fail. If they do execute code that might fail they
|
|
||||||
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 an exception it will
|
|
||||||
be passed an error object.
|
|
||||||
|
|
||||||
The return values of teardown functions are ignored.
|
|
||||||
|
|
||||||
.. admonition:: Debug Note
|
|
||||||
|
|
||||||
In debug mode Flask will not tear down a request on an exception
|
|
||||||
immediately. Instead it will keep it alive so that the interactive
|
|
||||||
debugger can still access it. This behavior can be controlled
|
|
||||||
by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable.
|
|
||||||
"""
|
|
||||||
self.teardown_request_funcs.setdefault(None, []).append(f)
|
|
||||||
return f
|
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def teardown_appcontext(self, f):
|
def teardown_appcontext(self, f):
|
||||||
"""Registers a function to be called when the application context
|
"""Registers a function to be called when the application context
|
||||||
|
|
@ -1568,12 +1261,6 @@ class Flask(_PackageBoundObject):
|
||||||
self.teardown_appcontext_funcs.append(f)
|
self.teardown_appcontext_funcs.append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def context_processor(self, f):
|
|
||||||
"""Registers a template context processor function."""
|
|
||||||
self.template_context_processors[None].append(f)
|
|
||||||
return f
|
|
||||||
|
|
||||||
@setupmethod
|
@setupmethod
|
||||||
def shell_context_processor(self, f):
|
def shell_context_processor(self, f):
|
||||||
"""Registers a shell context processor function.
|
"""Registers a shell context processor function.
|
||||||
|
|
@ -1583,32 +1270,6 @@ class Flask(_PackageBoundObject):
|
||||||
self.shell_context_processors.append(f)
|
self.shell_context_processors.append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def url_value_preprocessor(self, f):
|
|
||||||
"""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
|
|
||||||
|
|
||||||
@setupmethod
|
|
||||||
def url_defaults(self, f):
|
|
||||||
"""Callback function for URL defaults for all view functions of the
|
|
||||||
application. It's called with the endpoint and values and should
|
|
||||||
update the values passed in place.
|
|
||||||
"""
|
|
||||||
self.url_default_functions.setdefault(None, []).append(f)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def _find_error_handler(self, e):
|
def _find_error_handler(self, e):
|
||||||
"""Return a registered error handler for an exception in this order:
|
"""Return a registered error handler for an exception in this order:
|
||||||
blueprint handler for a specific code, app handler for a specific code,
|
blueprint handler for a specific code, app handler for a specific code,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from .helpers import _endpoint_from_view_func
|
from .scaffold import _endpoint_from_view_func
|
||||||
from .helpers import _PackageBoundObject
|
from .scaffold import _sentinel
|
||||||
|
from .scaffold import Scaffold
|
||||||
# a singleton sentinel value for parameter defaults
|
|
||||||
_sentinel = object()
|
|
||||||
|
|
||||||
|
|
||||||
class BlueprintSetupState:
|
class BlueprintSetupState:
|
||||||
|
|
@ -76,7 +74,7 @@ class BlueprintSetupState:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Blueprint(_PackageBoundObject):
|
class Blueprint(Scaffold):
|
||||||
"""Represents a blueprint, a collection of routes and other
|
"""Represents a blueprint, a collection of routes and other
|
||||||
app-related functions that can be registered on a real application
|
app-related functions that can be registered on a real application
|
||||||
later.
|
later.
|
||||||
|
|
@ -167,20 +165,25 @@ class Blueprint(_PackageBoundObject):
|
||||||
root_path=None,
|
root_path=None,
|
||||||
cli_group=_sentinel,
|
cli_group=_sentinel,
|
||||||
):
|
):
|
||||||
_PackageBoundObject.__init__(
|
super().__init__(
|
||||||
self, import_name, template_folder, root_path=root_path
|
import_name=import_name,
|
||||||
|
static_folder=static_folder,
|
||||||
|
static_url_path=static_url_path,
|
||||||
|
template_folder=template_folder,
|
||||||
|
root_path=root_path,
|
||||||
)
|
)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
self.subdomain = subdomain
|
self.subdomain = subdomain
|
||||||
self.static_folder = static_folder
|
|
||||||
self.static_url_path = static_url_path
|
|
||||||
self.deferred_functions = []
|
self.deferred_functions = []
|
||||||
if url_defaults is None:
|
if url_defaults is None:
|
||||||
url_defaults = {}
|
url_defaults = {}
|
||||||
self.url_values_defaults = url_defaults
|
self.url_values_defaults = url_defaults
|
||||||
self.cli_group = cli_group
|
self.cli_group = cli_group
|
||||||
|
|
||||||
|
def _is_setup_finished(self):
|
||||||
|
return self.warn_on_modifications and self._got_registered_once
|
||||||
|
|
||||||
def record(self, func):
|
def record(self, func):
|
||||||
"""Registers a function that is called when the blueprint is
|
"""Registers a function that is called when the blueprint is
|
||||||
registered on the application. This function is called with the
|
registered on the application. This function is called with the
|
||||||
|
|
@ -241,6 +244,36 @@ class Blueprint(_PackageBoundObject):
|
||||||
endpoint="static",
|
endpoint="static",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Merge app and self dictionaries.
|
||||||
|
def merge_dict_lists(self_dict, app_dict):
|
||||||
|
"""Merges self_dict into app_dict. Replaces None keys with self.name.
|
||||||
|
Values of dict must be lists.
|
||||||
|
"""
|
||||||
|
for key, values in self_dict.items():
|
||||||
|
key = self.name if key is None else f"{self.name}.{key}"
|
||||||
|
app_dict.setdefault(key, []).extend(values)
|
||||||
|
|
||||||
|
def merge_dict_nested(self_dict, app_dict):
|
||||||
|
"""Merges self_dict into app_dict. Replaces None keys with self.name.
|
||||||
|
Values of dict must be dict.
|
||||||
|
"""
|
||||||
|
for key, value in self_dict.items():
|
||||||
|
key = self.name if key is None else f"{self.name}.{key}"
|
||||||
|
app_dict[key] = value
|
||||||
|
|
||||||
|
app.view_functions.update(self.view_functions)
|
||||||
|
|
||||||
|
merge_dict_lists(self.before_request_funcs, app.before_request_funcs)
|
||||||
|
merge_dict_lists(self.after_request_funcs, app.after_request_funcs)
|
||||||
|
merge_dict_lists(self.teardown_request_funcs, app.teardown_request_funcs)
|
||||||
|
merge_dict_lists(self.url_default_functions, app.url_default_functions)
|
||||||
|
merge_dict_lists(self.url_value_preprocessors, app.url_value_preprocessors)
|
||||||
|
merge_dict_lists(
|
||||||
|
self.template_context_processors, app.template_context_processors
|
||||||
|
)
|
||||||
|
|
||||||
|
merge_dict_nested(self.error_handler_spec, app.error_handler_spec)
|
||||||
|
|
||||||
for deferred in self.deferred_functions:
|
for deferred in self.deferred_functions:
|
||||||
deferred(state)
|
deferred(state)
|
||||||
|
|
||||||
|
|
@ -258,18 +291,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
self.cli.name = cli_resolved_group
|
self.cli.name = cli_resolved_group
|
||||||
app.cli.add_command(self.cli)
|
app.cli.add_command(self.cli)
|
||||||
|
|
||||||
def route(self, rule, **options):
|
|
||||||
"""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):
|
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||||
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
"""Like :meth:`Flask.add_url_rule` but for a blueprint. The endpoint for
|
||||||
the :func:`url_for` function is prefixed with the name of the blueprint.
|
the :func:`url_for` function is prefixed with the name of the blueprint.
|
||||||
|
|
@ -282,23 +303,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
), "Blueprint view function name should not contain dots"
|
), "Blueprint view function name should not contain dots"
|
||||||
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
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
|
|
||||||
prefix the endpoint with the blueprint name, this has to be done
|
|
||||||
explicitly by the user of this method. If the endpoint is prefixed
|
|
||||||
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):
|
def app_template_filter(self, name=None):
|
||||||
"""Register a custom template filter, available application wide. Like
|
"""Register a custom template filter, available application wide. Like
|
||||||
:meth:`Flask.template_filter` but for a blueprint.
|
:meth:`Flask.template_filter` but for a blueprint.
|
||||||
|
|
@ -391,16 +395,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
|
|
||||||
self.record_once(register_template)
|
self.record_once(register_template)
|
||||||
|
|
||||||
def before_request(self, f):
|
|
||||||
"""Like :meth:`Flask.before_request` but for a blueprint. This function
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def before_app_request(self, f):
|
def before_app_request(self, f):
|
||||||
"""Like :meth:`Flask.before_request`. Such a function is executed
|
"""Like :meth:`Flask.before_request`. Such a function is executed
|
||||||
before each request, even if outside of a blueprint.
|
before each request, even if outside of a blueprint.
|
||||||
|
|
@ -417,16 +411,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
|
self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def after_request(self, f):
|
|
||||||
"""Like :meth:`Flask.after_request` but for a blueprint. This function
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def after_app_request(self, f):
|
def after_app_request(self, f):
|
||||||
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
||||||
is executed after each request, even if outside of the blueprint.
|
is executed after each request, even if outside of the blueprint.
|
||||||
|
|
@ -436,18 +420,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def teardown_request(self, f):
|
|
||||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. This
|
|
||||||
function is only executed when tearing down requests handled by a
|
|
||||||
function of that blueprint. Teardown request functions are executed
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def teardown_app_request(self, f):
|
def teardown_app_request(self, f):
|
||||||
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
||||||
function is executed when tearing down each request, even if outside of
|
function is executed when tearing down each request, even if outside of
|
||||||
|
|
@ -458,17 +430,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
)
|
)
|
||||||
return 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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def app_context_processor(self, f):
|
def app_context_processor(self, f):
|
||||||
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
||||||
function is executed each request, even if outside of the blueprint.
|
function is executed each request, even if outside of the blueprint.
|
||||||
|
|
@ -489,26 +450,6 @@ class Blueprint(_PackageBoundObject):
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def url_value_preprocessor(self, f):
|
|
||||||
"""Registers a function as URL value preprocessor for this
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def url_defaults(self, f):
|
|
||||||
"""Callback function for URL defaults for this blueprint. It's called
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
def app_url_value_preprocessor(self, f):
|
def app_url_value_preprocessor(self, f):
|
||||||
"""Same as :meth:`url_value_preprocessor` but application wide.
|
"""Same as :meth:`url_value_preprocessor` but application wide.
|
||||||
"""
|
"""
|
||||||
|
|
@ -524,35 +465,3 @@ class Blueprint(_PackageBoundObject):
|
||||||
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
||||||
)
|
)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def errorhandler(self, code_or_exception):
|
|
||||||
"""Registers an error handler that becomes active for this blueprint
|
|
||||||
only. Please be aware that routing does not happen local to a
|
|
||||||
blueprint so an error handler for 404 usually is not handled by
|
|
||||||
a blueprint unless it is caused inside a view function. Another
|
|
||||||
special case is the 500 internal server error which is always looked
|
|
||||||
up from the application.
|
|
||||||
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
return f
|
|
||||||
|
|
||||||
return decorator
|
|
||||||
|
|
||||||
def register_error_handler(self, code_or_exception, f):
|
|
||||||
"""Non-decorator version of the :meth:`errorhandler` error attach
|
|
||||||
function, akin to the :meth:`~flask.Flask.register_error_handler`
|
|
||||||
application-wide function of the :class:`~flask.Flask` object but
|
|
||||||
for error handlers limited to this blueprint.
|
|
||||||
|
|
||||||
.. versionadded:: 0.11
|
|
||||||
"""
|
|
||||||
self.record_once(
|
|
||||||
lambda s: s.app._register_error_handler(self.name, code_or_exception, f)
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -76,14 +76,6 @@ def get_load_dotenv(default=True):
|
||||||
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."
|
|
||||||
return view_func.__name__
|
|
||||||
|
|
||||||
|
|
||||||
def stream_with_context(generator_or_function):
|
def stream_with_context(generator_or_function):
|
||||||
"""Request contexts disappear when the response is started on the server.
|
"""Request contexts disappear when the response is started on the server.
|
||||||
This is done for efficiency reasons and to make it less likely to encounter
|
This is done for efficiency reasons and to make it less likely to encounter
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,411 @@
|
||||||
|
from functools import update_wrapper
|
||||||
|
|
||||||
|
from werkzeug.exceptions import default_exceptions
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
|
from .helpers import _PackageBoundObject
|
||||||
|
from .templating import _default_template_ctx_processor
|
||||||
|
|
||||||
|
# a singleton sentinel value for parameter defaults
|
||||||
|
_sentinel = object()
|
||||||
|
|
||||||
|
|
||||||
|
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._is_setup_finished():
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class Scaffold(_PackageBoundObject):
|
||||||
|
"""A common base for class Flask and class Blueprint.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#: Skeleton local JSON decoder class to use.
|
||||||
|
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`.
|
||||||
|
json_encoder = None
|
||||||
|
|
||||||
|
#: Skeleton local JSON decoder class to use.
|
||||||
|
#: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`.
|
||||||
|
json_decoder = None
|
||||||
|
|
||||||
|
#: The name of the package or module that this app belongs to. Do not
|
||||||
|
#: change this once it is set by the constructor.
|
||||||
|
import_name = None
|
||||||
|
|
||||||
|
#: Location of the template files to be added to the template lookup.
|
||||||
|
#: ``None`` if templates should not be added.
|
||||||
|
template_folder = None
|
||||||
|
|
||||||
|
#: Absolute path to the package on the filesystem. Used to look up
|
||||||
|
#: resources contained in the package.
|
||||||
|
root_path = None
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
import_name,
|
||||||
|
static_folder="static",
|
||||||
|
static_url_path=None,
|
||||||
|
template_folder=None,
|
||||||
|
root_path=None,
|
||||||
|
):
|
||||||
|
super().__init__(
|
||||||
|
import_name=import_name,
|
||||||
|
template_folder=template_folder,
|
||||||
|
root_path=root_path,
|
||||||
|
)
|
||||||
|
self.static_folder = static_folder
|
||||||
|
self.static_url_path = static_url_path
|
||||||
|
|
||||||
|
#: A dictionary of all view functions registered. The keys will
|
||||||
|
#: be function names which are also used to generate URLs and
|
||||||
|
#: the values are the function objects themselves.
|
||||||
|
#: To register a view function, use the :meth:`route` decorator.
|
||||||
|
self.view_functions = {}
|
||||||
|
|
||||||
|
#: A dictionary of all registered error handlers. The key is ``None``
|
||||||
|
#: for error handlers active on the application, otherwise the key is
|
||||||
|
#: the name of the blueprint. Each key points to another dictionary
|
||||||
|
#: where the key is the status code of the http exception. The
|
||||||
|
#: special key ``None`` points to a list of tuples where the first item
|
||||||
|
#: is the class for the instance check and the second the error handler
|
||||||
|
#: function.
|
||||||
|
#:
|
||||||
|
#: To register an error handler, use the :meth:`errorhandler`
|
||||||
|
#: decorator.
|
||||||
|
self.error_handler_spec = {}
|
||||||
|
|
||||||
|
#: 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 dictionary with lists of functions that should be called after
|
||||||
|
#: each 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 close database connections. To register a function
|
||||||
|
#: here, use the :meth:`after_request` decorator.
|
||||||
|
self.after_request_funcs = {}
|
||||||
|
|
||||||
|
#: A dictionary with lists of functions that are called after
|
||||||
|
#: each request, even if an exception has occurred. The key of the
|
||||||
|
#: dictionary is the name of the blueprint this function is active for,
|
||||||
|
#: ``None`` for all requests. These functions are not allowed to modify
|
||||||
|
#: the request, and their return values are ignored. If an exception
|
||||||
|
#: occurred while processing the request, it gets passed to each
|
||||||
|
#: teardown_request function. To register a function here, use the
|
||||||
|
#: :meth:`teardown_request` decorator.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.7
|
||||||
|
self.teardown_request_funcs = {}
|
||||||
|
|
||||||
|
#: A dictionary with list of functions that are called without argument
|
||||||
|
#: to populate the template context. The key of the dictionary is the
|
||||||
|
#: name of the blueprint this function is active for, ``None`` for all
|
||||||
|
#: 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]}
|
||||||
|
|
||||||
|
#: 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 = {}
|
||||||
|
|
||||||
|
#: A dictionary with lists of functions that can be used as URL value
|
||||||
|
#: preprocessors. 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
|
||||||
|
#: of URL values before they are used as the keyword arguments of the
|
||||||
|
#: view function. For each function registered this one should also
|
||||||
|
#: provide a :meth:`url_defaults` function that adds the parameters
|
||||||
|
#: automatically again that were removed that way.
|
||||||
|
#:
|
||||||
|
#: .. versionadded:: 0.7
|
||||||
|
self.url_default_functions = {}
|
||||||
|
|
||||||
|
def _is_setup_finished(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def route(self, rule, **options):
|
||||||
|
"""A decorator that is used to register a view function for a
|
||||||
|
given URL rule. This does the same thing as :meth:`add_url_rule`
|
||||||
|
but is intended for decorator usage::
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return 'Hello World'
|
||||||
|
|
||||||
|
For more information refer to :ref:`url-route-registrations`.
|
||||||
|
|
||||||
|
:param rule: the URL rule as string
|
||||||
|
:param endpoint: the endpoint for the registered URL rule. Flask
|
||||||
|
itself assumes the name of the view function as
|
||||||
|
endpoint
|
||||||
|
: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
|
||||||
|
is a list of methods this rule should be limited
|
||||||
|
to (``GET``, ``POST`` etc.). By default a rule
|
||||||
|
just listens for ``GET`` (and implicitly ``HEAD``).
|
||||||
|
Starting with Flask 0.6, ``OPTIONS`` is implicitly
|
||||||
|
added and handled by the standard request handling.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f):
|
||||||
|
endpoint = options.pop("endpoint", None)
|
||||||
|
self.add_url_rule(rule, endpoint, f, **options)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def add_url_rule(
|
||||||
|
self,
|
||||||
|
rule,
|
||||||
|
endpoint=None,
|
||||||
|
view_func=None,
|
||||||
|
provide_automatic_options=None,
|
||||||
|
**options,
|
||||||
|
):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def endpoint(self, endpoint):
|
||||||
|
"""A decorator to register a function as an endpoint.
|
||||||
|
Example::
|
||||||
|
|
||||||
|
@app.endpoint('example.endpoint')
|
||||||
|
def example():
|
||||||
|
return "example"
|
||||||
|
|
||||||
|
:param endpoint: the name of the endpoint
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(f):
|
||||||
|
self.view_functions[endpoint] = f
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def before_request(self, f):
|
||||||
|
"""Registers a function to run before each request.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def after_request(self, f):
|
||||||
|
"""Register a function to be run after each request.
|
||||||
|
|
||||||
|
Your function must take one parameter, an instance of
|
||||||
|
:attr:`response_class` and return a new response object or the
|
||||||
|
same (see :meth:`process_response`).
|
||||||
|
|
||||||
|
As of Flask 0.7 this function might not be executed at the end of the
|
||||||
|
request in case an unhandled exception occurred.
|
||||||
|
"""
|
||||||
|
self.after_request_funcs.setdefault(None, []).append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def teardown_request(self, f):
|
||||||
|
"""Register a function to be run at the end of each request,
|
||||||
|
regardless of whether there was an exception or not. These functions
|
||||||
|
are executed when the request context is popped, even if not an
|
||||||
|
actual request was performed.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
ctx = app.test_request_context()
|
||||||
|
ctx.push()
|
||||||
|
...
|
||||||
|
ctx.pop()
|
||||||
|
|
||||||
|
When ``ctx.pop()`` is executed in the above example, the teardown
|
||||||
|
functions are called just before the request context moves from the
|
||||||
|
stack of active contexts. This becomes relevant if you are using
|
||||||
|
such constructs in tests.
|
||||||
|
|
||||||
|
Generally teardown functions must take every necessary step to avoid
|
||||||
|
that they will fail. If they do execute code that might fail they
|
||||||
|
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 an exception it will
|
||||||
|
be passed an error object.
|
||||||
|
|
||||||
|
The return values of teardown functions are ignored.
|
||||||
|
|
||||||
|
.. admonition:: Debug Note
|
||||||
|
|
||||||
|
In debug mode Flask will not tear down a request on an exception
|
||||||
|
immediately. Instead it will keep it alive so that the interactive
|
||||||
|
debugger can still access it. This behavior can be controlled
|
||||||
|
by the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable.
|
||||||
|
"""
|
||||||
|
self.teardown_request_funcs.setdefault(None, []).append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def context_processor(self, f):
|
||||||
|
"""Registers a template context processor function."""
|
||||||
|
self.template_context_processors[None].append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def url_value_preprocessor(self, f):
|
||||||
|
"""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
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def url_defaults(self, f):
|
||||||
|
"""Callback function for URL defaults for all view functions of the
|
||||||
|
application. It's called with the endpoint and values and should
|
||||||
|
update the values passed in place.
|
||||||
|
"""
|
||||||
|
self.url_default_functions.setdefault(None, []).append(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def errorhandler(self, code_or_exception):
|
||||||
|
"""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)
|
||||||
|
def page_not_found(error):
|
||||||
|
return 'This page does not exist', 404
|
||||||
|
|
||||||
|
You can also register handlers for arbitrary exceptions::
|
||||||
|
|
||||||
|
@app.errorhandler(DatabaseError)
|
||||||
|
def special_exception_handler(error):
|
||||||
|
return 'Database connection failed', 500
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
Use :meth:`register_error_handler` instead of modifying
|
||||||
|
:attr:`error_handler_spec` directly, for application wide error
|
||||||
|
handlers.
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
One can now additionally also register custom exception types
|
||||||
|
that do not necessarily have to be a subclass of the
|
||||||
|
:class:`~werkzeug.exceptions.HTTPException` class.
|
||||||
|
|
||||||
|
: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
|
||||||
|
usage.
|
||||||
|
|
||||||
|
.. versionadded:: 0.7
|
||||||
|
"""
|
||||||
|
self._register_error_handler(None, code_or_exception, f)
|
||||||
|
|
||||||
|
@setupmethod
|
||||||
|
def _register_error_handler(self, key, code_or_exception, f):
|
||||||
|
"""
|
||||||
|
:type key: None|str
|
||||||
|
:type code_or_exception: int|T<=Exception
|
||||||
|
:type f: callable
|
||||||
|
"""
|
||||||
|
if isinstance(code_or_exception, HTTPException): # old broken behavior
|
||||||
|
raise ValueError(
|
||||||
|
"Tried to register a handler for an exception instance"
|
||||||
|
f" {code_or_exception!r}. Handlers can only be"
|
||||||
|
" registered for exception classes or HTTP error codes."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
exc_class, code = self._get_exc_class_and_code(code_or_exception)
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError(
|
||||||
|
f"'{code_or_exception}' is not a recognized HTTP error"
|
||||||
|
" code. Use a subclass of HTTPException with that code"
|
||||||
|
" instead."
|
||||||
|
)
|
||||||
|
|
||||||
|
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
|
||||||
|
handlers[exc_class] = f
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_exc_class_and_code(exc_class_or_code):
|
||||||
|
"""Get the exception class being handled. For HTTP status codes
|
||||||
|
or ``HTTPException`` subclasses, return both the exception and
|
||||||
|
status code.
|
||||||
|
|
||||||
|
:param exc_class_or_code: Any exception class, or an HTTP status
|
||||||
|
code as an integer.
|
||||||
|
"""
|
||||||
|
if isinstance(exc_class_or_code, int):
|
||||||
|
exc_class = default_exceptions[exc_class_or_code]
|
||||||
|
else:
|
||||||
|
exc_class = exc_class_or_code
|
||||||
|
|
||||||
|
assert issubclass(
|
||||||
|
exc_class, Exception
|
||||||
|
), "Custom exceptions must be subclasses of Exception."
|
||||||
|
|
||||||
|
if issubclass(exc_class, HTTPException):
|
||||||
|
return exc_class, exc_class.code
|
||||||
|
else:
|
||||||
|
return exc_class, None
|
||||||
|
|
||||||
|
|
||||||
|
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."
|
||||||
|
return view_func.__name__
|
||||||
Loading…
Reference in New Issue