Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/flask/blueprints.py: 32%

241 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

1import json 

2import os 

3import typing as t 

4from collections import defaultdict 

5from functools import update_wrapper 

6 

7from . import typing as ft 

8from .scaffold import _endpoint_from_view_func 

9from .scaffold import _sentinel 

10from .scaffold import Scaffold 

11from .scaffold import setupmethod 

12 

13if t.TYPE_CHECKING: # pragma: no cover 

14 from .app import Flask 

15 

16DeferredSetupFunction = t.Callable[["BlueprintSetupState"], t.Callable] 

17T_after_request = t.TypeVar("T_after_request", bound=ft.AfterRequestCallable) 

18T_before_first_request = t.TypeVar( 

19 "T_before_first_request", bound=ft.BeforeFirstRequestCallable 

20) 

21T_before_request = t.TypeVar("T_before_request", bound=ft.BeforeRequestCallable) 

22T_error_handler = t.TypeVar("T_error_handler", bound=ft.ErrorHandlerCallable) 

23T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable) 

24T_template_context_processor = t.TypeVar( 

25 "T_template_context_processor", bound=ft.TemplateContextProcessorCallable 

26) 

27T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable) 

28T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable) 

29T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable) 

30T_url_defaults = t.TypeVar("T_url_defaults", bound=ft.URLDefaultCallable) 

31T_url_value_preprocessor = t.TypeVar( 

32 "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable 

33) 

34 

35 

36class BlueprintSetupState: 

37 """Temporary holder object for registering a blueprint with the 

38 application. An instance of this class is created by the 

39 :meth:`~flask.Blueprint.make_setup_state` method and later passed 

40 to all register callback functions. 

41 """ 

42 

43 def __init__( 

44 self, 

45 blueprint: "Blueprint", 

46 app: "Flask", 

47 options: t.Any, 

48 first_registration: bool, 

49 ) -> None: 

50 #: a reference to the current application 

51 self.app = app 

52 

53 #: a reference to the blueprint that created this setup state. 

54 self.blueprint = blueprint 

55 

56 #: a dictionary with all options that were passed to the 

57 #: :meth:`~flask.Flask.register_blueprint` method. 

58 self.options = options 

59 

60 #: as blueprints can be registered multiple times with the 

61 #: application and not everything wants to be registered 

62 #: multiple times on it, this attribute can be used to figure 

63 #: out if the blueprint was registered in the past already. 

64 self.first_registration = first_registration 

65 

66 subdomain = self.options.get("subdomain") 

67 if subdomain is None: 

68 subdomain = self.blueprint.subdomain 

69 

70 #: The subdomain that the blueprint should be active for, ``None`` 

71 #: otherwise. 

72 self.subdomain = subdomain 

73 

74 url_prefix = self.options.get("url_prefix") 

75 if url_prefix is None: 

76 url_prefix = self.blueprint.url_prefix 

77 #: The prefix that should be used for all URLs defined on the 

78 #: blueprint. 

79 self.url_prefix = url_prefix 

80 

81 self.name = self.options.get("name", blueprint.name) 

82 self.name_prefix = self.options.get("name_prefix", "") 

83 

84 #: A dictionary with URL defaults that is added to each and every 

85 #: URL that was defined with the blueprint. 

86 self.url_defaults = dict(self.blueprint.url_values_defaults) 

87 self.url_defaults.update(self.options.get("url_defaults", ())) 

88 

89 def add_url_rule( 

90 self, 

91 rule: str, 

92 endpoint: t.Optional[str] = None, 

93 view_func: t.Optional[t.Callable] = None, 

94 **options: t.Any, 

95 ) -> None: 

96 """A helper method to register a rule (and optionally a view function) 

97 to the application. The endpoint is automatically prefixed with the 

98 blueprint's name. 

99 """ 

100 if self.url_prefix is not None: 

101 if rule: 

