Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/exceptions.py: 76%

237 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-09 07:17 +0000

1"""Implements a number of Python exceptions which can be raised from within 

2a view to trigger a standard HTTP non-200 response. 

3 

4Usage Example 

5------------- 

6 

7.. code-block:: python 

8 

9 from werkzeug.wrappers.request import Request 

10 from werkzeug.exceptions import HTTPException, NotFound 

11 

12 def view(request): 

13 raise NotFound() 

14 

15 @Request.application 

16 def application(request): 

17 try: 

18 return view(request) 

19 except HTTPException as e: 

20 return e 

21 

22As you can see from this example those exceptions are callable WSGI 

23applications. However, they are not Werkzeug response objects. You 

24can get a response object by calling ``get_response()`` on a HTTP 

25exception. 

26 

27Keep in mind that you may have to pass an environ (WSGI) or scope 

28(ASGI) to ``get_response()`` because some errors fetch additional 

29information relating to the request. 

30 

31If you want to hook in a different exception page to say, a 404 status 

32code, you can add a second except for a specific subclass of an error: 

33 

34.. code-block:: python 

35 

36 @Request.application 

37 def application(request): 

38 try: 

39 return view(request) 

40 except NotFound as e: 

41 return not_found(request) 

42 except HTTPException as e: 

43 return e 

44 

45""" 

46from __future__ import annotations 

47 

48import typing as t 

49from datetime import datetime 

50 

51from markupsafe import escape 

52from markupsafe import Markup 

53 

54from ._internal import _get_environ 

55 

56if t.TYPE_CHECKING: 

57 from _typeshed.wsgi import StartResponse 

58 from _typeshed.wsgi import WSGIEnvironment 

59 from .datastructures import WWWAuthenticate 

60 from .sansio.response import Response 

61 from .wrappers.request import Request as WSGIRequest 

62 from .wrappers.response import Response as WSGIResponse 

63 

64 

65class HTTPException(Exception): 

66 """The base class for all HTTP exceptions. This exception can be called as a WSGI 

67 application to render a default error page or you can catch the subclasses 

68 of it independently and render nicer error messages. 

69 

70 .. versionchanged:: 2.1 

71 Removed the ``wrap`` class method. 

72 """ 

73 

74 code: int | None = None 

75 description: str | None = None 

76 

77 def __init__( 

78 self, 

79 description: str | None = None, 

80 response: Response | None = None, 

81 ) -> None: 

82 super().__init__() 

83 if description is not None: 

84 self.description = description 

85 self.response = response 

86 

87 @property 

88 def name(self) -> str: 

89 """The status name.""" 

90 from .http import HTTP_STATUS_CODES 

91 

92 return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore 

93 

94 def get_description( 

95 self, 

96 environ: WSGIEnvironment | None = None, 

97 scope: dict | None = None, 

98 ) -> str: 

99 """Get the description.""" 

100 if self.description is None: 

101 description = "" 

102 else: 

103 description = self.description 

104 

105 description = escape(description).replace("\n", Markup("<br>")) 

106 return f"<p>{description}</p>" 

107 

108 def get_body( 

109 self, 

110 environ: WSGIEnvironment | None = None, 

111 scope: dict | None = None, 

112 ) -> str: 

113 """Get the HTML body.""" 

114 return ( 

115 "<!doctype html>\n" 

116 "<html lang=en>\n" 

117 f"<title>{self.code} {escape(self.name)}</title>\n" 

118 f"<h1>{escape(self.name)}</h1>\n" 

119 f"{self.get_description(environ)}\n" 

120 ) 

121 

122 def get_headers( 

123 self, 

124 environ: WSGIEnvironment | None = None, 

125 scope: dict | None = None, 

126 ) -> list[tuple[str, str]]: 

127 """Get a list of headers.""" 

128 return [("Content-Type", "text/html; charset=utf-8")] 

129 

130 def get_response( 

131 self, 

132 environ: WSGIEnvironment | WSGIRequest | None = None, 

133 scope: dict | None = None, 

134 ) -> Response: 

135 """Get a response object. If one was passed to the exception 

136 it's returned directly. 

137 

138 :param environ: the optional environ for the request. This 

139 can be used to modify the response depending 

140 on how the request looked like. 

141 :return: a :class:`Response` object or a subclass thereof. 

142 """ 

143 from .wrappers.response import Response as WSGIResponse # noqa: F811 

144 

145 if self.response is not None: 

146 return self.response 

147 if environ is not None: 

