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

252 statements  

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

1from __future__ import annotations 

2 

3import importlib.util 

4import os 

5import pathlib 

6import sys 

7import typing as t 

8from collections import defaultdict 

9from datetime import timedelta 

10from functools import update_wrapper 

11 

12from jinja2 import FileSystemLoader 

13from werkzeug.exceptions import default_exceptions 

14from werkzeug.exceptions import HTTPException 

15from werkzeug.utils import cached_property 

16 

17from . import typing as ft 

18from .cli import AppGroup 

19from .globals import current_app 

20from .helpers import get_root_path 

21from .helpers import send_from_directory 

22from .templating import _default_template_ctx_processor 

23 

24if t.TYPE_CHECKING: # pragma: no cover 

25 from .wrappers import Response 

26 

27# a singleton sentinel value for parameter defaults 

28_sentinel = object() 

29 

30F = t.TypeVar("F", bound=t.Callable[..., t.Any]) 

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

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

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

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

35T_template_context_processor = t.TypeVar( 

36 "T_template_context_processor", bound=ft.TemplateContextProcessorCallable 

37) 

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

39T_url_value_preprocessor = t.TypeVar( 

40 "T_url_value_preprocessor", bound=ft.URLValuePreprocessorCallable 

41) 

42T_route = t.TypeVar("T_route", bound=ft.RouteCallable) 

43 

44 

45def setupmethod(f: F) -> F: 

46 f_name = f.__name__ 

47 

48 def wrapper_func(self, *args: t.Any, **kwargs: t.Any) -> t.Any: 

49 self._check_setup_finished(f_name) 

50 return f(self, *args, **kwargs) 

51 

52 return t.cast(F, update_wrapper(wrapper_func, f)) 

53 

54 

55class Scaffold: 

56 """Common behavior shared between :class:`~flask.Flask` and 

57 :class:`~flask.blueprints.Blueprint`. 

58 

59 :param import_name: The import name of the module where this object 

60 is defined. Usually :attr:`__name__` should be used. 

61 :param static_folder: Path to a folder of static files to serve. 

62 If this is set, a static route will be added. 

63 :param static_url_path: URL prefix for the static route. 

64 :param template_folder: Path to a folder containing template files. 

65 for rendering. If this is set, a Jinja loader will be added. 

66 :param root_path: The path that static, template, and resource files 

67 are relative to. Typically not set, it is discovered based on 

68 the ``import_name``. 

69 

70 .. versionadded:: 2.0 

71 """ 

72 

73 name: str 

74 _static_folder: str | None = None 

75 _static_url_path: str | None = None 

76 

77 def __init__( 

78 self, 

79 import_name: str, 

80 static_folder: str | os.PathLike | None = None, 

81 static_url_path: str | None = None, 

82 template_folder: str | os.PathLike | None = None, 

83 root_path: str | None = None, 

84 ): 

85 #: The name of the package or module that this object belongs 

86 #: to. Do not change this once it is set by the constructor. 

87 self.import_name = import_name 

88 

89 self.static_folder = static_folder # type: ignore 

90 self.static_url_path = static_url_path 

91 

92 #: The path to the templates folder, relative to 

93 #: :attr:`root_path`, to add to the template loader. ``None`` if 

94 #: templates should not be added. 

95 self.template_folder = template_folder 

96 

97 if root_path is None: 

98 root_path = get_root_path(self.import_name) 

99 

100 #: Absolute path to the package on the filesystem. Used to look 

101 #: up resources contained in the package. 

102 self.root_path = root_path 

103 

104 #: The Click command group for registering CLI commands for this 

105 #: object. The commands are available from the ``flask`` command 

106 #: once the application has been discovered and blueprints have 

107 #: been registered. 

108 self.cli = AppGroup() 

109 

110 #: A dictionary mapping endpoint names to view functions. 

111 #: 

112 #: To register a view function, use the :meth:`route` decorator. 

113 #: 

114 #: This data structure is internal. It should not be modified 

115 #: directly and its format may change at any time. 

116 self.view_functions: dict[str, t.Callable] = {} 

117 

118 #: A data structure of registered error handlers, in the format 

119 #: ``{scope: {code: {class: handler}}}``. The ``scope`` key is 