102 rule = "/".join((self.url_prefix.rstrip("/"), rule.lstrip("/"))) 

103 else: 

104 rule = self.url_prefix 

105 options.setdefault("subdomain", self.subdomain) 

106 if endpoint is None: 

107 endpoint = _endpoint_from_view_func(view_func) # type: ignore 

108 defaults = self.url_defaults 

109 if "defaults" in options: 

110 defaults = dict(defaults, **options.pop("defaults")) 

111 

112 self.app.add_url_rule( 

113 rule, 

114 f"{self.name_prefix}.{self.name}.{endpoint}".lstrip("."), 

115 view_func, 

116 defaults=defaults, 

117 **options, 

118 ) 

119 

120 

121class Blueprint(Scaffold): 

122 """Represents a blueprint, a collection of routes and other 

123 app-related functions that can be registered on a real application 

124 later. 

125 

126 A blueprint is an object that allows defining application functions 

127 without requiring an application object ahead of time. It uses the 

128 same decorators as :class:`~flask.Flask`, but defers the need for an 

129 application by recording them for later registration. 

130 

131 Decorating a function with a blueprint creates a deferred function 

132 that is called with :class:`~flask.blueprints.BlueprintSetupState` 

133 when the blueprint is registered on an application. 

134 

135 See :doc:`/blueprints` for more information. 

136 

137 :param name: The name of the blueprint. Will be prepended to each 

138 endpoint name. 

139 :param import_name: The name of the blueprint package, usually 

140 ``__name__``. This helps locate the ``root_path`` for the 

141 blueprint. 

142 :param static_folder: A folder with static files that should be 

143 served by the blueprint's static route. The path is relative to 

144 the blueprint's root path. Blueprint static files are disabled 

145 by default. 

146 :param static_url_path: The url to serve static files from. 

147 Defaults to ``static_folder``. If the blueprint does not have 

148 a ``url_prefix``, the app's static route will take precedence, 

149 and the blueprint's static files won't be accessible. 

150 :param template_folder: A folder with templates that should be added 

151 to the app's template search path. The path is relative to the 

152 blueprint's root path. Blueprint templates are disabled by 

153 default. Blueprint templates have a lower precedence than those 

154 in the app's templates folder. 

155 :param url_prefix: A path to prepend to all of the blueprint's URLs, 

156 to make them distinct from the rest of the app's routes. 

157 :param subdomain: A subdomain that blueprint routes will match on by 

158 default. 

159 :param url_defaults: A dict of default values that blueprint routes 

160 will receive by default. 

161 :param root_path: By default, the blueprint will automatically set 

162 this based on ``import_name``. In certain situations this 

163 automatic detection can fail, so the path can be specified 

164 manually instead. 

165 

166 .. versionchanged:: 1.1.0 

167 Blueprints have a ``cli`` group to register nested CLI commands. 

168 The ``cli_group`` parameter controls the name of the group under 

169 the ``flask`` command. 

170 

171 .. versionadded:: 0.7 

172 """ 

173 

174 _got_registered_once = False 

175 

176 _json_encoder: t.Union[t.Type[json.JSONEncoder], None] = None 

177 _json_decoder: t.Union[t.Type[json.JSONDecoder], None] = None 

178 

179 @property 

180 def json_encoder( 

181 self, 

182 ) -> t.Union[t.Type[json.JSONEncoder], None]: 

183 """Blueprint-local JSON encoder class to use. Set to ``None`` to use the app's. 

184 

185 .. deprecated:: 2.2 

186 Will be removed in Flask 2.3. Customize 

187 :attr:`json_provider_class` instead. 

188 

189 .. versionadded:: 0.10 

190 """ 

191 import warnings 

192 

193 warnings.warn( 

194 "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." 

195 " Customize 'app.json_provider_class' or 'app.json' instead.", 

196 DeprecationWarning, 

197 stacklevel=2, 

198 ) 

199 return self._json_encoder 

200 

