2021-03-24 20:47:55 +00:00
|
|
|
from collections import defaultdict
|
2011-05-29 18:55:06 +02:00
|
|
|
from functools import update_wrapper
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2020-07-22 15:02:31 -04:00
|
|
|
from .scaffold import _endpoint_from_view_func
|
|
|
|
|
from .scaffold import _sentinel
|
|
|
|
|
from .scaffold import Scaffold
|
2018-05-14 22:05:54 -04:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2020-04-04 09:43:06 -07:00
|
|
|
class BlueprintSetupState:
|
2011-05-29 15:54:58 +02:00
|
|
|
"""Temporary holder object for registering a blueprint with the
|
2011-06-17 20:27:25 +02:00
|
|
|
application. An instance of this class is created by the
|
|
|
|
|
:meth:`~flask.Blueprint.make_setup_state` method and later passed
|
|
|
|
|
to all register callback functions.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
|
|
|
|
|
2011-05-29 18:55:06 +02:00
|
|
|
def __init__(self, blueprint, app, options, first_registration):
|
2011-06-17 20:27:25 +02:00
|
|
|
#: a reference to the current application
|
2011-05-29 15:54:58 +02:00
|
|
|
self.app = app
|
2011-06-17 20:27:25 +02:00
|
|
|
|
2012-04-09 17:17:52 -03:00
|
|
|
#: a reference to the blueprint that created this setup state.
|
2011-05-29 15:54:58 +02:00
|
|
|
self.blueprint = blueprint
|
2011-06-17 20:27:25 +02:00
|
|
|
|
|
|
|
|
#: a dictionary with all options that were passed to the
|
|
|
|
|
#: :meth:`~flask.Flask.register_blueprint` method.
|
2011-05-29 15:54:58 +02:00
|
|
|
self.options = options
|
2011-06-17 20:27:25 +02:00
|
|
|
|
|
|
|
|
#: as blueprints can be registered multiple times with the
|
|
|
|
|
#: application and not everything wants to be registered
|
|
|
|
|
#: multiple times on it, this attribute can be used to figure
|
|
|
|
|
#: out if the blueprint was registered in the past already.
|
2011-05-29 18:55:06 +02:00
|
|
|
self.first_registration = first_registration
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2019-05-06 15:39:41 -04:00
|
|
|
subdomain = self.options.get("subdomain")
|
2011-05-29 15:54:58 +02:00
|
|
|
if subdomain is None:
|
|
|
|
|
subdomain = self.blueprint.subdomain
|
2011-06-17 20:27:25 +02:00
|
|
|
|
2014-11-05 06:04:58 +03:00
|
|
|
#: The subdomain that the blueprint should be active for, ``None``
|
2011-06-17 20:27:25 +02:00
|
|
|
#: otherwise.
|
2011-05-29 15:54:58 +02:00
|
|
|
self.subdomain = subdomain
|
|
|
|
|
|
2019-05-06 15:39:41 -04:00
|
|
|
url_prefix = self.options.get("url_prefix")
|
2011-05-29 15:54:58 +02:00
|
|
|
if url_prefix is None:
|
|
|
|
|
url_prefix = self.blueprint.url_prefix
|
2011-06-17 20:27:25 +02:00
|
|
|
#: The prefix that should be used for all URLs defined on the
|
|
|
|
|
#: blueprint.
|
2011-05-29 15:54:58 +02:00
|
|
|
self.url_prefix = url_prefix
|
|
|
|
|
|
2021-02-24 21:18:12 +00:00
|
|
|
self.name_prefix = self.options.get("name_prefix", "")
|
|
|
|
|
|
2011-06-17 20:27:25 +02:00
|
|
|
#: A dictionary with URL defaults that is added to each and every
|
|
|
|
|
#: URL that was defined with the blueprint.
|
2011-07-06 10:16:56 +02:00
|
|
|
self.url_defaults = dict(self.blueprint.url_values_defaults)
|
2019-05-06 15:39:41 -04:00
|
|
|
self.url_defaults.update(self.options.get("url_defaults", ()))
|
2011-06-17 03:29:40 +02:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
2011-06-17 03:29:40 +02:00
|
|
|
"""A helper method to register a rule (and optionally a view function)
|
|
|
|
|
to the application. The endpoint is automatically prefixed with the
|
|
|
|
|
blueprint's name.
|
|
|
|
|
"""
|
2018-04-29 15:32:12 -07:00
|
|
|
if self.url_prefix is not None:
|
2018-05-01 14:35:55 +09:00
|
|
|
if rule:
|
2019-05-06 15:39:41 -04:00
|
|
|
rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
|
2018-05-01 14:35:55 +09:00
|
|
|
else:
|
|
|
|
|
rule = self.url_prefix
|
2019-05-06 15:39:41 -04:00
|
|
|
options.setdefault("subdomain", self.subdomain)
|
2011-05-29 15:54:58 +02:00
|
|
|
if endpoint is None:
|
|
|
|
|
endpoint = _endpoint_from_view_func(view_func)
|
2011-06-17 03:29:40 +02:00
|
|
|
defaults = self.url_defaults
|
2019-05-06 15:39:41 -04:00
|
|
|
if "defaults" in options:
|
|
|
|
|
defaults = dict(defaults, **options.pop("defaults"))
|
|
|
|
|
self.app.add_url_rule(
|
|
|
|
|
rule,
|
2021-02-24 21:18:12 +00:00
|
|
|
f"{self.name_prefix}{self.blueprint.name}.{endpoint}",
|
2019-05-06 15:39:41 -04:00
|
|
|
view_func,
|
|
|
|
|
defaults=defaults,
|
2020-04-04 09:43:06 -07:00
|
|
|
**options,
|
2019-05-06 15:39:41 -04:00
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
|
|
|
|
|
|
2020-07-22 15:02:31 -04:00
|
|
|
class Blueprint(Scaffold):
|
2019-06-12 14:01:49 -07:00
|
|
|
"""Represents a blueprint, a collection of routes and other
|
|
|
|
|
app-related functions that can be registered on a real application
|
|
|
|
|
later.
|
|
|
|
|
|
|
|
|
|
A blueprint is an object that allows defining application functions
|
|
|
|
|
without requiring an application object ahead of time. It uses the
|
|
|
|
|
same decorators as :class:`~flask.Flask`, but defers the need for an
|
|
|
|
|
application by recording them for later registration.
|
|
|
|
|
|
|
|
|
|
Decorating a function with a blueprint creates a deferred function
|
|
|
|
|
that is called with :class:`~flask.blueprints.BlueprintSetupState`
|
|
|
|
|
when the blueprint is registered on an application.
|
|
|
|
|
|
2020-04-04 12:57:14 -07:00
|
|
|
See :doc:`/blueprints` for more information.
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2019-06-12 14:01:49 -07:00
|
|
|
:param name: The name of the blueprint. Will be prepended to each
|
|
|
|
|
endpoint name.
|
|
|
|
|
:param import_name: The name of the blueprint package, usually
|
|
|
|
|
``__name__``. This helps locate the ``root_path`` for the
|
|
|
|
|
blueprint.
|
|
|
|
|
:param static_folder: A folder with static files that should be
|
|
|
|
|
served by the blueprint's static route. The path is relative to
|
|
|
|
|
the blueprint's root path. Blueprint static files are disabled
|
|
|
|
|
by default.
|
|
|
|
|
:param static_url_path: The url to serve static files from.
|
|
|
|
|
Defaults to ``static_folder``. If the blueprint does not have
|
|
|
|
|
a ``url_prefix``, the app's static route will take precedence,
|
|
|
|
|
and the blueprint's static files won't be accessible.
|
|
|
|
|
:param template_folder: A folder with templates that should be added
|
|
|
|
|
to the app's template search path. The path is relative to the
|
|
|
|
|
blueprint's root path. Blueprint templates are disabled by
|
|
|
|
|
default. Blueprint templates have a lower precedence than those
|
|
|
|
|
in the app's templates folder.
|
|
|
|
|
:param url_prefix: A path to prepend to all of the blueprint's URLs,
|
|
|
|
|
to make them distinct from the rest of the app's routes.
|
|
|
|
|
:param subdomain: A subdomain that blueprint routes will match on by
|
|
|
|
|
default.
|
|
|
|
|
:param url_defaults: A dict of default values that blueprint routes
|
|
|
|
|
will receive by default.
|
2021-03-10 10:51:06 -08:00
|
|
|
:param root_path: By default, the blueprint will automatically set
|
|
|
|
|
this based on ``import_name``. In certain situations this
|
|
|
|
|
automatic detection can fail, so the path can be specified
|
|
|
|
|
manually instead.
|
|
|
|
|
|
|
|
|
|
.. versionchanged:: 1.1.0
|
|
|
|
|
Blueprints have a ``cli`` group to register nested CLI commands.
|
|
|
|
|
The ``cli_group`` parameter controls the name of the group under
|
|
|
|
|
the ``flask`` command.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.7
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
|
|
|
|
|
2011-06-07 15:32:44 +02:00
|
|
|
warn_on_modifications = False
|
|
|
|
|
_got_registered_once = False
|
|
|
|
|
|
2021-03-08 15:05:47 -08:00
|
|
|
#: Blueprint local JSON encoder class to use. Set to ``None`` to use
|
|
|
|
|
#: the app's :class:`~flask.Flask.json_encoder`.
|
2016-06-08 12:03:26 +03:00
|
|
|
json_encoder = None
|
2021-03-08 15:05:47 -08:00
|
|
|
#: Blueprint local JSON decoder class to use. Set to ``None`` to use
|
|
|
|
|
#: the app's :class:`~flask.Flask.json_decoder`.
|
2016-06-08 12:03:26 +03:00
|
|
|
json_decoder = None
|
|
|
|
|
|
2019-05-06 15:39:41 -04:00
|
|
|
def __init__(
|
|
|
|
|
self,
|
|
|
|
|
name,
|
|
|
|
|
import_name,
|
|
|
|
|
static_folder=None,
|
|
|
|
|
static_url_path=None,
|
|
|
|
|
template_folder=None,
|
|
|
|
|
url_prefix=None,
|
|
|
|
|
subdomain=None,
|
|
|
|
|
url_defaults=None,
|
|
|
|
|
root_path=None,
|
2018-05-14 22:05:54 -04:00
|
|
|
cli_group=_sentinel,
|
2019-05-06 15:39:41 -04:00
|
|
|
):
|
2020-07-22 15:02:31 -04:00
|
|
|
super().__init__(
|
|
|
|
|
import_name=import_name,
|
|
|
|
|
static_folder=static_folder,
|
|
|
|
|
static_url_path=static_url_path,
|
|
|
|
|
template_folder=template_folder,
|
|
|
|
|
root_path=root_path,
|
2019-05-06 15:39:41 -04:00
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
self.name = name
|
|
|
|
|
self.url_prefix = url_prefix
|
|
|
|
|
self.subdomain = subdomain
|
|
|
|
|
self.deferred_functions = []
|
2021-03-10 10:51:06 -08:00
|
|
|
|
2011-06-17 03:29:40 +02:00
|
|
|
if url_defaults is None:
|
|
|
|
|
url_defaults = {}
|
2021-03-10 10:51:06 -08:00
|
|
|
|
2011-07-06 10:16:56 +02:00
|
|
|
self.url_values_defaults = url_defaults
|
2018-05-14 22:05:54 -04:00
|
|
|
self.cli_group = cli_group
|
2021-02-24 21:18:12 +00:00
|
|
|
self._blueprints = []
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2020-07-22 15:02:31 -04:00
|
|
|
def _is_setup_finished(self):
|
|
|
|
|
return self.warn_on_modifications and self._got_registered_once
|
|
|
|
|
|
2011-06-07 15:32:44 +02:00
|
|
|
def record(self, func):
|
|
|
|
|
"""Registers a function that is called when the blueprint is
|
|
|
|
|
registered on the application. This function is called with the
|
|
|
|
|
state as argument as returned by the :meth:`make_setup_state`
|
|
|
|
|
method.
|
|
|
|
|
"""
|
|
|
|
|
if self._got_registered_once and self.warn_on_modifications:
|
|
|
|
|
from warnings import warn
|
2019-05-06 15:39:41 -04:00
|
|
|
|
|
|
|
|
warn(
|
|
|
|
|
Warning(
|
2021-03-10 10:51:06 -08:00
|
|
|
"The blueprint was already registered once but is"
|
|
|
|
|
" getting modified now. These changes will not show"
|
|
|
|
|
" up."
|
2019-05-06 15:39:41 -04:00
|
|
|
)
|
|
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
self.deferred_functions.append(func)
|
|
|
|
|
|
2011-06-07 15:32:44 +02:00
|
|
|
def record_once(self, func):
|
|
|
|
|
"""Works like :meth:`record` but wraps the function in another
|
|
|
|
|
function that will ensure the function is only called once. If the
|
|
|
|
|
blueprint is registered a second time on the application, the
|
|
|
|
|
function passed is not called.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-05-29 18:55:06 +02:00
|
|
|
def wrapper(state):
|
|
|
|
|
if state.first_registration:
|
|
|
|
|
func(state)
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-06-07 15:32:44 +02:00
|
|
|
return self.record(update_wrapper(wrapper, func))
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2011-05-29 18:55:06 +02:00
|
|
|
def make_setup_state(self, app, options, first_registration=False):
|
2011-06-17 20:27:25 +02:00
|
|
|
"""Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
|
|
|
|
|
object that is later passed to the register callback functions.
|
|
|
|
|
Subclasses can override this to return a subclass of the setup state.
|
|
|
|
|
"""
|
2011-05-29 18:55:06 +02:00
|
|
|
return BlueprintSetupState(self, app, options, first_registration)
|
|
|
|
|
|
2021-02-24 21:18:12 +00:00
|
|
|
def register_blueprint(self, blueprint, **options):
|
|
|
|
|
"""Register a :class:`~flask.Blueprint` on this blueprint. Keyword
|
|
|
|
|
arguments passed to this method will override the defaults set
|
|
|
|
|
on the blueprint.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
|
"""
|
|
|
|
|
self._blueprints.append((blueprint, options))
|
|
|
|
|
|
|
|
|
|
def register(self, app, options):
|
2021-03-10 10:51:06 -08:00
|
|
|
"""Called by :meth:`Flask.register_blueprint` to register all
|
|
|
|
|
views and callbacks registered on the blueprint with the
|
|
|
|
|
application. Creates a :class:`.BlueprintSetupState` and calls
|
|
|
|
|
each :meth:`record` callbackwith it.
|
2017-06-14 07:16:55 -07:00
|
|
|
|
2021-03-10 10:51:06 -08:00
|
|
|
:param app: The application this blueprint is being registered
|
|
|
|
|
with.
|
2017-06-14 07:16:55 -07:00
|
|
|
:param options: Keyword arguments forwarded from
|
|
|
|
|
:meth:`~Flask.register_blueprint`.
|
|
|
|
|
:param first_registration: Whether this is the first time this
|
|
|
|
|
blueprint has been registered on the application.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2021-02-24 21:18:12 +00:00
|
|
|
first_registration = False
|
|
|
|
|
|
|
|
|
|
if self.name in app.blueprints:
|
|
|
|
|
assert app.blueprints[self.name] is self, (
|
|
|
|
|
"A name collision occurred between blueprints"
|
|
|
|
|
f" {self!r} and {app.blueprints[self.name]!r}."
|
|
|
|
|
f" Both share the same name {self.name!r}."
|
|
|
|
|
f" Blueprints that are created on the fly need unique"
|
|
|
|
|
f" names."
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
app.blueprints[self.name] = self
|
|
|
|
|
first_registration = True
|
|
|
|
|
|
2011-06-07 15:32:44 +02:00
|
|
|
self._got_registered_once = True
|
2011-05-29 18:55:06 +02:00
|
|
|
state = self.make_setup_state(app, options, first_registration)
|
2017-06-14 07:16:55 -07:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
if self.has_static_folder:
|
2017-06-14 07:16:55 -07:00
|
|
|
state.add_url_rule(
|
2020-04-04 11:39:03 -07:00
|
|
|
f"{self.static_url_path}/<path:filename>",
|
2019-05-06 15:39:41 -04:00
|
|
|
view_func=self.send_static_file,
|
|
|
|
|
endpoint="static",
|
2017-06-14 07:16:55 -07:00
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2021-03-10 10:51:06 -08:00
|
|
|
# Merge blueprint data into parent.
|
|
|
|
|
if first_registration:
|
|
|
|
|
|
2021-03-24 20:47:55 +00:00
|
|
|
def extend(bp_dict, parent_dict, ensure_sync=False):
|
2021-03-10 10:51:06 -08:00
|
|
|
for key, values in bp_dict.items():
|
|
|
|
|
key = self.name if key is None else f"{self.name}.{key}"
|
2021-03-24 20:47:55 +00:00
|
|
|
|
|
|
|
|
if ensure_sync:
|
|
|
|
|
values = [app.ensure_sync(func) for func in values]
|
|
|
|
|
|
2021-03-10 10:51:06 -08:00
|
|
|
parent_dict[key].extend(values)
|
2020-07-22 15:02:31 -04:00
|
|
|
|
2021-03-24 20:47:55 +00:00
|
|
|
for key, value in self.error_handler_spec.items():
|
|
|
|
|
key = self.name if key is None else f"{self.name}.{key}"
|
|
|
|
|
value = defaultdict(
|
|
|
|
|
dict,
|
|
|
|
|
{
|
|
|
|
|
code: {
|
|
|
|
|
exc_class: app.ensure_sync(func)
|
|
|
|
|
for exc_class, func in code_values.items()
|
|
|
|
|
}
|
|
|
|
|
for code, code_values in value.items()
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
app.error_handler_spec[key] = value
|
2021-03-10 10:51:06 -08:00
|
|
|
|
2021-03-24 20:47:55 +00:00
|
|
|
for endpoint, func in self.view_functions.items():
|
|
|
|
|
app.view_functions[endpoint] = app.ensure_sync(func)
|
|
|
|
|
|
|
|
|
|
extend(
|
|
|
|
|
self.before_request_funcs, app.before_request_funcs, ensure_sync=True
|
|
|
|
|
)
|
|
|
|
|
extend(self.after_request_funcs, app.after_request_funcs, ensure_sync=True)
|
|
|
|
|
extend(
|
|
|
|
|
self.teardown_request_funcs,
|
|
|
|
|
app.teardown_request_funcs,
|
|
|
|
|
ensure_sync=True,
|
|
|
|
|
)
|
2021-03-10 10:51:06 -08:00
|
|
|
extend(self.url_default_functions, app.url_default_functions)
|
|
|
|
|
extend(self.url_value_preprocessors, app.url_value_preprocessors)
|
|
|
|
|
extend(self.template_context_processors, app.template_context_processors)
|
2020-07-22 15:02:31 -04:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
for deferred in self.deferred_functions:
|
|
|
|
|
deferred(state)
|
|
|
|
|
|
2021-03-10 10:51:06 -08:00
|
|
|
cli_resolved_group = options.get("cli_group", self.cli_group)
|
|
|
|
|
|
2021-02-24 21:18:12 +00:00
|
|
|
if self.cli.commands:
|
|
|
|
|
if cli_resolved_group is None:
|
|
|
|
|
app.cli.commands.update(self.cli.commands)
|
|
|
|
|
elif cli_resolved_group is _sentinel:
|
|
|
|
|
self.cli.name = self.name
|
|
|
|
|
app.cli.add_command(self.cli)
|
|
|
|
|
else:
|
|
|
|
|
self.cli.name = cli_resolved_group
|
|
|
|
|
app.cli.add_command(self.cli)
|
|
|
|
|
|
|
|
|
|
for blueprint, bp_options in self._blueprints:
|
|
|
|
|
url_prefix = options.get("url_prefix", "")
|
|
|
|
|
if "url_prefix" in bp_options:
|
|
|
|
|
url_prefix = (
|
|
|
|
|
url_prefix.rstrip("/") + "/" + bp_options["url_prefix"].lstrip("/")
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
bp_options["url_prefix"] = url_prefix
|
|
|
|
|
bp_options["name_prefix"] = options.get("name_prefix", "") + self.name + "."
|
|
|
|
|
blueprint.register(app, bp_options)
|
2018-05-14 22:05:54 -04:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
2011-06-08 10:59:32 -04:00
|
|
|
"""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.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2011-08-25 17:26:57 +01:00
|
|
|
if endpoint:
|
2019-05-06 15:39:41 -04:00
|
|
|
assert "." not in endpoint, "Blueprint endpoints should not contain dots"
|
|
|
|
|
if view_func and hasattr(view_func, "__name__"):
|
|
|
|
|
assert (
|
|
|
|
|
"." not in view_func.__name__
|
|
|
|
|
), "Blueprint view function name should not contain dots"
|
|
|
|
|
self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
|
2011-05-29 15:54:58 +02:00
|
|
|
|
2012-01-16 20:16:48 -05:00
|
|
|
def app_template_filter(self, name=None):
|
|
|
|
|
"""Register a custom template filter, available application wide. Like
|
|
|
|
|
:meth:`Flask.template_filter` but for a blueprint.
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the filter, otherwise the
|
|
|
|
|
function name will be used.
|
2011-07-08 15:46:16 +01:00
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-07-08 15:46:16 +01:00
|
|
|
def decorator(f):
|
2012-01-16 20:16:48 -05:00
|
|
|
self.add_app_template_filter(f, name=name)
|
2011-07-08 15:46:16 +01:00
|
|
|
return f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-07-08 15:46:16 +01:00
|
|
|
return decorator
|
|
|
|
|
|
2012-01-16 20:16:48 -05:00
|
|
|
def add_app_template_filter(self, f, name=None):
|
|
|
|
|
"""Register a custom template filter, available application wide. Like
|
|
|
|
|
:meth:`Flask.add_template_filter` but for a blueprint. Works exactly
|
|
|
|
|
like the :meth:`app_template_filter` decorator.
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the filter, otherwise the
|
|
|
|
|
function name will be used.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-01-16 20:16:48 -05:00
|
|
|
def register_template(state):
|
|
|
|
|
state.app.jinja_env.filters[name or f.__name__] = f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-01-16 20:16:48 -05:00
|
|
|
self.record_once(register_template)
|
|
|
|
|
|
2012-10-07 12:51:46 +02:00
|
|
|
def app_template_test(self, name=None):
|
|
|
|
|
"""Register a custom template test, available application wide. Like
|
|
|
|
|
:meth:`Flask.template_test` but for a blueprint.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.10
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the test, otherwise the
|
|
|
|
|
function name will be used.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-10-07 12:51:46 +02:00
|
|
|
def decorator(f):
|
|
|
|
|
self.add_app_template_test(f, name=name)
|
|
|
|
|
return f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-10-07 12:51:46 +02:00
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
def add_app_template_test(self, f, name=None):
|
|
|
|
|
"""Register a custom template test, available application wide. Like
|
|
|
|
|
:meth:`Flask.add_template_test` but for a blueprint. Works exactly
|
|
|
|
|
like the :meth:`app_template_test` decorator.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.10
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the test, otherwise the
|
|
|
|
|
function name will be used.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-10-07 12:51:46 +02:00
|
|
|
def register_template(state):
|
|
|
|
|
state.app.jinja_env.tests[name or f.__name__] = f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2012-10-07 12:51:46 +02:00
|
|
|
self.record_once(register_template)
|
|
|
|
|
|
2013-01-27 00:46:19 +00:00
|
|
|
def app_template_global(self, name=None):
|
|
|
|
|
"""Register a custom template global, available application wide. Like
|
|
|
|
|
:meth:`Flask.template_global` but for a blueprint.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.10
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the global, otherwise the
|
|
|
|
|
function name will be used.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2013-01-27 00:46:19 +00:00
|
|
|
def decorator(f):
|
|
|
|
|
self.add_app_template_global(f, name=name)
|
|
|
|
|
return f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2013-01-27 00:46:19 +00:00
|
|
|
return decorator
|
|
|
|
|
|
|
|
|
|
def add_app_template_global(self, f, name=None):
|
|
|
|
|
"""Register a custom template global, available application wide. Like
|
|
|
|
|
:meth:`Flask.add_template_global` but for a blueprint. Works exactly
|
|
|
|
|
like the :meth:`app_template_global` decorator.
|
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.10
|
|
|
|
|
|
|
|
|
|
:param name: the optional name of the global, otherwise the
|
|
|
|
|
function name will be used.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2013-01-27 00:46:19 +00:00
|
|
|
def register_template(state):
|
|
|
|
|
state.app.jinja_env.globals[name or f.__name__] = f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2013-01-27 00:46:19 +00:00
|
|
|
self.record_once(register_template)
|
|
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def before_app_request(self, f):
|
|
|
|
|
"""Like :meth:`Flask.before_request`. Such a function is executed
|
2011-06-08 10:59:32 -04:00
|
|
|
before each request, even if outside of a blueprint.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
2021-03-24 20:47:55 +00:00
|
|
|
lambda s: s.app.before_request_funcs.setdefault(None, []).append(
|
|
|
|
|
s.app.ensure_sync(f)
|
|
|
|
|
)
|
2019-05-06 15:39:41 -04:00
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
return f
|
|
|
|
|
|
2011-08-07 12:43:38 +02:00
|
|
|
def before_app_first_request(self, f):
|
|
|
|
|
"""Like :meth:`Flask.before_first_request`. Such a function is
|
|
|
|
|
executed before the first request to the application.
|
|
|
|
|
"""
|
2021-03-24 20:47:55 +00:00
|
|
|
self.record_once(
|
|
|
|
|
lambda s: s.app.before_first_request_funcs.append(s.app.ensure_sync(f))
|
|
|
|
|
)
|
2011-08-07 12:43:38 +02:00
|
|
|
return f
|
|
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def after_app_request(self, f):
|
2011-06-08 10:59:32 -04:00
|
|
|
"""Like :meth:`Flask.after_request` but for a blueprint. Such a function
|
|
|
|
|
is executed after each request, even if outside of the blueprint.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
2021-03-24 20:47:55 +00:00
|
|
|
lambda s: s.app.after_request_funcs.setdefault(None, []).append(
|
|
|
|
|
s.app.ensure_sync(f)
|
|
|
|
|
)
|
2019-05-06 15:39:41 -04:00
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
return f
|
|
|
|
|
|
2011-06-27 09:23:31 -04:00
|
|
|
def teardown_app_request(self, f):
|
|
|
|
|
"""Like :meth:`Flask.teardown_request` but for a blueprint. Such a
|
|
|
|
|
function is executed when tearing down each request, even if outside of
|
|
|
|
|
the blueprint.
|
|
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
|
|
|
|
lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
|
|
|
|
|
)
|
2011-06-27 09:23:31 -04:00
|
|
|
return f
|
|
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def app_context_processor(self, f):
|
2011-06-08 10:59:32 -04:00
|
|
|
"""Like :meth:`Flask.context_processor` but for a blueprint. Such a
|
|
|
|
|
function is executed each request, even if outside of the blueprint.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
|
|
|
|
lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
|
|
|
|
|
)
|
2011-05-29 15:54:58 +02:00
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
def app_errorhandler(self, code):
|
2011-06-08 10:59:32 -04:00
|
|
|
"""Like :meth:`Flask.errorhandler` but for a blueprint. This
|
|
|
|
|
handler is used for all requests, even if outside of the blueprint.
|
2011-05-29 15:54:58 +02:00
|
|
|
"""
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
def decorator(f):
|
2011-06-07 15:32:44 +02:00
|
|
|
self.record_once(lambda s: s.app.errorhandler(code)(f))
|
2011-05-29 15:54:58 +02:00
|
|
|
return f
|
2019-05-06 15:39:41 -04:00
|
|
|
|
2011-05-29 15:54:58 +02:00
|
|
|
return decorator
|
2011-05-29 19:52:10 +02:00
|
|
|
|
|
|
|
|
def app_url_value_preprocessor(self, f):
|
2020-11-01 05:30:02 -08:00
|
|
|
"""Same as :meth:`url_value_preprocessor` but application wide."""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
|
|
|
|
lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
|
|
|
|
|
)
|
2011-05-29 19:52:10 +02:00
|
|
|
return f
|
|
|
|
|
|
|
|
|
|
def app_url_defaults(self, f):
|
2020-11-01 05:30:02 -08:00
|
|
|
"""Same as :meth:`url_defaults` but application wide."""
|
2019-05-06 15:39:41 -04:00
|
|
|
self.record_once(
|
|
|
|
|
lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
|
|
|
|
|
)
|
2011-05-29 19:52:10 +02:00
|
|
|
return f
|
2021-03-24 20:47:55 +00:00
|
|
|
|
|
|
|
|
def ensure_sync(self, f):
|
|
|
|
|
"""Ensure the function is synchronous.
|
|
|
|
|
|
|
|
|
|
Override if you would like custom async to sync behaviour in
|
2021-04-06 15:31:28 -07:00
|
|
|
this blueprint. Otherwise the app's
|
|
|
|
|
:meth:`~flask.Flask.ensure_sync` is used.
|
2021-03-24 20:47:55 +00:00
|
|
|
|
|
|
|
|
.. versionadded:: 2.0
|
|
|
|
|
"""
|
|
|
|
|
return f
|