1# -*- coding: utf-8 -*-
2"""
3    flask.blueprints
4    ~~~~~~~~~~~~~~~~
5
6    Blueprints are the recommended way to implement larger or more
7    pluggable applications in Flask 0.7 and later.
8
9    :copyright: 2010 Pallets
10    :license: BSD-3-Clause
11"""
12from functools import update_wrapper
13
14from .helpers import _endpoint_from_view_func
15from .helpers import _PackageBoundObject
16
17# a singleton sentinel value for parameter defaults
18_sentinel = object()
19
20
21class BlueprintSetupState(object):
22    """Temporary holder object for registering a blueprint with the
23    application.  An instance of this class is created by the
24    :meth:`~flask.Blueprint.make_setup_state` method and later passed
25    to all register callback functions.
26    """
27
28    def __init__(self, blueprint, app, options, first_registration):
29        #: a reference to the current application
30        self.app = app
31
32        #: a reference to the blueprint that created this setup state.
33        self.blueprint = blueprint
34
35        #: a dictionary with all options that were passed to the
36        #: :meth:`~flask.Flask.register_blueprint` method.
37        self.options = options
38
39        #: as blueprints can be registered multiple times with the
40        #: application and not everything wants to be registered
41        #: multiple times on it, this attribute can be used to figure
42        #: out if the blueprint was registered in the past already.
43        self.first_registration = first_registration
44
45        subdomain = self.options.get("subdomain")
46        if subdomain is None:
47            subdomain = self.blueprint.subdomain
48
49        #: The subdomain that the blueprint should be active for, ``None``
50        #: otherwise.
51        self.subdomain = subdomain
52
53        url_prefix = self.options.get("url_prefix")
54        if url_prefix is None:
55            url_prefix = self.blueprint.url_prefix
56        #: The prefix that should be used for all URLs defined on the
57        #: blueprint.
58        self.url_prefix = url_prefix
59
60        #: A dictionary with URL defaults that is added to each and every
61        #: URL that was defined with the blueprint.
62        self.url_defaults = dict(self.blueprint.url_values_defaults)
63        self.url_defaults.update(self.options.get("url_defaults", ()))
64
65    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
66        """A helper method to register a rule (and optionally a view function)
67        to the application.  The endpoint is automatically prefixed with the
68        blueprint's name.
69        """
70        if self.url_prefix is not None:
71            if rule:
72                rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/")))
73            else:
74                rule = self.url_prefix
75        options.setdefault("subdomain", self.subdomain)
76        if endpoint is None:
77            endpoint = _endpoint_from_view_func(view_func)
78        defaults = self.url_defaults
79        if "defaults" in options:
80            defaults = dict(defaults, **options.pop("defaults"))
81        self.app.add_url_rule(
82            rule,
83            "%s.%s" % (self.blueprint.name, endpoint),
84            view_func,
85            defaults=defaults,
86            **options
87        )
88
89
90class Blueprint(_PackageBoundObject):
91    """Represents a blueprint, a collection of routes and other
92    app-related functions that can be registered on a real application
93    later.
94
95    A blueprint is an object that allows defining application functions
96    without requiring an application object ahead of time. It uses the
97    same decorators as :class:`~flask.Flask`, but defers the need for an
98    application by recording them for later registration.
99
100    Decorating a function with a blueprint creates a deferred function
101    that is called with :class:`~flask.blueprints.BlueprintSetupState`
102    when the blueprint is registered on an application.
103
104    See :ref:`blueprints` for more information.
105
106    .. versionchanged:: 1.1.0
107        Blueprints have a ``cli`` group to register nested CLI commands.
108        The ``cli_group`` parameter controls the name of the group under
109        the ``flask`` command.
110
111    .. versionadded:: 0.7
112
113    :param name: The name of the blueprint. Will be prepended to each
114        endpoint name.
115    :param import_name: The name of the blueprint package, usually
116        ``__name__``. This helps locate the ``root_path`` for the
117        blueprint.
118    :param static_folder: A folder with static files that should be
119        served by the blueprint's static route. The path is relative to
120        the blueprint's root path. Blueprint static files are disabled
121        by default.
122    :param static_url_path: The url to serve static files from.
123        Defaults to ``static_folder``. If the blueprint does not have
124        a ``url_prefix``, the app's static route will take precedence,
125        and the blueprint's static files won't be accessible.
126    :param template_folder: A folder with templates that should be added
127        to the app's template search path. The path is relative to the
128        blueprint's root path. Blueprint templates are disabled by
129        default. Blueprint templates have a lower precedence than those
130        in the app's templates folder.
131    :param url_prefix: A path to prepend to all of the blueprint's URLs,
132        to make them distinct from the rest of the app's routes.
133    :param subdomain: A subdomain that blueprint routes will match on by
134        default.
135    :param url_defaults: A dict of default values that blueprint routes
136        will receive by default.
137    :param root_path: By default, the blueprint will automatically this
138        based on ``import_name``. In certain situations this automatic
139        detection can fail, so the path can be specified manually
140        instead.
141    """
142
143    warn_on_modifications = False
144    _got_registered_once = False
145
146    #: Blueprint local JSON decoder class to use.
147    #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`.
148    json_encoder = None
149    #: Blueprint local JSON decoder class to use.
150    #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`.
151    json_decoder = None
152
153    # TODO remove the next three attrs when Sphinx :inherited-members: works
154    # https://github.com/sphinx-doc/sphinx/issues/741
155
156    #: The name of the package or module that this app belongs to. Do not
157    #: change this once it is set by the constructor.
158    import_name = None
159
160    #: Location of the template files to be added to the template lookup.
161    #: ``None`` if templates should not be added.
162    template_folder = None
163
164    #: Absolute path to the package on the filesystem. Used to look up
165    #: resources contained in the package.
166    root_path = None
167
168    def __init__(
169        self,
170        name,
171        import_name,
172        static_folder=None,
173        static_url_path=None,
174        template_folder=None,
175        url_prefix=None,
176        subdomain=None,
177        url_defaults=None,
178        root_path=None,
179        cli_group=_sentinel,
180    ):
181        _PackageBoundObject.__init__(
182            self, import_name, template_folder, root_path=root_path
183        )
184        self.name = name
185        self.url_prefix = url_prefix
186        self.subdomain = subdomain
187        self.static_folder = static_folder
188        self.static_url_path = static_url_path
189        self.deferred_functions = []
190        if url_defaults is None:
191            url_defaults = {}
192        self.url_values_defaults = url_defaults
193        self.cli_group = cli_group
194
195    def record(self, func):
196        """Registers a function that is called when the blueprint is
197        registered on the application.  This function is called with the
198        state as argument as returned by the :meth:`make_setup_state`
199        method.
200        """
201        if self._got_registered_once and self.warn_on_modifications:
202            from warnings import warn
203
204            warn(
205                Warning(
206                    "The blueprint was already registered once "
207                    "but is getting modified now.  These changes "
208                    "will not show up."
209                )
210            )
211        self.deferred_functions.append(func)
212
213    def record_once(self, func):
214        """Works like :meth:`record` but wraps the function in another
215        function that will ensure the function is only called once.  If the
216        blueprint is registered a second time on the application, the
217        function passed is not called.
218        """
219
220        def wrapper(state):
221            if state.first_registration:
222                func(state)
223
224        return self.record(update_wrapper(wrapper, func))
225
226    def make_setup_state(self, app, options, first_registration=False):
227        """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState`
228        object that is later passed to the register callback functions.
229        Subclasses can override this to return a subclass of the setup state.
230        """
231        return BlueprintSetupState(self, app, options, first_registration)
232
233    def register(self, app, options, first_registration=False):
234        """Called by :meth:`Flask.register_blueprint` to register all views
235        and callbacks registered on the blueprint with the application. Creates
236        a :class:`.BlueprintSetupState` and calls each :meth:`record` callback
237        with it.
238
239        :param app: The application this blueprint is being registered with.
240        :param options: Keyword arguments forwarded from
241            :meth:`~Flask.register_blueprint`.
242        :param first_registration: Whether this is the first time this
243            blueprint has been registered on the application.
244        """
245        self._got_registered_once = True
246        state = self.make_setup_state(app, options, first_registration)
247
248        if self.has_static_folder:
249            state.add_url_rule(
250                self.static_url_path + "/<path:filename>",
251                view_func=self.send_static_file,
252                endpoint="static",
253            )
254
255        for deferred in self.deferred_functions:
256            deferred(state)
257
258        cli_resolved_group = options.get("cli_group", self.cli_group)
259
260        if not self.cli.commands:
261            return
262
263        if cli_resolved_group is None:
264            app.cli.commands.update(self.cli.commands)
265        elif cli_resolved_group is _sentinel:
266            self.cli.name = self.name
267            app.cli.add_command(self.cli)
268        else:
269            self.cli.name = cli_resolved_group
270            app.cli.add_command(self.cli)
271
272    def route(self, rule, **options):
273        """Like :meth:`Flask.route` but for a blueprint.  The endpoint for the
274        :func:`url_for` function is prefixed with the name of the blueprint.
275        """
276
277        def decorator(f):
278            endpoint = options.pop("endpoint", f.__name__)
279            self.add_url_rule(rule, endpoint, f, **options)
280            return f
281
282        return decorator
283
284    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
285        """Like :meth:`Flask.add_url_rule` but for a blueprint.  The endpoint for
286        the :func:`url_for` function is prefixed with the name of the blueprint.
287        """
288        if endpoint:
289            assert "." not in endpoint, "Blueprint endpoints should not contain dots"
290        if view_func and hasattr(view_func, "__name__"):
291            assert (
292                "." not in view_func.__name__
293            ), "Blueprint view function name should not contain dots"
294        self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options))
295
296    def endpoint(self, endpoint):
297        """Like :meth:`Flask.endpoint` but for a blueprint.  This does not
298        prefix the endpoint with the blueprint name, this has to be done
299        explicitly by the user of this method.  If the endpoint is prefixed
300        with a `.` it will be registered to the current blueprint, otherwise
301        it's an application independent endpoint.
302        """
303
304        def decorator(f):
305            def register_endpoint(state):
306                state.app.view_functions[endpoint] = f
307
308            self.record_once(register_endpoint)
309            return f
310
311        return decorator
312
313    def app_template_filter(self, name=None):
314        """Register a custom template filter, available application wide.  Like
315        :meth:`Flask.template_filter` but for a blueprint.
316
317        :param name: the optional name of the filter, otherwise the
318                     function name will be used.
319        """
320
321        def decorator(f):
322            self.add_app_template_filter(f, name=name)
323            return f
324
325        return decorator
326
327    def add_app_template_filter(self, f, name=None):
328        """Register a custom template filter, available application wide.  Like
329        :meth:`Flask.add_template_filter` but for a blueprint.  Works exactly
330        like the :meth:`app_template_filter` decorator.
331
332        :param name: the optional name of the filter, otherwise the
333                     function name will be used.
334        """
335
336        def register_template(state):
337            state.app.jinja_env.filters[name or f.__name__] = f
338
339        self.record_once(register_template)
340
341    def app_template_test(self, name=None):
342        """Register a custom template test, available application wide.  Like
343        :meth:`Flask.template_test` but for a blueprint.
344
345        .. versionadded:: 0.10
346
347        :param name: the optional name of the test, otherwise the
348                     function name will be used.
349        """
350
351        def decorator(f):
352            self.add_app_template_test(f, name=name)
353            return f
354
355        return decorator
356
357    def add_app_template_test(self, f, name=None):
358        """Register a custom template test, available application wide.  Like
359        :meth:`Flask.add_template_test` but for a blueprint.  Works exactly
360        like the :meth:`app_template_test` decorator.
361
362        .. versionadded:: 0.10
363
364        :param name: the optional name of the test, otherwise the
365                     function name will be used.
366        """
367
368        def register_template(state):
369            state.app.jinja_env.tests[name or f.__name__] = f
370
371        self.record_once(register_template)
372
373    def app_template_global(self, name=None):
374        """Register a custom template global, available application wide.  Like
375        :meth:`Flask.template_global` but for a blueprint.
376
377        .. versionadded:: 0.10
378
379        :param name: the optional name of the global, otherwise the
380                     function name will be used.
381        """
382
383        def decorator(f):
384            self.add_app_template_global(f, name=name)
385            return f
386
387        return decorator
388
389    def add_app_template_global(self, f, name=None):
390        """Register a custom template global, available application wide.  Like
391        :meth:`Flask.add_template_global` but for a blueprint.  Works exactly
392        like the :meth:`app_template_global` decorator.
393
394        .. versionadded:: 0.10
395
396        :param name: the optional name of the global, otherwise the
397                     function name will be used.
398        """
399
400        def register_template(state):
401            state.app.jinja_env.globals[name or f.__name__] = f
402
403        self.record_once(register_template)
404
405    def before_request(self, f):
406        """Like :meth:`Flask.before_request` but for a blueprint.  This function
407        is only executed before each request that is handled by a function of
408        that blueprint.
409        """
410        self.record_once(
411            lambda s: s.app.before_request_funcs.setdefault(self.name, []).append(f)
412        )
413        return f
414
415    def before_app_request(self, f):
416        """Like :meth:`Flask.before_request`.  Such a function is executed
417        before each request, even if outside of a blueprint.
418        """
419        self.record_once(
420            lambda s: s.app.before_request_funcs.setdefault(None, []).append(f)
421        )
422        return f
423
424    def before_app_first_request(self, f):
425        """Like :meth:`Flask.before_first_request`.  Such a function is
426        executed before the first request to the application.
427        """
428        self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
429        return f
430
431    def after_request(self, f):
432        """Like :meth:`Flask.after_request` but for a blueprint.  This function
433        is only executed after each request that is handled by a function of
434        that blueprint.
435        """
436        self.record_once(
437            lambda s: s.app.after_request_funcs.setdefault(self.name, []).append(f)
438        )
439        return f
440
441    def after_app_request(self, f):
442        """Like :meth:`Flask.after_request` but for a blueprint.  Such a function
443        is executed after each request, even if outside of the blueprint.
444        """
445        self.record_once(
446            lambda s: s.app.after_request_funcs.setdefault(None, []).append(f)
447        )
448        return f
449
450    def teardown_request(self, f):
451        """Like :meth:`Flask.teardown_request` but for a blueprint.  This
452        function is only executed when tearing down requests handled by a
453        function of that blueprint.  Teardown request functions are executed
454        when the request context is popped, even when no actual request was
455        performed.
456        """
457        self.record_once(
458            lambda s: s.app.teardown_request_funcs.setdefault(self.name, []).append(f)
459        )
460        return f
461
462    def teardown_app_request(self, f):
463        """Like :meth:`Flask.teardown_request` but for a blueprint.  Such a
464        function is executed when tearing down each request, even if outside of
465        the blueprint.
466        """
467        self.record_once(
468            lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f)
469        )
470        return f
471
472    def context_processor(self, f):
473        """Like :meth:`Flask.context_processor` but for a blueprint.  This
474        function is only executed for requests handled by a blueprint.
475        """
476        self.record_once(
477            lambda s: s.app.template_context_processors.setdefault(
478                self.name, []
479            ).append(f)
480        )
481        return f
482
483    def app_context_processor(self, f):
484        """Like :meth:`Flask.context_processor` but for a blueprint.  Such a
485        function is executed each request, even if outside of the blueprint.
486        """
487        self.record_once(
488            lambda s: s.app.template_context_processors.setdefault(None, []).append(f)
489        )
490        return f
491
492    def app_errorhandler(self, code):
493        """Like :meth:`Flask.errorhandler` but for a blueprint.  This
494        handler is used for all requests, even if outside of the blueprint.
495        """
496
497        def decorator(f):
498            self.record_once(lambda s: s.app.errorhandler(code)(f))
499            return f
500
501        return decorator
502
503    def url_value_preprocessor(self, f):
504        """Registers a function as URL value preprocessor for this
505        blueprint.  It's called before the view functions are called and
506        can modify the url values provided.
507        """
508        self.record_once(
509            lambda s: s.app.url_value_preprocessors.setdefault(self.name, []).append(f)
510        )
511        return f
512
513    def url_defaults(self, f):
514        """Callback function for URL defaults for this blueprint.  It's called
515        with the endpoint and values and should update the values passed
516        in place.
517        """
518        self.record_once(
519            lambda s: s.app.url_default_functions.setdefault(self.name, []).append(f)
520        )
521        return f
522
523    def app_url_value_preprocessor(self, f):
524        """Same as :meth:`url_value_preprocessor` but application wide.
525        """
526        self.record_once(
527            lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f)
528        )
529        return f
530
531    def app_url_defaults(self, f):
532        """Same as :meth:`url_defaults` but application wide.
533        """
534        self.record_once(
535            lambda s: s.app.url_default_functions.setdefault(None, []).append(f)
536        )
537        return f
538
539    def errorhandler(self, code_or_exception):
540        """Registers an error handler that becomes active for this blueprint
541        only.  Please be aware that routing does not happen local to a
542        blueprint so an error handler for 404 usually is not handled by
543        a blueprint unless it is caused inside a view function.  Another
544        special case is the 500 internal server error which is always looked
545        up from the application.
546
547        Otherwise works as the :meth:`~flask.Flask.errorhandler` decorator
548        of the :class:`~flask.Flask` object.
549        """
550
551        def decorator(f):
552            self.record_once(
553                lambda s: s.app._register_error_handler(self.name, code_or_exception, f)
554            )
555            return f
556
557        return decorator
558
559    def register_error_handler(self, code_or_exception, f):
560        """Non-decorator version of the :meth:`errorhandler` error attach
561        function, akin to the :meth:`~flask.Flask.register_error_handler`
562        application-wide function of the :class:`~flask.Flask` object but
563        for error handlers limited to this blueprint.
564
565        .. versionadded:: 0.11
566        """
567        self.record_once(
568            lambda s: s.app._register_error_handler(self.name, code_or_exception, f)
569        )
570