201 @json_encoder.setter 

202 def json_encoder(self, value: t.Union[t.Type[json.JSONEncoder], None]) -> None: 

203 import warnings 

204 

205 warnings.warn( 

206 "'bp.json_encoder' is deprecated and will be removed in Flask 2.3." 

207 " Customize 'app.json_provider_class' or 'app.json' instead.", 

208 DeprecationWarning, 

209 stacklevel=2, 

210 ) 

211 self._json_encoder = value 

212 

213 @property 

214 def json_decoder( 

215 self, 

216 ) -> t.Union[t.Type[json.JSONDecoder], None]: 

217 """Blueprint-local JSON decoder class to use. Set to ``None`` to use the app's. 

218 

219 .. deprecated:: 2.2 

220 Will be removed in Flask 2.3. Customize 

221 :attr:`json_provider_class` instead. 

222 

223 .. versionadded:: 0.10 

224 """ 

225 import warnings 

226 

227 warnings.warn( 

228 "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." 

229 " Customize 'app.json_provider_class' or 'app.json' instead.", 

230 DeprecationWarning, 

231 stacklevel=2, 

232 ) 

233 return self._json_decoder 

234 

235 @json_decoder.setter 

236 def json_decoder(self, value: t.Union[t.Type[json.JSONDecoder], None]) -> None: 

237 import warnings 

238 

239 warnings.warn( 

240 "'bp.json_decoder' is deprecated and will be removed in Flask 2.3." 

241 " Customize 'app.json_provider_class' or 'app.json' instead.", 

242 DeprecationWarning, 

243 stacklevel=2, 

244 ) 

245 self._json_decoder = value 

246 

247 def __init__( 

248 self, 

249 name: str, 

250 import_name: str, 

251 static_folder: t.Optional[t.Union[str, os.PathLike]] = None, 

252 static_url_path: t.Optional[str] = None, 

253 template_folder: t.Optional[t.Union[str, os.PathLike]] = None, 

254 url_prefix: t.Optional[str] = None, 

255 subdomain: t.Optional[str] = None, 

256 url_defaults: t.Optional[dict] = None, 

257 root_path: t.Optional[str] = None, 

258 cli_group: t.Optional[str] = _sentinel, # type: ignore 

259 ): 

260 super().__init__( 

261 import_name=import_name, 

262 static_folder=static_folder, 

263 static_url_path=static_url_path, 

264 template_folder=template_folder, 

265 root_path=root_path, 

266 ) 

267 

268 if "." in name: 

269 raise ValueError("'name' may not contain a dot '.' character.") 

270 

271 self.name = name 

272 self.url_prefix = url_prefix 

273 self.subdomain = subdomain 

274 self.deferred_functions: t.List[DeferredSetupFunction] = [] 

275 

276 if url_defaults is None: 

277 url_defaults = {} 

278 

279 self.url_values_defaults = url_defaults 

280 self.cli_group = cli_group 

281 self._blueprints: t.List[t.Tuple["Blueprint", dict]] = [] 

282 

283 def _check_setup_finished(self, f_name: str) -> None: 

284 if self._got_registered_once: 

285 import warnings 

286 

287 warnings.warn( 

288 f"The setup method '{f_name}' can no longer be called on" 

289 f" the blueprint '{self.name}'. It has already been" 

290 " registered at least once, any changes will not be" 

291 " applied consistently.\n" 

292 "Make sure all imports, decorators, functions, etc." 

293 " needed to set up the blueprint are done before" 

294 " registering it.\n" 

295 "This warning will become an exception in Flask 2.3.", 

296 UserWarning, 

297 stacklevel=3, 

298 ) 

299 

300 @setupmethod 

301 def record(self, func: t.Callable) -> None: 

302 """Registers a function that is called when the blueprint is 

303 registered on the application. This function is called with the 

304 state as argument as returned by the :meth:`make_setup_state` 

305 method. 

306 """ 