148 environ = _get_environ(environ) 

149 headers = self.get_headers(environ, scope) 

150 return WSGIResponse(self.get_body(environ, scope), self.code, headers) 

151 

152 def __call__( 

153 self, environ: WSGIEnvironment, start_response: StartResponse 

154 ) -> t.Iterable[bytes]: 

155 """Call the exception as WSGI application. 

156 

157 :param environ: the WSGI environment. 

158 :param start_response: the response callable provided by the WSGI 

159 server. 

160 """ 

161 response = t.cast("WSGIResponse", self.get_response(environ)) 

162 return response(environ, start_response) 

163 

164 def __str__(self) -> str: 

165 code = self.code if self.code is not None else "???" 

166 return f"{code} {self.name}: {self.description}" 

167 

168 def __repr__(self) -> str: 

169 code = self.code if self.code is not None else "???" 

170 return f"<{type(self).__name__} '{code}: {self.name}'>" 

171 

172 

173class BadRequest(HTTPException): 

174 """*400* `Bad Request` 

175 

176 Raise if the browser sends something to the application the application 

177 or server cannot handle. 

178 """ 

179 

180 code = 400 

181 description = ( 

182 "The browser (or proxy) sent a request that this server could " 

183 "not understand." 

184 ) 

185 

186 

187class BadRequestKeyError(BadRequest, KeyError): 

188 """An exception that is used to signal both a :exc:`KeyError` and a 

189 :exc:`BadRequest`. Used by many of the datastructures. 

190 """ 

191 

192 _description = BadRequest.description 

193 #: Show the KeyError along with the HTTP error message in the 

194 #: response. This should be disabled in production, but can be 

195 #: useful in a debug mode. 

196 show_exception = False 

197 

198 def __init__(self, arg: str | None = None, *args: t.Any, **kwargs: t.Any): 

199 super().__init__(*args, **kwargs) 

200 

201 if arg is None: 

202 KeyError.__init__(self) 

203 else: 

204 KeyError.__init__(self, arg) 

205 

206 @property # type: ignore 

207 def description(self) -> str: 

208 if self.show_exception: 

209 return ( 

210 f"{self._description}\n" 

211 f"{KeyError.__name__}: {KeyError.__str__(self)}" 

212 ) 

213 

214 return self._description 

215 

216 @description.setter 

217 def description(self, value: str) -> None: 

218 self._description = value 

219 

220 

221class ClientDisconnected(BadRequest): 

222 """Internal exception that is raised if Werkzeug detects a disconnected 

223 client. Since the client is already gone at that point attempting to 

224 send the error message to the client might not work and might ultimately 

225 result in another exception in the server. Mainly this is here so that 

226 it is silenced by default as far as Werkzeug is concerned. 

227 

228 Since disconnections cannot be reliably detected and are unspecified 

229 by WSGI to a large extent this might or might not be raised if a client 

230 is gone. 

231 

232 .. versionadded:: 0.8 

233 """ 

234 

235 

236class SecurityError(BadRequest): 

237 """Raised if something triggers a security error. This is otherwise 

238 exactly like a bad request error. 

239 

240 .. versionadded:: 0.9 

241 """ 

242 

243 

244class BadHost(BadRequest): 

245 """Raised if the submitted host is badly formatted. 

246 

247 .. versionadded:: 0.11.2 

248 """ 

249 

250 

251class Unauthorized(HTTPException): 

252 """*401* ``Unauthorized`` 

253 

254 Raise if the user is not authorized to access a resource. 

255 

256 The ``www_authenticate`` argument should be used to set the 

257 ``WWW-Authenticate`` header. This is used for HTTP basic auth and 

258 other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate` 

259 to create correctly formatted values. Strictly speaking a 401 

260 response is invalid if it doesn't provide at least one value for 

261 this header, although real clients typically don't care. 

262 

263 :param description: Override the default message used for the body 

264 of the response. 

265 :param www-authenticate: A single value, or list of values, for the 

266 WWW-Authenticate header(s). 

267 

268 .. versionchanged:: 2.0 

269 Serialize multiple ``www_authenticate`` items into multiple 

270 ``WWW-Authenticate`` headers, rather than joining them 

271 into a single value, for better interoperability. 

272 

273 .. versionchanged:: 0.15.3 

274 If the ``www_authenticate`` argument is not set, the 

275 ``WWW-Authenticate`` header is not set. 

276 

277 .. versionchanged:: 0.15.3 

278 The ``response`` argument was restored. 

279 

280 .. versionchanged:: 0.15.1 

281 ``description`` was moved back as the first argument, restoring 

282 its previous position. 

283 

284 .. versionchanged:: 0.15.0 

285 ``www_authenticate`` was added as the first argument, ahead of 

286 ``description``. 

287 """ 