120 #: the name of a blueprint the handlers are active for, or 

121 #: ``None`` for all requests. The ``code`` key is the HTTP 

122 #: status code for ``HTTPException``, or ``None`` for 

123 #: other exceptions. The innermost dictionary maps exception 

124 #: classes to handler functions. 

125 #: 

126 #: To register an error handler, use the :meth:`errorhandler` 

127 #: decorator. 

128 #: 

129 #: This data structure is internal. It should not be modified 

130 #: directly and its format may change at any time. 

131 self.error_handler_spec: dict[ 

132 ft.AppOrBlueprintKey, 

133 dict[int | None, dict[type[Exception], ft.ErrorHandlerCallable]], 

134 ] = defaultdict(lambda: defaultdict(dict)) 

135 

136 #: A data structure of functions to call at the beginning of 

137 #: each request, in the format ``{scope: [functions]}``. The 

138 #: ``scope`` key is the name of a blueprint the functions are 

139 #: active for, or ``None`` for all requests. 

140 #: 

141 #: To register a function, use the :meth:`before_request` 

142 #: decorator. 

143 #: 

144 #: This data structure is internal. It should not be modified 

145 #: directly and its format may change at any time. 

146 self.before_request_funcs: dict[ 

147 ft.AppOrBlueprintKey, list[ft.BeforeRequestCallable] 

148 ] = defaultdict(list) 

149 

150 #: A data structure of functions to call at the end of each 

151 #: request, in the format ``{scope: [functions]}``. The 

152 #: ``scope`` key is the name of a blueprint the functions are 

153 #: active for, or ``None`` for all requests. 

154 #: 

155 #: To register a function, use the :meth:`after_request` 

156 #: decorator. 

157 #: 

158 #: This data structure is internal. It should not be modified 

159 #: directly and its format may change at any time. 

160 self.after_request_funcs: dict[ 

161 ft.AppOrBlueprintKey, list[ft.AfterRequestCallable] 

162 ] = defaultdict(list) 

163 

164 #: A data structure of functions to call at the end of each 

165 #: request even if an exception is raised, in the format 

166 #: ``{scope: [functions]}``. The ``scope`` key is the name of a 

167 #: blueprint the functions are active for, or ``None`` for all 

168 #: requests. 

169 #: 

170 #: To register a function, use the :meth:`teardown_request` 

171 #: decorator. 

172 #: 

173 #: This data structure is internal. It should not be modified 

174 #: directly and its format may change at any time. 

175 self.teardown_request_funcs: dict[ 

176 ft.AppOrBlueprintKey, list[ft.TeardownCallable] 

177 ] = defaultdict(list) 

178 

179 #: A data structure of functions to call to pass extra context 

180 #: values when rendering templates, in the format 

181 #: ``{scope: [functions]}``. The ``scope`` key is the name of a 

182 #: blueprint the functions are active for, or ``None`` for all 

183 #: requests. 

184 #: 

185 #: To register a function, use the :meth:`context_processor` 

186 #: decorator. 

187 #: 

188 #: This data structure is internal. It should not be modified 

189 #: directly and its format may change at any time. 

190 self.template_context_processors: dict[ 

191 ft.AppOrBlueprintKey, list[ft.TemplateContextProcessorCallable] 

192 ] = defaultdict(list, {None: [_default_template_ctx_processor]}) 

193 

194 #: A data structure of functions to call to modify the keyword 

195 #: arguments passed to the view function, in the format 

196 #: ``{scope: [functions]}``. The ``scope`` key is the name of a 

197 #: blueprint the functions are active for, or ``None`` for all 

198 #: requests. 

199 #: 

200 #: To register a function, use the 

201 #: :meth:`url_value_preprocessor` decorator. 

202 #: 

203 #: This data structure is internal. It should not be modified 

204 #: directly and its format may change at any time. 

205 self.url_value_preprocessors: dict[ 

206 ft.AppOrBlueprintKey, 

207 list[ft.URLValuePreprocessorCallable], 

208 ] = defaultdict(list) 

209 

210 #: A data structure of functions to call to modify the keyword 

211 #: arguments when generating URLs, in the format 

