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