288 

289 code = 401 

290 description = ( 

291 "The server could not verify that you are authorized to access" 

292 " the URL requested. You either supplied the wrong credentials" 

293 " (e.g. a bad password), or your browser doesn't understand" 

294 " how to supply the credentials required." 

295 ) 

296 

297 def __init__( 

298 self, 

299 description: str | None = None, 

300 response: Response | None = None, 

301 www_authenticate: None | (WWWAuthenticate | t.Iterable[WWWAuthenticate]) = None, 

302 ) -> None: 

303 super().__init__(description, response) 

304 

305 from .datastructures import WWWAuthenticate 

306 

307 if isinstance(www_authenticate, WWWAuthenticate): 

308 www_authenticate = (www_authenticate,) 

309 

310 self.www_authenticate = www_authenticate 

311 

312 def get_headers( 

313 self, 

314 environ: WSGIEnvironment | None = None, 

315 scope: dict | None = None, 

316 ) -> list[tuple[str, str]]: 

317 headers = super().get_headers(environ, scope) 

318 if self.www_authenticate: 

319 headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate) 

320 return headers 

321 

322 

323class Forbidden(HTTPException): 

324 """*403* `Forbidden` 

325 

326 Raise if the user doesn't have the permission for the requested resource 

327 but was authenticated. 

328 """ 

329 

330 code = 403 

331 description = ( 

332 "You don't have the permission to access the requested" 

333 " resource. It is either read-protected or not readable by the" 

334 " server." 

335 ) 

336 

337 

338class NotFound(HTTPException): 

339 """*404* `Not Found` 

340 

341 Raise if a resource does not exist and never existed. 

342 """ 

343 

344 code = 404 

345 description = ( 

346 "The requested URL was not found on the server. If you entered" 

347 " the URL manually please check your spelling and try again." 

348 ) 

349 

350 

351class MethodNotAllowed(HTTPException): 

352 """*405* `Method Not Allowed` 

353 

354 Raise if the server used a method the resource does not handle. For 

355 example `POST` if the resource is view only. Especially useful for REST. 

356 

357 The first argument for this exception should be a list of allowed methods. 

358 Strictly speaking the response would be invalid if you don't provide valid 

359 methods in the header which you can do with that list. 

360 """ 

361 

362 code = 405 

363 description = "The method is not allowed for the requested URL." 

364 

365 def __init__( 

366 self, 

367 valid_methods: t.Iterable[str] | None = None, 

368 description: str | None = None, 

369 response: Response | None = None, 

370 ) -> None: 

371 """Takes an optional list of valid http methods 

372 starting with werkzeug 0.3 the list will be mandatory.""" 

373 super().__init__(description=description, response=response) 

374 self.valid_methods = valid_methods 

375 

376 def get_headers( 

377 self, 

378 environ: WSGIEnvironment | None = None, 

379 scope: dict | None = None, 

380 ) -> list[tuple[str, str]]: 

381 headers = super().get_headers(environ, scope) 

382 if self.valid_methods: 

383 headers.append(("Allow", ", ".join(self.valid_methods))) 

384 return headers 

385 

386 

387class NotAcceptable(HTTPException): 

388 """*406* `Not Acceptable` 

389 

390 Raise if the server can't return any content conforming to the 

391 `Accept` headers of the client. 

392 """ 

393 

394 code = 406 

395 description = ( 

396 "The resource identified by the request is only capable of" 

397 " generating response entities which have content" 

398 " characteristics not acceptable according to the accept" 

399 " headers sent in the request." 

400 ) 

401 

402 

403class RequestTimeout(HTTPException): 

404 """*408* `Request Timeout` 

405 

406 Raise to signalize a timeout. 

407 """ 

408 

409 code = 408 

410 description = ( 

411 "The server closed the network connection because the browser" 

412 " didn't finish the request within the specified time." 

413 ) 

414 

415 

416class Conflict(HTTPException): 

417 """*409* `Conflict` 

418 

419 Raise to signal that a request cannot be completed because it conflicts 

420 with the current state on the server. 

421 

422 .. versionadded:: 0.7 

423 """ 

424 

425 code = 409 

