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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

237 statements  

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""" 

46 

47from __future__ import annotations 

48 

49import typing as t 

50from datetime import datetime 

51 

52from markupsafe import escape 

53from markupsafe import Markup 

54 

55from ._internal import _get_environ 

56 

57if t.TYPE_CHECKING: 

58 from _typeshed.wsgi import StartResponse 

59 from _typeshed.wsgi import WSGIEnvironment 

60 

61 from .datastructures import WWWAuthenticate 

62 from .sansio.response import Response 

63 from .wrappers.request import Request as WSGIRequest 

64 from .wrappers.response import Response as WSGIResponse 

65 

66 

67class HTTPException(Exception): 

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

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

70 of it independently and render nicer error messages. 

71 

72 .. versionchanged:: 2.1 

73 Removed the ``wrap`` class method. 

74 """ 

75 

76 code: int | None = None 

77 description: str | None = None 

78 

79 def __init__( 

80 self, 

81 description: str | None = None, 

82 response: Response | None = None, 

83 ) -> None: 

84 super().__init__() 

85 if description is not None: 

86 self.description = description 

87 self.response = response 

88 

89 @property 

90 def name(self) -> str: 

91 """The status name.""" 

92 from .http import HTTP_STATUS_CODES 

93 

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

95 

96 def get_description( 

97 self, 

98 environ: WSGIEnvironment | None = None, 

99 scope: dict[str, t.Any] | None = None, 

100 ) -> str: 

101 """Get the description.""" 

102 if self.description is None: 

103 description = "" 

104 else: 

105 description = self.description 

106 

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

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

109 

110 def get_body( 

111 self, 

112 environ: WSGIEnvironment | None = None, 

113 scope: dict[str, t.Any] | None = None, 

114 ) -> str: 

115 """Get the HTML body.""" 

116 return ( 

117 "<!doctype html>\n" 

118 "<html lang=en>\n" 

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

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

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

122 ) 

123 