307 self.deferred_functions.append(func) 

308 

309 @setupmethod 

310 def record_once(self, func: t.Callable) -> None: 

311 """Works like :meth:`record` but wraps the function in another 

312 function that will ensure the function is only called once. If the 

313 blueprint is registered a second time on the application, the 

314 function passed is not called. 

315 """ 

316 

317 def wrapper(state: BlueprintSetupState) -> None: 

318 if state.first_registration: 

319 func(state) 

320 

321 self.record(update_wrapper(wrapper, func)) 

322 

323 def make_setup_state( 

324 self, app: "Flask", options: dict, first_registration: bool = False 

325 ) -> BlueprintSetupState: 

326 """Creates an instance of :meth:`~flask.blueprints.BlueprintSetupState` 

327 object that is later passed to the register callback functions. 

328 Subclasses can override this to return a subclass of the setup state. 

329 """ 

330 return BlueprintSetupState(self, app, options, first_registration) 

331 

332 @setupmethod 

333 def register_blueprint(self, blueprint: "Blueprint", **options: t.Any) -> None: 

334 """Register a :class:`~flask.Blueprint` on this blueprint. Keyword 

335 arguments passed to this method will override the defaults set 

336 on the blueprint. 

337 

338 .. versionchanged:: 2.0.1 

339 The ``name`` option can be used to change the (pre-dotted) 

340 name the blueprint is registered with. This allows the same 

341 blueprint to be registered multiple times with unique names 

342 for ``url_for``. 

343 

344 .. versionadded:: 2.0 

345 """ 

346 if blueprint is self: 

347 raise ValueError("Cannot register a blueprint on itself") 

348 self._blueprints.append((blueprint, options)) 

349 

350 def register(self, app: "Flask", options: dict) -> None: 

351 """Called by :meth:`Flask.register_blueprint` to register all 

352 views and callbacks registered on the blueprint with the 

353 application. Creates a :class:`.BlueprintSetupState` and calls 

354 each :meth:`record` callback with it. 

355 

356 :param app: The application this blueprint is being registered 

357 with. 

358 :param options: Keyword arguments forwarded from 

359 :meth:`~Flask.register_blueprint`. 

360 

361 .. versionchanged:: 2.0.1 

362 Nested blueprints are registered with their dotted name. 

363 This allows different blueprints with the same name to be 

364 nested at different locations. 

365 

366 .. versionchanged:: 2.0.1 

367 The ``name`` option can be used to change the (pre-dotted) 

368 name the blueprint is registered with. This allows the same 

369 blueprint to be registered multiple times with unique names 

370 for ``url_for``. 

371 

372 .. versionchanged:: 2.0.1 

373 Registering the same blueprint with the same name multiple 

374 times is deprecated and will become an error in Flask 2.1. 

375 """ 

376 name_prefix = options.get("name_prefix", "") 

377 self_name = options.get("name", self.name) 

378 name = f"{name_prefix}.{self_name}".lstrip(".") 

379 

380 if name in app.blueprints: 

381 bp_desc = "this" if app.blueprints[name] is self else "a different" 

382 existing_at = f" '{name}'" if self_name != name else "" 

383 

384 raise ValueError( 

385 f"The name '{self_name}' is already registered for" 

386 f" {bp_desc} blueprint{existing_at}. Use 'name=' to" 

387 f" provide a unique name." 

388 ) 

389 

390 first_bp_registration = not any(bp is self for bp in app.blueprints.values()) 

391 first_name_registration = name not in app.blueprints 

392 

393 app.blueprints[name] = self 

394 self._got_registered_once = True 

395 state = self.make_setup_state(app, options, first_bp_registration) 

396 

397 if self.has_static_folder: 

398 state.add_url_rule( 

399 f"{self.static_url_path}/<path:filename>", 

400 view_func=self.send_static_file, 

401 endpoint="static", 

402 ) 