426 description = ( 

427 "A conflict happened while processing the request. The" 

428 " resource might have been modified while the request was being" 

429 " processed." 

430 ) 

431 

432 

433class Gone(HTTPException): 

434 """*410* `Gone` 

435 

436 Raise if a resource existed previously and went away without new location. 

437 """ 

438 

439 code = 410 

440 description = ( 

441 "The requested URL is no longer available on this server and" 

442 " there is no forwarding address. If you followed a link from a" 

443 " foreign page, please contact the author of this page." 

444 ) 

445 

446 

447class LengthRequired(HTTPException): 

448 """*411* `Length Required` 

449 

450 Raise if the browser submitted data but no ``Content-Length`` header which 

451 is required for the kind of processing the server does. 

452 """ 

453 

454 code = 411 

455 description = ( 

456 "A request with this method requires a valid <code>Content-" 

457 "Length</code> header." 

458 ) 

459 

460 

461class PreconditionFailed(HTTPException): 

462 """*412* `Precondition Failed` 

463 

464 Status code used in combination with ``If-Match``, ``If-None-Match``, or 

465 ``If-Unmodified-Since``. 

466 """ 

467 

468 code = 412 

469 description = ( 

470 "The precondition on the request for the URL failed positive evaluation." 

471 ) 

472 

473 

474class RequestEntityTooLarge(HTTPException): 

475 """*413* `Request Entity Too Large` 

476 

477 The status code one should return if the data submitted exceeded a given 

478 limit. 

479 """ 

480 

481 code = 413 

482 description = "The data value transmitted exceeds the capacity limit." 

483 

484 

485class RequestURITooLarge(HTTPException): 

486 """*414* `Request URI Too Large` 

487 

488 Like *413* but for too long URLs. 

489 """ 

490 

491 code = 414 

492 description = ( 

493 "The length of the requested URL exceeds the capacity limit for" 

494 " this server. The request cannot be processed." 

495 ) 

496 

497 

498class UnsupportedMediaType(HTTPException): 

499 """*415* `Unsupported Media Type` 

500 

501 The status code returned if the server is unable to handle the media type 

502 the client transmitted. 

503 """ 

504 

505 code = 415 

506 description = ( 

507 "The server does not support the media type transmitted in the request." 

508 ) 

509 

510 

511class RequestedRangeNotSatisfiable(HTTPException): 

512 """*416* `Requested Range Not Satisfiable` 

513 

514 The client asked for an invalid part of the file. 

515 

516 .. versionadded:: 0.7 

517 """ 

518 

519 code = 416 

520 description = "The server cannot provide the requested range." 

521 

522 def __init__( 

523 self, 

524 length: int | None = None, 

525 units: str = "bytes", 

526 description: str | None = None, 

527 response: Response | None = None, 

528 ) -> None: 

529 """Takes an optional `Content-Range` header value based on ``length`` 

530 parameter. 

531 """ 

532 super().__init__(description=description, response=response) 

533 self.length = length 

534 self.units = units 

535 

536 def get_headers( 

537 self, 

538 environ: WSGIEnvironment | None = None, 

539 scope: dict | None = None, 

540 ) -> list[tuple[str, str]]: 

541 headers = super().get_headers(environ, scope) 

542 if self.length is not None: 

543 headers.append(("Content-Range", f"{self.units} */{self.length}")) 

544 return headers 

545 

546 

547class ExpectationFailed(HTTPException): 

548 """*417* `Expectation Failed` 

549 

550 The server cannot meet the requirements of the Expect request-header. 

551 

552 .. versionadded:: 0.7 

553 """ 

554 

555 code = 417 

556 description = "The server could not meet the requirements of the Expect header" 

557 

558 

559class ImATeapot(HTTPException): 

560 """*418* `I'm a teapot` 

561 

562 The server should return this if it is a teapot and someone attempted 

563 to brew coffee with it. 

564 

565 .. versionadded:: 0.7 

566 """ 

567 

568 code = 418 

569 description = "This server is a teapot, not a coffee machine" 

570 

571 

572class UnprocessableEntity(HTTPException): 

573 """*422* `Unprocessable Entity` 

574 

575 Used if the request is well formed, but the instructions are otherwise 

576 incorrect. 

577 """ 

578 

579 code = 422 

580 description = ( 

581 "The request was well-formed but was unable to be followed due" 

582 " to semantic errors." 

583 ) 

584 

585 

586class Locked(HTTPException): 

587 """*423* `Locked` 

588 

589 Used if the resource that is being accessed is locked. 

590 """ 

