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

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

241 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: object | 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 MisdirectedRequest(HTTPException): 

575 """421 Misdirected Request 

576 

577 Indicates that the request was directed to a server that is not able to 

578 produce a response. 

579 

580 .. versionadded:: 3.1 

581 """ 

582 

583 code = 421 

584 description = "The server is not able to produce a response." 

585 

586 

587class UnprocessableEntity(HTTPException): 

588 """*422* `Unprocessable Entity` 

589 

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

591 incorrect. 

592 """ 

593 

594 code = 422 

595 description = ( 

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

597 " to semantic errors." 

598 ) 

599 

600 

601class Locked(HTTPException): 

602 """*423* `Locked` 

603 

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

605 """ 

606 

607 code = 423 

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

609 

610 

611class FailedDependency(HTTPException): 

612 """*424* `Failed Dependency` 

613 

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

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

616 """ 

617 

618 code = 424 

619 description = ( 

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

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

622 " failed." 

623 ) 

624 

625 

626class PreconditionRequired(HTTPException): 

627 """*428* `Precondition Required` 

628 

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

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

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

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

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

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

635 the resource. 

636 """ 

637 

638 code = 428 

639 description = ( 

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

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

642 ) 

643 

644 

645class _RetryAfter(HTTPException): 

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

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

648 a :class:`~datetime.datetime`. 

649 """ 

650 

651 def __init__( 

652 self, 

653 description: str | None = None, 

654 response: Response | None = None, 

655 retry_after: datetime | int | None = None, 

656 ) -> None: 

657 super().__init__(description, response) 

658 self.retry_after = retry_after 

659 

660 def get_headers( 

661 self, 

662 environ: WSGIEnvironment | None = None, 

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

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

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

666 

667 if self.retry_after: 

668 if isinstance(self.retry_after, datetime): 

669 from .http import http_date 

670 

671 value = http_date(self.retry_after) 

672 else: 

673 value = str(self.retry_after) 

674 

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

676 

677 return headers 

678 

679 

680class TooManyRequests(_RetryAfter): 

681 """*429* `Too Many Requests` 

682 

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

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

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

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

687 the user should wait before retrying. 

688 

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

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

691 :class:`~datetime.datetime`. 

692 

693 .. versionchanged:: 1.0 

694 Added ``retry_after`` parameter. 

695 """ 

696 

697 code = 429 

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

699 

700 

701class RequestHeaderFieldsTooLarge(HTTPException): 

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

703 

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

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

706 headers is too large. 

707 """ 

708 

709 code = 431 

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

711 

712 

713class UnavailableForLegalReasons(HTTPException): 

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

715 

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

717 resource as a consequence of a legal demand. 

718 """ 

719 

720 code = 451 

721 description = "Unavailable for legal reasons." 

722 

723 

724class InternalServerError(HTTPException): 

725 """*500* `Internal Server Error` 

726 

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

728 unknown error occurred in the dispatcher. 

729 

730 .. versionchanged:: 1.0.0 

731 Added the :attr:`original_exception` attribute. 

732 """ 

733 

734 code = 500 

735 description = ( 

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

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

738 " there is an error in the application." 

739 ) 

740 

741 def __init__( 

742 self, 

743 description: str | None = None, 

744 response: Response | None = None, 

745 original_exception: BaseException | None = None, 

746 ) -> None: 

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

748 #: used by frameworks to provide context when handling 

749 #: unexpected errors. 

750 self.original_exception = original_exception 

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

752 

753 

754class NotImplemented(HTTPException): 

755 """*501* `Not Implemented` 

756 

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

758 browser. 

759 """ 

760 

761 code = 501 

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

763 

764 

765class BadGateway(HTTPException): 

766 """*502* `Bad Gateway` 

767 

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

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

770 in attempting to fulfill the request. 

771 """ 

772 

773 code = 502 

774 description = ( 

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

776 ) 

777 

778 

779class ServiceUnavailable(_RetryAfter): 

780 """*503* `Service Unavailable` 

781 

782 Status code you should return if a service is temporarily 

783 unavailable. 

784 

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

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

787 :class:`~datetime.datetime`. 

788 

789 .. versionchanged:: 1.0 

790 Added ``retry_after`` parameter. 

791 """ 

792 

793 code = 503 

794 description = ( 

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

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

797 " again later." 

798 ) 

799 

800 

801class GatewayTimeout(HTTPException): 

802 """*504* `Gateway Timeout` 

803 

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

805 times out. 

806 """ 

807 

808 code = 504 

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

810 

811 

812class HTTPVersionNotSupported(HTTPException): 

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

814 

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

816 """ 

817 

818 code = 505 

819 description = ( 

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

821 ) 

822 

823 

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

825 

826 

827def _find_exceptions() -> None: 

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

829 try: 

830 is_http_exception = issubclass(obj, HTTPException) 

831 except TypeError: 

832 is_http_exception = False 

833 if not is_http_exception or obj.code is None: 

834 continue 

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

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

837 continue 

838 default_exceptions[obj.code] = obj 

839 

840 

841_find_exceptions() 

842del _find_exceptions 

843 

844 

845class Aborter: 

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

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

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

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

850 

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

852 """ 

853 

854 def __init__( 

855 self, 

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

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

858 ) -> None: 

859 if mapping is None: 

860 mapping = default_exceptions 

861 self.mapping = dict(mapping) 

862 if extra is not None: 

863 self.mapping.update(extra) 

864 

865 def __call__( 

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

867 ) -> t.NoReturn: 

868 from .sansio.response import Response 

869 

870 if isinstance(code, Response): 

871 raise HTTPException(response=code) 

872 

873 if code not in self.mapping: 

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

875 

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

877 

878 

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

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

881 application. 

882 

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

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

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

886 

887 abort(404) # 404 Not Found 

888 abort(Response('Hello World')) 

889 

890 """ 

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

892 

893 

894_aborter: Aborter = Aborter()