403 

404 # Merge blueprint data into parent. 

405 if first_bp_registration or first_name_registration: 

406 

407 def extend(bp_dict, parent_dict): 

408 for key, values in bp_dict.items(): 

409 key = name if key is None else f"{name}.{key}" 

410 parent_dict[key].extend(values) 

411 

412 for key, value in self.error_handler_spec.items(): 

413 key = name if key is None else f"{name}.{key}" 

414 value = defaultdict( 

415 dict, 

416 { 

417 code: { 

418 exc_class: func for exc_class, func in code_values.items() 

419 } 

420 for code, code_values in value.items() 

421 }, 

422 ) 

423 app.error_handler_spec[key] = value 

424 

425 for endpoint, func in self.view_functions.items(): 

426 app.view_functions[endpoint] = func 

427 

428 extend(self.before_request_funcs, app.before_request_funcs) 

429 extend(self.after_request_funcs, app.after_request_funcs) 

430 extend( 

431 self.teardown_request_funcs, 

432 app.teardown_request_funcs, 

433 ) 

434 extend(self.url_default_functions, app.url_default_functions) 

435 extend(self.url_value_preprocessors, app.url_value_preprocessors) 

436 extend(self.template_context_processors, app.template_context_processors) 

437 

438 for deferred in self.deferred_functions: 

439 deferred(state) 

440 

441 cli_resolved_group = options.get("cli_group", self.cli_group) 

442 

443 if self.cli.commands: 

444 if cli_resolved_group is None: 

445 app.cli.commands.update(self.cli.commands) 

446 elif cli_resolved_group is _sentinel: 

447 self.cli.name = name 

448 app.cli.add_command(self.cli) 

449 else: 

450 self.cli.name = cli_resolved_group 

451 app.cli.add_command(self.cli) 

452 

453 for blueprint, bp_options in self._blueprints: 

454 bp_options = bp_options.copy() 

455 bp_url_prefix = bp_options.get("url_prefix") 

456 

457 if bp_url_prefix is None: 

458 bp_url_prefix = blueprint.url_prefix 

459 

460 if state.url_prefix is not None and bp_url_prefix is not None: 

461 bp_options["url_prefix"] = ( 

462 state.url_prefix.rstrip("/") + "/" + bp_url_prefix.lstrip("/") 

463 ) 

464 elif bp_url_prefix is not None: 

465 bp_options["url_prefix"] = bp_url_prefix 

466 elif state.url_prefix is not None: 

467 bp_options["url_prefix"] = state.url_prefix 

468 

469 bp_options["name_prefix"] = name 

470 blueprint.register(app, bp_options) 

471 

472 @setupmethod 

473 def add_url_rule( 

474 self, 

475 rule: str, 

476 endpoint: t.Optional[str] = None, 

477 view_func: t.Optional[ft.RouteCallable] = None, 

478 provide_automatic_options: t.Optional[bool] = None, 

479 **options: t.Any, 

480 ) -> None: 

481 """Register a URL rule with the blueprint. See :meth:`.Flask.add_url_rule` for 

482 full documentation. 

483 

484 The URL rule is prefixed with the blueprint's URL prefix. The endpoint name, 

485 used with :func:`url_for`, is prefixed with the blueprint's name. 

486 """ 

487 if endpoint and "." in endpoint: 

488 raise ValueError("'endpoint' may not contain a dot '.' character.") 

489 

490 if view_func and hasattr(view_func, "__name__") and "." in view_func.__name__: 

491 raise ValueError("'view_func' name may not contain a dot '.' character.") 

492 

493 self.record( 

494 lambda s: s.add_url_rule( 

495 rule, 

496 endpoint, 

497 view_func, 

498 provide_automatic_options=provide_automatic_options, 

499 **options, 

500 ) 

501 ) 

502 

503 @setupmethod 

504 def app_template_filter( 

505 self, name: t.Optional[str] = None 

506 ) -> t.Callable[[T_template_filter], T_template_filter]: 

