Source code for flask.blueprints

# -*- coding: utf-8 -*-
"""
    flask.blueprints
    ~~~~~~~~~~~~~~~~

    Blueprints are the recommended way to implement larger or more
    pluggable applications in Flask 0.7 and later.

    :copyright: (c) 2015 by Armin Ronacher.
    :license: BSD, see LICENSE for more details.
"""
from functools import update_wrapper

from .helpers import _PackageBoundObject, _endpoint_from_view_func


[docs]class BlueprintSetupState(object): """Temporary holder object for registering a blueprint with the 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. """ def __init__(self, blueprint, app, options, first_registration): #: a reference to the current application self.app = app #: a reference to the blueprint that created this setup state. self.blueprint = blueprint #: a dictionary with all options that were passed to the #: :meth:`~flask.Flask.register_blueprint` method. self.options = options #: 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. self.first_registration = first_registration subdomain = self.options.get('subdomain') if subdomain is None: subdomain = self.blueprint.subdomain #: The subdomain that the blueprint should be active for, ``None`` #: otherwise. self.subdomain = subdomain url_prefix = self.options.get('url_prefix') if url_prefix is None: url_prefix = self.blueprint.url_prefix #: The prefix that should be used for all URLs defined on the #: blueprint. self.url_prefix = url_prefix #: A dictionary with URL defaults that is added to each and every #: URL that was defined with the blueprint. self.url_defaults = dict(self.blueprint.url_values_defaults) self.url_defaults.update(self.options.get('url_defaults', ()))
[docs] def add_url_rule(self, rule, endpoint=None, view_func=None, **options): """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. """ if self.url_prefix: rule = self.url_prefix + rule options.setdefault('subdomain', self.subdomain) if endpoint is None: endpoint = _endpoint_from_view_func(view_func) defaults = self.url_defaults if 'defaults' in options: defaults = dict(defaults, **options.pop('defaults')) self.app.add_url_rule(rule, '%s.%s' % (self.blueprint.name, endpoint), view_func, defaults=defaults, **options)
[docs]class Blueprint(_PackageBoundObject): """Represents a blueprint. A blueprint is an object that records functions that will be called with the :class:`~flask.blueprints.BlueprintSetupState` later to register functions or other things on the main application. See :ref:`blueprints` for more information. .. versionadded:: 0.7 """ warn_on_modifications = False _got_registered_once = False
[docs] def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None): _PackageBoundObject.__init__(self, import_name, template_folder, root_path=root_path) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain self.static_folder = static_folder self.static_url_path = static_url_path self.deferred_functions = [] if url_defaults is None: url_defaults = {} self.url_values_defaults = url_defaults
[docs] 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 warn(Warning('The blueprint was already registered once ' 'but is getting modified now. These changes ' 'will not show up.')) self.deferred_functions.append(func)
[docs] 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. """ def wrapper(state): if state.first_registration: func(state) return self.record(update_wrapper(wrapper, func))
[docs] def make_setup_state(self, app, options, first_registration=False): """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. """ return BlueprintSetupState(self, app, options, first_registration)
[docs] def register(self, app, options, first_registration=False): """Called by :meth:`Flask.register_blueprint` to register a blueprint on the application. This can be overridden to customize the register behavior. Keyword arguments from :func:`~flask.Flask.register_blueprint` are directly forwarded to this method in the `options` dictionary. """ self._got_registered_once = True state = self.make_setup_state(app, options, first_registration) if self.has_static_folder: state.add_url_rule(self.static_url_path + '/<path:filename>', view_func=self.send_static_file, endpoint='static') for deferred in self.deferred_functions: deferred(state)
[docs] 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
[docs] 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 the :func:`url_for` function is prefixed with the name of the blueprint. """ if endpoint: assert '.' not in endpoint, "Blueprint endpoints should not contain dots" self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
[docs] 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
[docs] 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. """ def decorator(f): self.add_app_template_filter(f, name=name) return f return decorator
[docs] 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. """ def register_template(state): state.app.jinja_env.filters[name or f.__name__] = f self.record_once(register_template)
[docs] 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. """ def decorator(f): self.add_app_template_test(f, name=name) return f return decorator
[docs] 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. """ def register_template(state): state.app.jinja_env.tests[name or f.__name__] = f self.record_once(register_template)
[docs] 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. """ def decorator(f): self.add_app_template_global(f, name=name) return f return decorator
[docs] 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. """ def register_template(state): state.app.jinja_env.globals[name or f.__name__] = f self.record_once(register_template)
[docs] 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
[docs] def before_app_request(self, f): """Like :meth:`Flask.before_request`. Such a function is executed before each request, even if outside of a blueprint. """ self.record_once(lambda s: s.app.before_request_funcs .setdefault(None, []).append(f)) return f
[docs] 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. """ self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) return f
[docs] 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
[docs] def after_app_request(self, f): """Like :meth:`Flask.after_request` but for a blueprint. Such a function is executed after each request, even if outside of the blueprint. """ self.record_once(lambda s: s.app.after_request_funcs .setdefault(None, []).append(f)) return f
[docs] 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
[docs] 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. """ self.record_once(lambda s: s.app.teardown_request_funcs .setdefault(None, []).append(f)) return f
[docs] 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
[docs] def app_context_processor(self, f): """Like :meth:`Flask.context_processor` but for a blueprint. Such a function is executed each request, even if outside of the blueprint. """ self.record_once(lambda s: s.app.template_context_processors .setdefault(None, []).append(f)) return f
[docs] def app_errorhandler(self, code): """Like :meth:`Flask.errorhandler` but for a blueprint. This handler is used for all requests, even if outside of the blueprint. """ def decorator(f): self.record_once(lambda s: s.app.errorhandler(code)(f)) return f return decorator
[docs] 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
[docs] 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
[docs] def app_url_value_preprocessor(self, f): """Same as :meth:`url_value_preprocessor` but application wide. """ self.record_once(lambda s: s.app.url_value_preprocessors .setdefault(None, []).append(f)) return f
[docs] def app_url_defaults(self, f): """Same as :meth:`url_defaults` but application wide. """ self.record_once(lambda s: s.app.url_default_functions .setdefault(None, []).append(f)) return f
[docs] 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
[docs] 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))