212 #: ``{scope: [functions]}``. The ``scope`` key is the name of a 

213 #: blueprint the functions are active for, or ``None`` for all 

214 #: requests. 

215 #: 

216 #: To register a function, use the :meth:`url_defaults` 

217 #: decorator. 

218 #: 

219 #: This data structure is internal. It should not be modified 

220 #: directly and its format may change at any time. 

221 self.url_default_functions: dict[ 

222 ft.AppOrBlueprintKey, list[ft.URLDefaultCallable] 

223 ] = defaultdict(list) 

224 

225 def __repr__(self) -> str: 

226 return f"<{type(self).__name__} {self.name!r}>" 

227 

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

229 raise NotImplementedError 

230 

231 @property 

232 def static_folder(self) -> str | None: 

233 """The absolute path to the configured static folder. ``None`` 

234 if no static folder is set. 

235 """ 

236 if self._static_folder is not None: 

237 return os.path.join(self.root_path, self._static_folder) 

238 else: 

239 return None 

240 

241 @static_folder.setter 

242 def static_folder(self, value: str | os.PathLike | None) -> None: 

243 if value is not None: 

244 value = os.fspath(value).rstrip(r"\/") 

245 

246 self._static_folder = value 

247 

248 @property 

249 def has_static_folder(self) -> bool: 

250 """``True`` if :attr:`static_folder` is set. 

251 

252 .. versionadded:: 0.5 

253 """ 

254 return self.static_folder is not None 

255 

256 @property 

257 def static_url_path(self) -> str | None: 

258 """The URL prefix that the static route will be accessible from. 

259 

260 If it was not configured during init, it is derived from 

261 :attr:`static_folder`. 

262 """ 

263 if self._static_url_path is not None: 

264 return self._static_url_path 

265 

266 if self.static_folder is not None: 

267 basename = os.path.basename(self.static_folder) 

268 return f"/{basename}".rstrip("/") 

269 

270 return None 

271 

272 @static_url_path.setter 

273 def static_url_path(self, value: str | None) -> None: 

274 if value is not None: 

275 value = value.rstrip("/") 

276 

277 self._static_url_path = value 

278 

279 def get_send_file_max_age(self, filename: str | None) -> int | None: 

280 """Used by :func:`send_file` to determine the ``max_age`` cache 

281 value for a given file path if it wasn't passed. 

282 

283 By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from 

284 the configuration of :data:`~flask.current_app`. This defaults 

285 to ``None``, which tells the browser to use conditional requests 

286 instead of a timed cache, which is usually preferable. 

287 

288 .. versionchanged:: 2.0 

289 The default configuration is ``None`` instead of 12 hours. 

290 

291 .. versionadded:: 0.9 

292 """ 

293 value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"] 

294 

295 if value is None: 

296 return None 

297 

298 if isinstance(value, timedelta): 

299 return int(value.total_seconds()) 

300 

301 return value 

302 

303 def send_static_file(self, filename: str) -> Response: 

304 """The view function used to serve files from 

305 :attr:`static_folder`. A route is automatically registered for 

306 this view at :attr:`static_url_path` if :attr:`static_folder` is 

307 set. 

308 

309 .. versionadded:: 0.5 

310 """ 

311 if not self.has_static_folder: 

312 raise RuntimeError("'static_folder' must be set to serve static_files.") 

313 

314 # send_file only knows to call get_send_file_max_age on the app, 

315 # call it here so it works for blueprints too. 

316 max_age = self.get_send_file_max_age(filename) 

317 return send_from_directory( 

318 t.cast(str, self.static_folder), filename, max_age=max_age 

319 ) 

320 

321 @cached_property 

322 def jinja_loader(self) -> FileSystemLoader | None: 

323 """The Jinja loader for this object's templates. By default this 

324 is a class :class:`jinja2.loaders.FileSystemLoader` to 

325 :attr:`template_folder` if it is set. 

326 

327 .. versionadded:: 0.5 

328 """ 

329 if self.template_folder is not None: 

330 return FileSystemLoader(os.path.join(self.root_path, self.template_folder)) 

331 else: 

332 return None 

333 

334 def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]: 