507 """Register a template filter, available in any template rendered by the 

508 application. Equivalent to :meth:`.Flask.template_filter`. 

509 

510 :param name: the optional name of the filter, otherwise the 

511 function name will be used. 

512 """ 

513 

514 def decorator(f: T_template_filter) -> T_template_filter: 

515 self.add_app_template_filter(f, name=name) 

516 return f 

517 

518 return decorator 

519 

520 @setupmethod 

521 def add_app_template_filter( 

522 self, f: ft.TemplateFilterCallable, name: t.Optional[str] = None 

523 ) -> None: 

524 """Register a template filter, available in any template rendered by the 

525 application. Works like the :meth:`app_template_filter` decorator. Equivalent to 

526 :meth:`.Flask.add_template_filter`. 

527 

528 :param name: the optional name of the filter, otherwise the 

529 function name will be used. 

530 """ 

531 

532 def register_template(state: BlueprintSetupState) -> None: 

533 state.app.jinja_env.filters[name or f.__name__] = f 

534 

535 self.record_once(register_template) 

536 

537 @setupmethod 

538 def app_template_test( 

539 self, name: t.Optional[str] = None 

540 ) -> t.Callable[[T_template_test], T_template_test]: 

541 """Register a template test, available in any template rendered by the 

542 application. Equivalent to :meth:`.Flask.template_test`. 

543 

544 .. versionadded:: 0.10 

545 

546 :param name: the optional name of the test, otherwise the 

547 function name will be used. 

548 """ 

549 

550 def decorator(f: T_template_test) -> T_template_test: 

551 self.add_app_template_test(f, name=name) 

552 return f 

553 

554 return decorator 

555 

556 @setupmethod 

557 def add_app_template_test( 

558 self, f: ft.TemplateTestCallable, name: t.Optional[str] = None 

559 ) -> None: 

560 """Register a template test, available in any template rendered by the 

561 application. Works like the :meth:`app_template_test` decorator. Equivalent to 

562 :meth:`.Flask.add_template_test`. 

563 

564 .. versionadded:: 0.10 

565 

566 :param name: the optional name of the test, otherwise the 

567 function name will be used. 

568 """ 

569 

570 def register_template(state: BlueprintSetupState) -> None: 

571 state.app.jinja_env.tests[name or f.__name__] = f 

572 

573 self.record_once(register_template) 

574 

575 @setupmethod 

576 def app_template_global( 

577 self, name: t.Optional[str] = None 

578 ) -> t.Callable[[T_template_global], T_template_global]: 

579 """Register a template global, available in any template rendered by the 

580 application. Equivalent to :meth:`.Flask.template_global`. 

581 

582 .. versionadded:: 0.10 

583 

584 :param name: the optional name of the global, otherwise the 

585 function name will be used. 

586 """ 

587 

588 def decorator(f: T_template_global) -> T_template_global: 

589 self.add_app_template_global(f, name=name) 

590 return f 

591 

592 return decorator 

593 

594 @setupmethod 

595 def add_app_template_global( 

596 self, f: ft.TemplateGlobalCallable, name: t.Optional[str] = None 

597 ) -> None: 

598 """Register a template global, available in any template rendered by the 

599 application. Works like the :meth:`app_template_global` decorator. Equivalent to 

600 :meth:`.Flask.add_template_global`. 

601 

602 .. versionadded:: 0.10 

603 

604 :param name: the optional name of the global, otherwise the 

605 function name will be used. 

606 """ 

607 

608 def register_template(state: BlueprintSetupState) -> None: 

609 state.app.jinja_env.globals[name or f.__name__] = f 

610 

611 self.record_once(register_template) 

612 

613 @setupmethod 

614 def before_app_request(self, f: T_before_request) -> T_before_request: 