591 

592 code = 423 

593 description = "The resource that is being accessed is locked." 

594 

595 

596class FailedDependency(HTTPException): 

597 """*424* `Failed Dependency` 

598 

599 Used if the method could not be performed on the resource 

600 because the requested action depended on another action and that action failed. 

601 """ 

602 

603 code = 424 

604 description = ( 

605 "The method could not be performed on the resource because the" 

606 " requested action depended on another action and that action" 

607 " failed." 

608 ) 

609 

610 

611class PreconditionRequired(HTTPException): 

612 """*428* `Precondition Required` 

613 

614 The server requires this request to be conditional, typically to prevent 

615 the lost update problem, which is a race condition between two or more 

616 clients attempting to update a resource through PUT or DELETE. By requiring 

617 each client to include a conditional header ("If-Match" or "If-Unmodified- 

618 Since") with the proper value retained from a recent GET request, the 

619 server ensures that each client has at least seen the previous revision of 

620 the resource. 

621 """ 

622 

623 code = 428 

624 description = ( 

625 "This request is required to be conditional; try using" 

626 ' "If-Match" or "If-Unmodified-Since".' 

627 ) 

628 

629 

630class _RetryAfter(HTTPException): 

631 """Adds an optional ``retry_after`` parameter which will set the 

632 ``Retry-After`` header. May be an :class:`int` number of seconds or 

633 a :class:`~datetime.datetime`. 

634 """ 

635 

636 def __init__( 

637 self, 

638 description: str | None = None, 

639 response: Response | None = None, 

640 retry_after: datetime | int | None = None, 

641 ) -> None: 

642 super().__init__(description, response) 

643 self.retry_after = retry_after 

644 

645 def get_headers( 

646 self, 

647 environ: WSGIEnvironment | None = None, 

648 scope: dict | None = None, 

649 ) -> list[tuple[str, str]]: 

650 headers = super().get_headers(environ, scope) 

651 

652 if self.retry_after: 

653 if isinstance(self.retry_after, datetime): 

654 from .http import http_date 

655 

656 value = http_date(self.retry_after) 

657 else: 

658 value = str(self.retry_after) 

659 

660 headers.append(("Retry-After", value)) 

661 

662 return headers 

663 

664 

665class TooManyRequests(_RetryAfter): 

666 """*429* `Too Many Requests` 

667 

668 The server is limiting the rate at which this user receives 

669 responses, and this request exceeds that rate. (The server may use 

670 any convenient method to identify users and their request rates). 

671 The server may include a "Retry-After" header to indicate how long 

672 the user should wait before retrying. 

673 

674 :param retry_after: If given, set the ``Retry-After`` header to this 

675 value. May be an :class:`int` number of seconds or a 

676 :class:`~datetime.datetime`. 

677 

678 .. versionchanged:: 1.0 

679 Added ``retry_after`` parameter. 

680 """ 

681 

682 code = 429 

683 description = "This user has exceeded an allotted request count. Try again later." 

684 

685 

686class RequestHeaderFieldsTooLarge(HTTPException): 

687 """*431* `Request Header Fields Too Large` 

688 

689 The server refuses to process the request because the header fields are too 

690 large. One or more individual fields may be too large, or the set of all 

691 headers is too large. 

692 """ 

693 

694 code = 431 

695 description = "One or more header fields exceeds the maximum size." 

696 

697 

698class UnavailableForLegalReasons(HTTPException): 

699 """*451* `Unavailable For Legal Reasons` 

700 

701 This status code indicates that the server is denying access to the 

702 resource as a consequence of a legal demand. 

703 """ 

704 

705 code = 451 

706 description = "Unavailable for legal reasons." 

707 

708 

709class InternalServerError(HTTPException): 

710 """*500* `Internal Server Error` 

711 

712 Raise if an internal server error occurred. This is a good fallback if an 

713 unknown error occurred in the dispatcher. 

714 

715 .. versionchanged:: 1.0.0 

716 Added the :attr:`original_exception` attribute. 

717 """ 

718 

719 code = 500 

720 description = ( 

721 "The server encountered an internal error and was unable to" 

722 " complete your request. Either the server is overloaded or" 

723 " there is an error in the application." 

724 ) 

725 

726 def __init__( 

727 self, 

728 description: str | None = None, 

729 response: Response | None = None, 

730 original_exception: BaseException | None = None, 

731 ) -> None: 

732 #: The original exception that caused this 500 error. Can be 

733 #: used by frameworks to provide context when handling 