335 """Open a resource file relative to :attr:`root_path` for 

336 reading. 

337 

338 For example, if the file ``schema.sql`` is next to the file 

339 ``app.py`` where the ``Flask`` app is defined, it can be opened 

340 with: 

341 

342 .. code-block:: python 

343 

344 with app.open_resource("schema.sql") as f: 

345 conn.executescript(f.read()) 

346 

347 :param resource: Path to the resource relative to 

348 :attr:`root_path`. 

349 :param mode: Open the file in this mode. Only reading is 

350 supported, valid values are "r" (or "rt") and "rb". 

351 """ 

352 if mode not in {"r", "rt", "rb"}: 

353 raise ValueError("Resources can only be opened for reading.") 

354 

355 return open(os.path.join(self.root_path, resource), mode) 

356 

357 def _method_route( 

358 self, 

359 method: str, 

360 rule: str, 

361 options: dict, 

362 ) -> t.Callable[[T_route], T_route]: 

363 if "methods" in options: 

364 raise TypeError("Use the 'route' decorator to use the 'methods' argument.") 

365 

366 return self.route(rule, methods=[method], **options) 

367 

368 @setupmethod 

369 def get(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

370 """Shortcut for :meth:`route` with ``methods=["GET"]``. 

371 

372 .. versionadded:: 2.0 

373 """ 

374 return self._method_route("GET", rule, options) 

375 

376 @setupmethod 

377 def post(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

378 """Shortcut for :meth:`route` with ``methods=["POST"]``. 

379 

380 .. versionadded:: 2.0 

381 """ 

382 return self._method_route("POST", rule, options) 

383 

384 @setupmethod 

385 def put(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

386 """Shortcut for :meth:`route` with ``methods=["PUT"]``. 

387 

388 .. versionadded:: 2.0 

389 """ 

390 return self._method_route("PUT", rule, options) 

391 

392 @setupmethod 

393 def delete(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

394 """Shortcut for :meth:`route` with ``methods=["DELETE"]``. 

395 

396 .. versionadded:: 2.0 

397 """ 

398 return self._method_route("DELETE", rule, options) 

399 

400 @setupmethod 

401 def patch(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

402 """Shortcut for :meth:`route` with ``methods=["PATCH"]``. 

403 

404 .. versionadded:: 2.0 

405 """ 

406 return self._method_route("PATCH", rule, options) 

407 

408 @setupmethod 

409 def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]: 

410 """Decorate a view function to register it with the given URL 

411 rule and options. Calls :meth:`add_url_rule`, which has more 

412 details about the implementation. 

413 

414 .. code-block:: python 

415 

416 @app.route("/") 

417 def index(): 

418 return "Hello, World!" 

419 

420 See :ref:`url-route-registrations`. 

421 

422 The endpoint name for the route defaults to the name of the view 

423 function if the ``endpoint`` parameter isn't passed. 

424 

425 The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` and 

426 ``OPTIONS`` are added automatically. 

427 

428 :param rule: The URL rule string. 

429 :param options: Extra options passed to the 

430 :class:`~werkzeug.routing.Rule` object. 

431 """ 

432 

433 def decorator(f: T_route) -> T_route: 

434 endpoint = options.pop("endpoint", None) 

435 self.add_url_rule(rule, endpoint, f, **options) 

436 return f 

437 

438 return decorator 

439 

440 @setupmethod 

441 def add_url_rule( 

442 self, 

443 rule: str, 

444 endpoint: str | None = None, 

445 view_func: ft.RouteCallable | None = None, 

446 provide_automatic_options: bool | None = None, 

447 **options: t.Any, 

448 ) -> None: 

449 """Register a rule for routing incoming requests and building 

450 URLs. The :meth:`route` decorator is a shortcut to call this 

451 with the ``view_func`` argument. These are equivalent: 

452 

453 .. code-block:: python 

454 

455 @app.route("/") 

456 def index(): 

457 ... 

458 

459 .. code-block:: python 

460 

461 def index(): 

462 ... 

463 

464 app.add_url_rule("/", view_func=index) 

465 

466 See :ref:`url-route-registrations`. 

467 

468 The endpoint name for the route defaults to the name of the view 

469 function if the ``endpoint`` parameter isn't passed. An error 

470 will be raised if a function has already been registered for the 

471 endpoint. 

472 

473 The ``methods`` parameter defaults to ``["GET"]``. ``HEAD`` is 

474 always added automatically, and ``OPTIONS`` is added 

475 automatically by default. 

476 

477 ``view_func`` does not necessarily need to be passed, but if the 

478 rule should participate in routing an endpoint name must be 

479 associated with a view function at some point with the 

480 :meth:`endpoint` decorator. 

481 

482 .. code-block:: python 

483 

484 app.add_url_rule("/", endpoint="index") 

485 

486 @app.endpoint("index") 

487 def index(): 

488 ... 

489 

490 If ``view_func`` has a ``required_methods`` attribute, those 

491 methods are added to the passed and automatic methods. If it 

492 has a ``provide_automatic_methods`` attribute, it is used as the 

493 default if the parameter is not passed. 

494 

495 :param rule: The URL rule string. 

496 :param endpoint: The endpoint name to associate with the rule 

497 and view function. Used when routing and building URLs. 

498 Defaults to ``view_func.__name__``. 

499 :param view_func: The view function to associate with the 

500 endpoint name. 

501 :param provide_automatic_options: Add the ``OPTIONS`` method and 

502 respond to ``OPTIONS`` requests automatically. 

503 :param options: Extra options passed to the 

504 :class:`~werkzeug.routing.Rule` object. 

505 """ 

506 raise NotImplementedError 

507 

508 @setupmethod 

509 def endpoint(self, endpoint: str) -> t.Callable[[F], F]: 

510 """Decorate a view function to register it for the given 

511 endpoint. Used if a rule is added without a ``view_func`` with 

512 :meth:`add_url_rule`. 

513 

514 .. code-block:: python 

515 

516 app.add_url_rule("/ex", endpoint="example") 

517 

518 @app.endpoint("example") 

519 def example(): 

520 ... 

521 

522 :param endpoint: The endpoint name to associate with the view 

523 function. 

524 """ 

525 

526 def decorator(f: F) -> F: 

527 self.view_functions[endpoint] = f 

528 return f 

529 

530 return decorator 

531 

532 @setupmethod 

533 def before_request(self, f: T_before_request) -> T_before_request: 

534 """Register a function to run before each request. 

535 

536 For example, this can be used to open a database connection, or 

537 to load the logged in user from the session. 

538 

539 .. code-block:: python 

540 

541 @app.before_request 

542 def load_user(): 

543 if "user_id" in session: 

544 g.user = db.session.get(session["user_id"]) 

545 

546 The function will be called without any arguments. If it returns 

547 a non-``None`` value, the value is handled as if it was the 

548 return value from the view, and further request handling is 

549 stopped. 

550 

551 This is available on both app and blueprint objects. When used on an app, this 

552 executes before every request. When used on a blueprint, this executes before 

553 every request that the blueprint handles. To register with a blueprint and 

554 execute before every request, use :meth:`.Blueprint.before_app_request`. 

555 """ 

556 self.before_request_funcs.setdefault(None, []).append(f) 

557 return f 

558 

559 @setupmethod 

560 def after_request(self, f: T_after_request) -> T_after_request: 

561 """Register a function to run after each request to this object. 

562 

563 The function is called with the response object, and must return 

564 a response object. This allows the functions to modify or 

565 replace the response before it is sent. 

566 

567 If a function raises an exception, any remaining 

568 ``after_request`` functions will not be called. Therefore, this 

569 should not be used for actions that must execute, such as to 

570 close resources. Use :meth:`teardown_request` for that. 

571 

572 This is available on both app and blueprint objects. When used on an app, this 

573 executes after every request. When used on a blueprint, this executes after 

574 every request that the blueprint handles. To register with a blueprint and 

575 execute after every request, use :meth:`.Blueprint.after_app_request`. 

576 """ 

577 self.after_request_funcs.setdefault(None, []).append(f) 

578 return f 

579 

580 @setupmethod 

581 def teardown_request(self, f: T_teardown) -> T_teardown: 

582 """Register a function to be called when the request context is 

583 popped. Typically this happens at the end of each request, but 

584 contexts may be pushed manually as well during testing. 

585 

586 .. code-block:: python 

587 

588 with app.test_request_context(): 

589 ... 

590 

591 When the ``with`` block exits (or ``ctx.pop()`` is called), the 

592 teardown functions are called just before the request context is 

593 made inactive. 

594 

595 When a teardown function was called because of an unhandled 

596 exception it will be passed an error object. If an 

597 :meth:`errorhandler` is registered, it will handle the exception 

598 and the teardown will not receive it. 

599 

600 Teardown functions must avoid raising exceptions. If they 

601 execute code that might fail they must surround that code with a 

602 ``try``/``except`` block and log any errors. 

603 

604 The return values of teardown functions are ignored. 

605 

606 This is available on both app and blueprint objects. When used on an app, this 

607 executes after every request. When used on a blueprint, this executes after 

608 every request that the blueprint handles. To register with a blueprint and 

609 execute after every request, use :meth:`.Blueprint.teardown_app_request`. 

610 """ 

611 self.teardown_request_funcs.setdefault(None, []).append(f) 

612 return f 

613 

614 @setupmethod 

615 def context_processor( 

616 self, 

617 f: T_template_context_processor, 

618 ) -> T_template_context_processor: 

619 """Registers a template context processor function. These functions run before 

620 rendering a template. The keys of the returned dict are added as variables 

621 available in the template. 

622 

623 This is available on both app and blueprint objects. When used on an app, this 

624 is called for every rendered template. When used on a blueprint, this is called 

625 for templates rendered from the blueprint's views. To register with a blueprint 

626 and affect every template, use :meth:`.Blueprint.app_context_processor`. 

627 """ 

628 self.template_context_processors[None].append(f) 

629 return f 

630 

631 @setupmethod 

632 def url_value_preprocessor( 

633 self, 

634 f: T_url_value_preprocessor, 

635 ) -> T_url_value_preprocessor: 

636 """Register a URL value preprocessor function for all view 

637 functions in the application. These functions will be called before the 

638 :meth:`before_request` functions. 

639 

640 The function can modify the values captured from the matched url before 

641 they are passed to the view. For example, this can be used to pop a 

642 common language code value and place it in ``g`` rather than pass it to 

643 every view. 

644 

645 The function is passed the endpoint name and values dict. The return 

646 value is ignored. 

647 

648 This is available on both app and blueprint objects. When used on an app, this 

649 is called for every request. When used on a blueprint, this is called for 

650 requests that the blueprint handles. To register with a blueprint and affect 

651 every request, use :meth:`.Blueprint.app_url_value_preprocessor`. 

652 """ 

653 self.url_value_preprocessors[None].append(f) 

654 return f 

655 

656 @setupmethod 

657 def url_defaults(self, f: T_url_defaults) -> T_url_defaults: 

658 """Callback function for URL defaults for all view functions of the 

659 application. It's called with the endpoint and values and should 

660 update the values passed in place. 

661 

662 This is available on both app and blueprint objects. When used on an app, this 

663 is called for every request. When used on a blueprint, this is called for 

664 requests that the blueprint handles. To register with a blueprint and affect 

665 every request, use :meth:`.Blueprint.app_url_defaults`. 

666 """ 

667 self.url_default_functions[None].append(f) 

668 return f 

669 

670 @setupmethod 

671 def errorhandler( 

672 self, code_or_exception: type[Exception] | int 

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

674 """Register a function to handle errors by code or exception class. 

675 

676 A decorator that is used to register a function given an 

677 error code. Example:: 

678 

679 @app.errorhandler(404) 

680 def page_not_found(error): 

681 return 'This page does not exist', 404 

682 

683 You can also register handlers for arbitrary exceptions:: 

684 

685 @app.errorhandler(DatabaseError) 

686 def special_exception_handler(error): 

687 return 'Database connection failed', 500 

688 

689 This is available on both app and blueprint objects. When used on an app, this 

690 can handle errors from every request. When used on a blueprint, this can handle 

691 errors from requests that the blueprint handles. To register with a blueprint 

692 and affect every request, use :meth:`.Blueprint.app_errorhandler`. 

693 

694 .. versionadded:: 0.7 

695 Use :meth:`register_error_handler` instead of modifying 

696 :attr:`error_handler_spec` directly, for application wide error 

697 handlers. 

698 

699 .. versionadded:: 0.7 

700 One can now additionally also register custom exception types 

701 that do not necessarily have to be a subclass of the 

702 :class:`~werkzeug.exceptions.HTTPException` class. 

703 

704 :param code_or_exception: the code as integer for the handler, or 

705 an arbitrary exception 

706 """ 

707 

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

709 self.register_error_handler(code_or_exception, f) 

710 return f 

711 

712 return decorator 

713 

714 @setupmethod 

715 def register_error_handler( 

716 self, 

717 code_or_exception: type[Exception] | int, 

718 f: ft.ErrorHandlerCallable, 

719 ) -> None: 

720 """Alternative error attach function to the :meth:`errorhandler` 

721 decorator that is more straightforward to use for non decorator 

722 usage. 

723 

724 .. versionadded:: 0.7 

725 """ 

726 exc_class, code = self._get_exc_class_and_code(code_or_exception) 

727 self.error_handler_spec[None][code][exc_class] = f 

728 

729 @staticmethod 

730 def _get_exc_class_and_code( 

731 exc_class_or_code: type[Exception] | int, 

732 ) -> tuple[type[Exception], int | None]: 

733 """Get the exception class being handled. For HTTP status codes 

734 or ``HTTPException`` subclasses, return both the exception and 

735 status code. 

736 

737 :param exc_class_or_code: Any exception class, or an HTTP status 

738 code as an integer. 

739 """ 

740 exc_class: type[Exception] 

741 

742 if isinstance(exc_class_or_code, int): 

743 try: 

744 exc_class = default_exceptions[exc_class_or_code] 

745 except KeyError: 

746 raise ValueError( 

747 f"'{exc_class_or_code}' is not a recognized HTTP" 

748 " error code. Use a subclass of HTTPException with" 

749 " that code instead." 

750 ) from None 

751 else: 

752 exc_class = exc_class_or_code 

753 

754 if isinstance(exc_class, Exception): 

755 raise TypeError( 

756 f"{exc_class!r} is an instance, not a class. Handlers" 

757 " can only be registered for Exception classes or HTTP" 

758 " error codes." 

759 ) 

760 

761 if not issubclass(exc_class, Exception): 

762 raise ValueError( 

763 f"'{exc_class.__name__}' is not a subclass of Exception." 

764 " Handlers can only be registered for Exception classes" 

765 " or HTTP error codes." 

766 ) 

767 

768 if issubclass(exc_class, HTTPException): 

769 return exc_class, exc_class.code 

770 else: 

771 return exc_class, None 

772 

773 

774def _endpoint_from_view_func(view_func: t.Callable) -> str: 

775 """Internal helper that returns the default endpoint for a given 

776 function. This always is the function name. 

777 """ 

778 assert view_func is not None, "expected view func if endpoint is not provided." 

779 return view_func.__name__ 

780 

781 

782def _matching_loader_thinks_module_is_package(loader, mod_name): 

783 """Attempt to figure out if the given name is a package or a module. 

784 

785 :param: loader: The loader that handled the name. 

786 :param mod_name: The name of the package or module. 

787 """ 

788 # Use loader.is_package if it's available. 

789 if hasattr(loader, "is_package"): 

790 return loader.is_package(mod_name) 

791 

792 cls = type(loader) 

793 

794 # NamespaceLoader doesn't implement is_package, but all names it 

795 # loads must be packages. 

796 if cls.__module__ == "_frozen_importlib" and cls.__name__ == "NamespaceLoader": 

797 return True 

798 

799 # Otherwise we need to fail with an error that explains what went 

800 # wrong. 

801 raise AttributeError( 

802 f"'{cls.__name__}.is_package()' must be implemented for PEP 302" 

803 f" import hooks." 

804 ) 

805 

806 

807def _path_is_relative_to(path: pathlib.PurePath, base: str) -> bool: 

808 # Path.is_relative_to doesn't exist until Python 3.9 

809 try: 

810 path.relative_to(base) 

811 return True 

812 except ValueError: 

813 return False 

814 

815 

816def _find_package_path(import_name): 

817 """Find the path that contains the package or module.""" 

818 root_mod_name, _, _ = import_name.partition(".") 

819 

820 try: 

821 root_spec = importlib.util.find_spec(root_mod_name) 

822 

823 if root_spec is None: 

824 raise ValueError("not found") 

825 # ImportError: the machinery told us it does not exist 

826 # ValueError: 

827 # - the module name was invalid 

828 # - the module name is __main__ 

829 # - *we* raised `ValueError` due to `root_spec` being `None` 

830 except (ImportError, ValueError): 

831 pass # handled below 

832 else: 

833 # namespace package 

834 if root_spec.origin in {"namespace", None}: 

835 package_spec = importlib.util.find_spec(import_name) 

836 if package_spec is not None and package_spec.submodule_search_locations: 

837 # Pick the path in the namespace that contains the submodule. 

838 package_path = pathlib.Path( 

839 os.path.commonpath(package_spec.submodule_search_locations) 

840 ) 

841 search_locations = ( 

842 location 

843 for location in root_spec.submodule_search_locations 

844 if _path_is_relative_to(package_path, location) 

845 ) 

846 else: 

847 # Pick the first path. 

848 search_locations = iter(root_spec.submodule_search_locations) 

849 return os.path.dirname(next(search_locations)) 

850 # a package (with __init__.py) 

851 elif root_spec.submodule_search_locations: 

852 return os.path.dirname(os.path.dirname(root_spec.origin)) 

853 # just a normal module 

854 else: 

855 return os.path.dirname(root_spec.origin) 

856 

857 # we were unable to find the `package_path` using PEP 451 loaders 

858 spec = importlib.util.find_spec(root_mod_name) 

859 loader = spec.loader if spec is not None else None 

860 

861 if loader is None or root_mod_name == "__main__": 

862 # import name is not found, or interactive/main module 

863 return os.getcwd() 

864 

865 if hasattr(loader, "get_filename"): 

866 filename = loader.get_filename(root_mod_name) 

867 elif hasattr(loader, "archive"): 

868 # zipimporter's loader.archive points to the .zip file. 

869 filename = loader.archive 

870 else: 

871 # At least one loader is missing both get_filename and archive: 

872 # Google App Engine's HardenedModulesHook, use __file__. 

873 filename = importlib.import_module(root_mod_name).__file__ 

874 

875 package_path = os.path.abspath(os.path.dirname(filename)) 

876 

877 # If the imported name is a package, filename is currently pointing 

878 # to the root of the package, need to get the current directory. 

879 if _matching_loader_thinks_module_is_package(loader, root_mod_name): 

880 package_path = os.path.dirname(package_path) 

881 

882 return package_path 

883 

884 

885def find_package(import_name: str): 

886 """Find the prefix that a package is installed under, and the path 

887 that it would be imported from. 

888 

889 The prefix is the directory containing the standard directory 

890 hierarchy (lib, bin, etc.). If the package is not installed to the 

891 system (:attr:`sys.prefix`) or a virtualenv (``site-packages``), 

892 ``None`` is returned. 

893 

894 The path is the entry in :attr:`sys.path` that contains the package 

895 for import. If the package is not installed, it's assumed that the 

896 package was imported from the current working directory. 

897 """ 

898 package_path = _find_package_path(import_name) 

899 py_prefix = os.path.abspath(sys.prefix) 

900 

901 # installed to the system 

902 if _path_is_relative_to(pathlib.PurePath(package_path), py_prefix): 

903 return py_prefix, package_path 

904 

905 site_parent, site_folder = os.path.split(package_path) 

906 

907 # installed to a virtualenv 

908 if site_folder.lower() == "site-packages": 

909 parent, folder = os.path.split(site_parent) 

910 

911 # Windows (prefix/lib/site-packages) 

912 if folder.lower() == "lib": 

913 return parent, package_path 

914 

915 # Unix (prefix/lib/pythonX.Y/site-packages) 

916 if os.path.basename(parent).lower() == "lib": 

917 return os.path.dirname(parent), package_path 

918 

919 # something else (prefix/site-packages) 

920 return site_parent, package_path 

921 

922 # not installed 

923 return None, package_path