615 """Like :meth:`before_request`, but before every request, not only those handled 

616 by the blueprint. Equivalent to :meth:`.Flask.before_request`. 

617 """ 

618 self.record_once( 

619 lambda s: s.app.before_request_funcs.setdefault(None, []).append(f) 

620 ) 

621 return f 

622 

623 @setupmethod 

624 def before_app_first_request( 

625 self, f: T_before_first_request 

626 ) -> T_before_first_request: 

627 """Register a function to run before the first request to the application is 

628 handled by the worker. Equivalent to :meth:`.Flask.before_first_request`. 

629 

630 .. deprecated:: 2.2 

631 Will be removed in Flask 2.3. Run setup code when creating 

632 the application instead. 

633 """ 

634 import warnings 

635 

636 warnings.warn( 

637 "'before_app_first_request' is deprecated and will be" 

638 " removed in Flask 2.3. Use 'record_once' instead to run" 

639 " setup code when registering the blueprint.", 

640 DeprecationWarning, 

641 stacklevel=2, 

642 ) 

643 self.record_once(lambda s: s.app.before_first_request_funcs.append(f)) 

644 return f 

645 

646 @setupmethod 

647 def after_app_request(self, f: T_after_request) -> T_after_request: 

648 """Like :meth:`after_request`, but after every request, not only those handled 

649 by the blueprint. Equivalent to :meth:`.Flask.after_request`. 

650 """ 

651 self.record_once( 

652 lambda s: s.app.after_request_funcs.setdefault(None, []).append(f) 

653 ) 

654 return f 

655 

656 @setupmethod 

657 def teardown_app_request(self, f: T_teardown) -> T_teardown: 

658 """Like :meth:`teardown_request`, but after every request, not only those 

659 handled by the blueprint. Equivalent to :meth:`.Flask.teardown_request`. 

660 """ 

661 self.record_once( 

662 lambda s: s.app.teardown_request_funcs.setdefault(None, []).append(f) 

663 ) 

664 return f 

665 

666 @setupmethod 

667 def app_context_processor( 

668 self, f: T_template_context_processor 

669 ) -> T_template_context_processor: 

670 """Like :meth:`context_processor`, but for templates rendered by every view, not 

671 only by the blueprint. Equivalent to :meth:`.Flask.context_processor`. 

672 """ 

673 self.record_once( 

674 lambda s: s.app.template_context_processors.setdefault(None, []).append(f) 

675 ) 

676 return f 

677 

678 @setupmethod 

679 def app_errorhandler( 

680 self, code: t.Union[t.Type[Exception], int] 

681 ) -> t.Callable[[T_error_handler], T_error_handler]: 

682 """Like :meth:`errorhandler`, but for every request, not only those handled by 

683 the blueprint. Equivalent to :meth:`.Flask.errorhandler`. 

684 """ 

685 

686 def decorator(f: T_error_handler) -> T_error_handler: 

687 self.record_once(lambda s: s.app.errorhandler(code)(f)) 

688 return f 

689 

690 return decorator 

691 

692 @setupmethod 

693 def app_url_value_preprocessor( 

694 self, f: T_url_value_preprocessor 

695 ) -> T_url_value_preprocessor: 

696 """Like :meth:`url_value_preprocessor`, but for every request, not only those 

697 handled by the blueprint. Equivalent to :meth:`.Flask.url_value_preprocessor`. 

698 """ 

699 self.record_once( 

700 lambda s: s.app.url_value_preprocessors.setdefault(None, []).append(f) 

701 ) 

702 return f 

703 

704 @setupmethod 

705 def app_url_defaults(self, f: T_url_defaults) -> T_url_defaults: 

706 """Like :meth:`url_defaults`, but for every request, not only those handled by 

707 the blueprint. Equivalent to :meth:`.Flask.url_defaults`. 

708 """ 

709 self.record_once( 

710 lambda s: s.app.url_default_functions.setdefault(None, []).append(f) 

711 ) 

712 return f