734 #: unexpected errors. 

735 self.original_exception = original_exception 

736 super().__init__(description=description, response=response) 

737 

738 

739class NotImplemented(HTTPException): 

740 """*501* `Not Implemented` 

741 

742 Raise if the application does not support the action requested by the 

743 browser. 

744 """ 

745 

746 code = 501 

747 description = "The server does not support the action requested by the browser." 

748 

749 

750class BadGateway(HTTPException): 

751 """*502* `Bad Gateway` 

752 

753 If you do proxying in your application you should return this status code 

754 if you received an invalid response from the upstream server it accessed 

755 in attempting to fulfill the request. 

756 """ 

757 

758 code = 502 

759 description = ( 

760 "The proxy server received an invalid response from an upstream server." 

761 ) 

762 

763 

764class ServiceUnavailable(_RetryAfter): 

765 """*503* `Service Unavailable` 

766 

767 Status code you should return if a service is temporarily 

768 unavailable. 

769 

770 :param retry_after: If given, set the ``Retry-After`` header to this 

771 value. May be an :class:`int` number of seconds or a 

772 :class:`~datetime.datetime`. 

773 

774 .. versionchanged:: 1.0 

775 Added ``retry_after`` parameter. 

776 """ 

777 

778 code = 503 

779 description = ( 

780 "The server is temporarily unable to service your request due" 

781 " to maintenance downtime or capacity problems. Please try" 

782 " again later." 

783 ) 

784 

785 

786class GatewayTimeout(HTTPException): 

787 """*504* `Gateway Timeout` 

788 

789 Status code you should return if a connection to an upstream server 

790 times out. 

791 """ 

792 

793 code = 504 

794 description = "The connection to an upstream server timed out." 

795 

796 

797class HTTPVersionNotSupported(HTTPException): 

798 """*505* `HTTP Version Not Supported` 

799 

800 The server does not support the HTTP protocol version used in the request. 

801 """ 

802 

803 code = 505 

804 description = ( 

805 "The server does not support the HTTP protocol version used in the request." 

806 ) 

807 

808 

809default_exceptions: dict[int, type[HTTPException]] = {} 

810 

811 

812def _find_exceptions() -> None: 

813 for obj in globals().values(): 

814 try: 

815 is_http_exception = issubclass(obj, HTTPException) 

816 except TypeError: 

817 is_http_exception = False 

818 if not is_http_exception or obj.code is None: 

819 continue 

820 old_obj = default_exceptions.get(obj.code, None) 

821 if old_obj is not None and issubclass(obj, old_obj): 

822 continue 

823 default_exceptions[obj.code] = obj 

824 

825 

826_find_exceptions() 

827del _find_exceptions 

828 

829 

830class Aborter: 

831 """When passed a dict of code -> exception items it can be used as 

832 callable that raises exceptions. If the first argument to the 

833 callable is an integer it will be looked up in the mapping, if it's 

834 a WSGI application it will be raised in a proxy exception. 

835 

836 The rest of the arguments are forwarded to the exception constructor. 

837 """ 

838 

839 def __init__( 

840 self, 

841 mapping: dict[int, type[HTTPException]] | None = None, 

842 extra: dict[int, type[HTTPException]] | None = None, 

843 ) -> None: 

844 if mapping is None: 

845 mapping = default_exceptions 

846 self.mapping = dict(mapping) 

847 if extra is not None: 

848 self.mapping.update(extra) 

849 

850 def __call__( 

851 self, code: int | Response, *args: t.Any, **kwargs: t.Any 

852 ) -> t.NoReturn: 

853 from .sansio.response import Response 

854 

855 if isinstance(code, Response): 

856 raise HTTPException(response=code) 

857 

858 if code not in self.mapping: 

859 raise LookupError(f"no exception for {code!r}") 

860 

861 raise self.mapping[code](*args, **kwargs) 

862 

863 

864def abort(status: int | Response, *args: t.Any, **kwargs: t.Any) -> t.NoReturn: 

865 """Raises an :py:exc:`HTTPException` for the given status code or WSGI 

866 application. 

867 

868 If a status code is given, it will be looked up in the list of 

869 exceptions and will raise that exception. If passed a WSGI application, 

870 it will wrap it in a proxy WSGI exception and raise that:: 

871 

872 abort(404) # 404 Not Found 

873 abort(Response('Hello World')) 

874 

875 """ 

876 _aborter(status, *args, **kwargs) 

877 

878 

879_aborter: Aborter = Aborter()