124 def get_headers( 

125 self, 

126 environ: WSGIEnvironment | None = None, 

127 scope: dict[str, t.Any] | None = None, 

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

129 """Get a list of headers.""" 

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

131 

132 def get_response( 

133 self, 

134 environ: WSGIEnvironment | WSGIRequest | None = None, 

135 scope: dict[str, t.Any] | None = None, 

136 ) -> Response: 

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

138 it's returned directly. 

139 

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

141 can be used to modify the response depending 

142 on how the request looked like. 

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

144 """ 

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

146 

147 if self.response is not None: 

148 return self.response 

149 if environ is not None: 

150 environ = _get_environ(environ) 

151 headers = self.get_headers(environ, scope) 

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

153 

154 def __call__( 

155 self, environ: WSGIEnvironment, start_response: StartResponse 

156 ) -> t.Iterable[bytes]: 

157 """Call the exception as WSGI application. 

158 

159 :param environ: the WSGI environment. 

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

161 server. 

162 """ 

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

164 return response(environ, start_response) 

165 

166 def __str__(self) -> str: 

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

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

169 

170 def __repr__(self) -> str: 

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

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

173 

174 

175class BadRequest(HTTPException): 

176 """*400* `Bad Request` 

177 

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

179 or server cannot handle. 

180 """ 

181 

182 code = 400 

183 description = ( 

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

185 "not understand." 

186 ) 

187 

188 

189class BadRequestKeyError(BadRequest, KeyError): 

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

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

192 """ 

193 

194 _description = BadRequest.description 

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

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

197 #: useful in a debug mode. 

198 show_exception = False 

199 

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

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

202 

203 if arg is None: 

204 KeyError.__init__(self) 

205 else: 

206 KeyError.__init__(self, arg) 

207 

208 @property # type: ignore 

209 def description(self) -> str: 

210 if self.show_exception: 

211 return ( 

212 f"{self._description}\n" 

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

214 ) 

215 

216 return self._description 

217 

218 @description.setter 

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

220 self._description = value 

221 

222 

223class ClientDisconnected(BadRequest): 

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

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

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

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

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

229 

230 Since disconnections cannot be reliably detected and are unspecified 

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

232 is gone. 

233 

234 .. versionadded:: 0.8 

235 """ 

236 

237 

238class SecurityError(BadRequest): 

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

240 exactly like a bad request error. 

241 

242 .. versionadded:: 0.9 

243 """ 

244 

245 

246class BadHost(BadRequest): 

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

248 

249 .. versionadded:: 0.11.2 

250 """ 

251 

252 

253class Unauthorized(HTTPException): 

254 """*401* ``Unauthorized`` 

255 

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

257 

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

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

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

261 to create correctly formatted values. Strictly speaking a 401 

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

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

264 

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

266 of the response. 

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

268 WWW-Authenticate header(s). 

269 

270 .. versionchanged:: 2.0 

271 Serialize multiple ``www_authenticate`` items into multiple 

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

273 into a single value, for better interoperability. 

274 

275 .. versionchanged:: 0.15.3 

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

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

278 

279 .. versionchanged:: 0.15.3 

280 The ``response`` argument was restored. 

281 

282 .. versionchanged:: 0.15.1 

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

284 its previous position. 

285 

286 .. versionchanged:: 0.15.0 

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

288 ``description``. 

289 """ 

290 

291 code = 401 

292 description = ( 

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

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

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

296 " how to supply the credentials required." 

297 ) 

298 

299 def __init__( 

300 self, 

301 description: str | None = None, 

302 response: Response | None = None, 

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

304 ) -> None: 

305 super().__init__(description, response) 

306 

307 from .datastructures import WWWAuthenticate 

308 

309 if isinstance(www_authenticate, WWWAuthenticate): 

310 www_authenticate = (www_authenticate,) 

311 

312 self.www_authenticate = www_authenticate 

313 

314 def get_headers( 

315 self, 

316 environ: WSGIEnvironment | None = None, 

317 scope: dict[str, t.Any] | None = None, 

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

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

320 if self.www_authenticate: 

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

322 return headers 

323 

324 

325class Forbidden(HTTPException): 

326 """*403* `Forbidden` 

327 

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

329 but was authenticated. 

330 """ 

331 

332 code = 403 

333 description = ( 

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

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

336 " server." 

337 ) 

338 

339 

340class NotFound(HTTPException): 

341 """*404* `Not Found` 

342 

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

344 """ 

345 

346 code = 404 

347 description = ( 

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

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

350 ) 

351 

352 

353class MethodNotAllowed(HTTPException): 

354 """*405* `Method Not Allowed` 

355 

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

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

358 

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

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

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

362 """ 

363 

364 code = 405 

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

366 

367 def __init__( 

368 self, 

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

370 description: str | None = None, 

371 response: Response | None = None, 

372 ) -> None: 

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

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

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

376 self.valid_methods = valid_methods 

377 

378 def get_headers( 

379 self, 

380 environ: WSGIEnvironment | None = None, 

381 scope: dict[str, t.Any] | None = None, 

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

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

384 if self.valid_methods: 

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

386 return headers 

387 

388 

389class NotAcceptable(HTTPException): 

390 """*406* `Not Acceptable` 

391 

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

393 `Accept` headers of the client. 

394 """ 

395 

396 code = 406 

397 description = ( 

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

399 " generating response entities which have content" 

400 " characteristics not acceptable according to the accept" 

401 " headers sent in the request." 

402 ) 

403 

404 

405class RequestTimeout(HTTPException): 

406 """*408* `Request Timeout` 

407 

408 Raise to signalize a timeout. 

409 """ 

410 

411 code = 408 

412 description = ( 

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

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

415 ) 

416 

417 

418class Conflict(HTTPException): 

419 """*409* `Conflict` 

420 

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

422 with the current state on the server. 

423 

424 .. versionadded:: 0.7 

425 """ 

426 

427 code = 409 

428 description = ( 

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

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

431 " processed." 

432 ) 

433 

434 

435class Gone(HTTPException): 

436 """*410* `Gone` 

437 

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

439 """ 

440 

441 code = 410 

442 description = ( 

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

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

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

446 ) 

447 

448 

449class LengthRequired(HTTPException): 

450 """*411* `Length Required` 

451 

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

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

454 """ 

455 

456 code = 411 

457 description = ( 

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

459 "Length</code> header." 

460 ) 

461 

462 

463class PreconditionFailed(HTTPException): 

464 """*412* `Precondition Failed` 

465 

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

467 ``If-Unmodified-Since``. 

468 """ 

469 

470 code = 412 

471 description = ( 

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

473 ) 

474 

475 

476class RequestEntityTooLarge(HTTPException): 

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

478 

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

480 limit. 

481 """ 

482 

483 code = 413 

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

485 

486 

487class RequestURITooLarge(HTTPException): 

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

489 

490 Like *413* but for too long URLs. 

491 """ 

492 

493 code = 414 

494 description = ( 

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

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

497 ) 

498 

499 

500class UnsupportedMediaType(HTTPException): 

501 """*415* `Unsupported Media Type` 

502 

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

504 the client transmitted. 

505 """ 

506 

507 code = 415 

508 description = ( 

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

510 ) 

511 

512 

513class RequestedRangeNotSatisfiable(HTTPException): 

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

515 

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

517 

518 .. versionadded:: 0.7 

519 """ 

520 

521 code = 416 

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

523 

524 def __init__( 

525 self, 

526 length: int | None = None, 

527 units: str = "bytes", 

528 description: str | None = None, 

529 response: Response | None = None, 

530 ) -> None: 

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

532 parameter. 

533 """ 

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

535 self.length = length 

536 self.units = units 

537 

538 def get_headers( 

539 self, 

540 environ: WSGIEnvironment | None = None, 

541 scope: dict[str, t.Any] | None = None, 

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

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

544 if self.length is not None: 

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

546 return headers 

547 

548 

549class ExpectationFailed(HTTPException): 

550 """*417* `Expectation Failed` 

551 

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

553 

554 .. versionadded:: 0.7 

555 """ 

556 

557 code = 417 

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

559 

560 

561class ImATeapot(HTTPException): 

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

563 

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

565 to brew coffee with it. 

566 

567 .. versionadded:: 0.7 

568 """ 

569 

570 code = 418 

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

572 

573 

574class UnprocessableEntity(HTTPException): 

575 """*422* `Unprocessable Entity` 

576 

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

578 incorrect. 

579 """ 

580 

581 code = 422 

582 description = ( 

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

584 " to semantic errors." 

585 ) 

586 

587 

588class Locked(HTTPException): 

589 """*423* `Locked` 

590 

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

592 """ 

593 

594 code = 423 

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

596 

597 

598class FailedDependency(HTTPException): 

599 """*424* `Failed Dependency` 

600 

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

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

603 """ 

604 

605 code = 424 

606 description = ( 

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

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

609 " failed." 

610 ) 

611 

612 

613class PreconditionRequired(HTTPException): 

614 """*428* `Precondition Required` 

615 

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

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

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

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

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

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

622 the resource. 

623 """ 

624 

625 code = 428 

626 description = ( 

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

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

629 ) 

630 

631 

632class _RetryAfter(HTTPException): 

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

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

635 a :class:`~datetime.datetime`. 

636 """ 

637 

638 def __init__( 

639 self, 

640 description: str | None = None, 

641 response: Response | None = None, 

642 retry_after: datetime | int | None = None, 

643 ) -> None: 

644 super().__init__(description, response) 

645 self.retry_after = retry_after 

646 

647 def get_headers( 

648 self, 

649 environ: WSGIEnvironment | None = None, 

650 scope: dict[str, t.Any] | None = None, 

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

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

653 

654 if self.retry_after: 

655 if isinstance(self.retry_after, datetime): 

656 from .http import http_date 

657 

658 value = http_date(self.retry_after) 

659 else: 

660 value = str(self.retry_after) 

661 

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

663 

664 return headers 

665 

666 

667class TooManyRequests(_RetryAfter): 

668 """*429* `Too Many Requests` 

669 

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

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

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

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

674 the user should wait before retrying. 

675 

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

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

678 :class:`~datetime.datetime`. 

679 

680 .. versionchanged:: 1.0 

681 Added ``retry_after`` parameter. 

682 """ 

683 

684 code = 429 

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

686 

687 

688class RequestHeaderFieldsTooLarge(HTTPException): 

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

690 

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

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

693 headers is too large. 

694 """ 

695 

696 code = 431 

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

698 

699 

700class UnavailableForLegalReasons(HTTPException): 

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

702 

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

704 resource as a consequence of a legal demand. 

705 """ 

706 

707 code = 451 

708 description = "Unavailable for legal reasons." 

709 

710 

711class InternalServerError(HTTPException): 

712 """*500* `Internal Server Error` 

713 

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

715 unknown error occurred in the dispatcher. 

716 

717 .. versionchanged:: 1.0.0 

718 Added the :attr:`original_exception` attribute. 

719 """ 

720 

721 code = 500 

722 description = ( 

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

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

725 " there is an error in the application." 

726 ) 

727 

728 def __init__( 

729 self, 

730 description: str | None = None, 

731 response: Response | None = None, 

732 original_exception: BaseException | None = None, 

733 ) -> None: 

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

735 #: used by frameworks to provide context when handling 

736 #: unexpected errors. 

737 self.original_exception = original_exception 

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

739 

740 

741class NotImplemented(HTTPException): 

742 """*501* `Not Implemented` 

743 

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

745 browser. 

746 """ 

747 

748 code = 501 

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

750 

751 

752class BadGateway(HTTPException): 

753 """*502* `Bad Gateway` 

754 

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

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

757 in attempting to fulfill the request. 

758 """ 

759 

760 code = 502 

761 description = ( 

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

763 ) 

764 

765 

766class ServiceUnavailable(_RetryAfter): 

767 """*503* `Service Unavailable` 

768 

769 Status code you should return if a service is temporarily 

770 unavailable. 

771 

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

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

774 :class:`~datetime.datetime`. 

775 

776 .. versionchanged:: 1.0 

777 Added ``retry_after`` parameter. 

778 """ 

779 

780 code = 503 

781 description = ( 

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

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

784 " again later." 

785 ) 

786 

787 

788class GatewayTimeout(HTTPException): 

789 """*504* `Gateway Timeout` 

790 

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

792 times out. 

793 """ 

794 

795 code = 504 

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

797 

798 

799class HTTPVersionNotSupported(HTTPException): 

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

801 

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

803 """ 

804 

805 code = 505 

806 description = ( 

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

808 ) 

809 

810 

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

812 

813 

814def _find_exceptions() -> None: 

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

816 try: 

817 is_http_exception = issubclass(obj, HTTPException) 

818 except TypeError: 

819 is_http_exception = False 

820 if not is_http_exception or obj.code is None: 

821 continue 

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

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

824 continue 

825 default_exceptions[obj.code] = obj 

826 

827 

828_find_exceptions() 

829del _find_exceptions 

830 

831 

832class Aborter: 

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

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

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

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

837 

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

839 """ 

840 

841 def __init__( 

842 self, 

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

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

845 ) -> None: 

846 if mapping is None: 

847 mapping = default_exceptions 

848 self.mapping = dict(mapping) 

849 if extra is not None: 

850 self.mapping.update(extra) 

851 

852 def __call__( 

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

854 ) -> t.NoReturn: 

855 from .sansio.response import Response 

856 

857 if isinstance(code, Response): 

858 raise HTTPException(response=code) 

859 

860 if code not in self.mapping: 

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

862 

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

864 

865 

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

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

868 application. 

869 

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

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

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

873 

874 abort(404) # 404 Not Found 

875 abort(Response('Hello World')) 

876 

877 """ 

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

879 

880 

881_aborter: Aborter = Aborter()