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

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

248 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 

51from http import HTTPStatus 

52 

53from markupsafe import escape 

54from markupsafe import Markup 

55 

56from ._internal import _get_environ 

57 

58if t.TYPE_CHECKING: 

59 from _typeshed.wsgi import StartResponse 

60 from _typeshed.wsgi import WSGIEnvironment 

61 

62 from .datastructures import WWWAuthenticate 

63 from .sansio.response import Response as SansIOResponse 

64 from .wrappers.request import Request as WSGIRequest 

65 from .wrappers.response import Response as WSGIResponse 

66 

67 

68class HTTPException(Exception): 

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

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

71 of it independently and render nicer error messages. 

72 

73 .. versionchanged:: 2.1 

74 Removed the ``wrap`` class method. 

75 """ 

76 

77 code: int | None = None 

78 description: str | None = None 

79 

80 def __init__( 

81 self, 

82 description: str | None = None, 

83 response: SansIOResponse | None = None, 

84 ) -> None: 

85 super().__init__() 

86 if description is not None: 

87 self.description = description 

88 self.response = response 

89 

90 @property 

91 def name(self) -> str: 

92 """The status name.""" 

93 try: 

94 return HTTPStatus(self.code or 0).phrase 

95 except ValueError: 

96 return "Unknown" 

97 

98 def get_description( 

99 self, 

100 environ: WSGIEnvironment | None = None, 

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

102 ) -> str: 

103 """Get the description.""" 

104 if self.description is None: 

105 description = "" 

106 else: 

107 description = self.description 

108 

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

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

111 

112 def get_body( 

113 self, 

114 environ: WSGIEnvironment | None = None, 

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

116 ) -> str: 

117 """Get the HTML body.""" 

118 return ( 

119 "<!doctype html>\n" 

120 "<html lang=en>\n" 

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

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

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

124 ) 

125 

126 def get_headers( 

127 self, 

128 environ: WSGIEnvironment | None = None, 

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

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

131 """Get a list of headers.""" 

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

133 

134 @t.overload 

135 def get_response( 

136 self, 

137 environ: WSGIEnvironment | WSGIRequest | None = ..., 

138 scope: None = None, 

139 ) -> WSGIResponse: ... 

140 @t.overload 

141 def get_response( 

142 self, 

143 environ: None = None, 

144 scope: dict[str, t.Any] = ..., 

145 ) -> SansIOResponse: ... 

146 def get_response( 

147 self, 

148 environ: WSGIEnvironment | WSGIRequest | None = None, 

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

150 ) -> WSGIResponse | SansIOResponse: 

151 """Get a response object. 

152 

153 :param environ: A WSGI environ dict or request object. If given, may be 

154 used to customize the response based on the request. 

155 :param scope: An ASGI scope dict. If given, may be used to customize the 

156 response based on the request. 

157 :return: A WSGI :class:`werkzeug.wrappers.Response` if called without 

158 arguments or with ``environ``. A sans-IO 

159 :class:`werkzeug.sansio.Response` for ASGI if called with 

160 ``scope``. 

161 """ 

162 from .wrappers.response import Response 

163 

164 if self.response is not None: 

165 return self.response 

166 if environ is not None: 

167 environ = _get_environ(environ) 

168 headers = self.get_headers(environ, scope) 

169 return Response(self.get_body(environ, scope), self.code, headers) 

170 

171 def __call__( 

172 self, environ: WSGIEnvironment, start_response: StartResponse 

173 ) -> t.Iterable[bytes]: 

174 """Call the exception as WSGI application. 

175 

176 :param environ: the WSGI environment. 

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

178 server. 

179 """ 

180 response = self.get_response(environ) 

181 return response(environ, start_response) 

182 

183 def __str__(self) -> str: 

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

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

186 

187 def __repr__(self) -> str: 

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

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

190 

191 

192class BadRequest(HTTPException): 

193 """*400* `Bad Request` 

194 

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

196 or server cannot handle. 

197 """ 

198 

199 code = 400 

200 description = ( 

201 "The browser (or proxy) sent a request that this server could not understand." 

202 ) 

203 

204 

205class BadRequestKeyError(BadRequest, KeyError): 

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

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

208 """ 

209 

210 _description = BadRequest.description 

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

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

213 #: useful in a debug mode. 

214 show_exception = False 

215 

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

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

218 

219 if arg is None: 

220 KeyError.__init__(self) 

221 else: 

222 KeyError.__init__(self, arg) 

223 

224 @property 

225 def description(self) -> str: 

226 if self.show_exception: 

227 return f"{self._description}\n{KeyError.__name__}: {KeyError.__str__(self)}" 

228 

229 return self._description 

230 

231 @description.setter 

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

233 self._description = value 

234 

235 

236class ClientDisconnected(BadRequest): 

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

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

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

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

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

242 

243 Since disconnections cannot be reliably detected and are unspecified 

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

245 is gone. 

246 

247 .. versionadded:: 0.8 

248 """ 

249 

250 

251class SecurityError(BadRequest): 

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

253 exactly like a bad request error. 

254 

255 .. versionadded:: 0.9 

256 """ 

257 

258 

259class BadHost(BadRequest): 

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

261 

262 .. versionadded:: 0.11.2 

263 """ 

264 

265 

266class Unauthorized(HTTPException): 

267 """*401* ``Unauthorized`` 

268 

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

270 

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

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

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

274 to create correctly formatted values. Strictly speaking a 401 

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

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

277 

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

279 of the response. 

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

281 WWW-Authenticate header(s). 

282 

283 .. versionchanged:: 2.0 

284 Serialize multiple ``www_authenticate`` items into multiple 

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

286 into a single value, for better interoperability. 

287 

288 .. versionchanged:: 0.15.3 

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

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

291 

292 .. versionchanged:: 0.15.3 

293 The ``response`` argument was restored. 

294 

295 .. versionchanged:: 0.15.1 

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

297 its previous position. 

298 

299 .. versionchanged:: 0.15.0 

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

301 ``description``. 

302 """ 

303 

304 code = 401 

305 description = ( 

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

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

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

309 " how to supply the credentials required." 

310 ) 

311 

312 def __init__( 

313 self, 

314 description: str | None = None, 

315 response: SansIOResponse | None = None, 

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

317 ) -> None: 

318 super().__init__(description, response) 

319 

320 from .datastructures import WWWAuthenticate 

321 

322 if isinstance(www_authenticate, WWWAuthenticate): 

323 www_authenticate = (www_authenticate,) 

324 

325 self.www_authenticate = www_authenticate 

326 

327 def get_headers( 

328 self, 

329 environ: WSGIEnvironment | None = None, 

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

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

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

333 if self.www_authenticate: 

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

335 return headers 

336 

337 

338class Forbidden(HTTPException): 

339 """*403* `Forbidden` 

340 

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

342 but was authenticated. 

343 """ 

344 

345 code = 403 

346 description = ( 

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

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

349 " server." 

350 ) 

351 

352 

353class NotFound(HTTPException): 

354 """*404* `Not Found` 

355 

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

357 """ 

358 

359 code = 404 

360 description = ( 

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

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

363 ) 

364 

365 

366class MethodNotAllowed(HTTPException): 

367 """*405* `Method Not Allowed` 

368 

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

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

371 

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

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

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

375 """ 

376 

377 code = 405 

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

379 

380 def __init__( 

381 self, 

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

383 description: str | None = None, 

384 response: SansIOResponse | None = None, 

385 ) -> None: 

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

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

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

389 self.valid_methods = valid_methods 

390 

391 def get_headers( 

392 self, 

393 environ: WSGIEnvironment | None = None, 

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

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

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

397 if self.valid_methods: 

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

399 return headers 

400 

401 

402class NotAcceptable(HTTPException): 

403 """*406* `Not Acceptable` 

404 

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

406 `Accept` headers of the client. 

407 """ 

408 

409 code = 406 

410 description = ( 

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

412 " generating response entities which have content" 

413 " characteristics not acceptable according to the accept" 

414 " headers sent in the request." 

415 ) 

416 

417 

418class RequestTimeout(HTTPException): 

419 """*408* `Request Timeout` 

420 

421 Raise to signalize a timeout. 

422 """ 

423 

424 code = 408 

425 description = ( 

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

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

428 ) 

429 

430 

431class Conflict(HTTPException): 

432 """*409* `Conflict` 

433 

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

435 with the current state on the server. 

436 

437 .. versionadded:: 0.7 

438 """ 

439 

440 code = 409 

441 description = ( 

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

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

444 " processed." 

445 ) 

446 

447 

448class Gone(HTTPException): 

449 """*410* `Gone` 

450 

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

452 """ 

453 

454 code = 410 

455 description = ( 

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

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

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

459 ) 

460 

461 

462class LengthRequired(HTTPException): 

463 """*411* `Length Required` 

464 

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

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

467 """ 

468 

469 code = 411 

470 description = ( 

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

472 "Length</code> header." 

473 ) 

474 

475 

476class PreconditionFailed(HTTPException): 

477 """*412* `Precondition Failed` 

478 

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

480 ``If-Unmodified-Since``. 

481 """ 

482 

483 code = 412 

484 description = ( 

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

486 ) 

487 

488 

489class RequestEntityTooLarge(HTTPException): 

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

491 

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

493 limit. 

494 """ 

495 

496 code = 413 

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

498 

499 

500class RequestURITooLarge(HTTPException): 

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

502 

503 Like *413* but for too long URLs. 

504 """ 

505 

506 code = 414 

507 description = ( 

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

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

510 ) 

511 

512 

513class UnsupportedMediaType(HTTPException): 

514 """*415* `Unsupported Media Type` 

515 

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

517 the client transmitted. 

518 """ 

519 

520 code = 415 

521 description = ( 

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

523 ) 

524 

525 

526class RequestedRangeNotSatisfiable(HTTPException): 

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

528 

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

530 

531 .. versionadded:: 0.7 

532 """ 

533 

534 code = 416 

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

536 

537 def __init__( 

538 self, 

539 length: int | None = None, 

540 units: str = "bytes", 

541 description: str | None = None, 

542 response: SansIOResponse | None = None, 

543 ) -> None: 

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

545 parameter. 

546 """ 

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

548 self.length = length 

549 self.units = units 

550 

551 def get_headers( 

552 self, 

553 environ: WSGIEnvironment | None = None, 

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

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

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

557 if self.length is not None: 

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

559 return headers 

560 

561 

562class ExpectationFailed(HTTPException): 

563 """*417* `Expectation Failed` 

564 

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

566 

567 .. versionadded:: 0.7 

568 """ 

569 

570 code = 417 

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

572 

573 

574class ImATeapot(HTTPException): 

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

576 

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

578 to brew coffee with it. 

579 

580 .. versionadded:: 0.7 

581 """ 

582 

583 code = 418 

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

585 

586 

587class MisdirectedRequest(HTTPException): 

588 """421 Misdirected Request 

589 

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

591 produce a response. 

592 

593 .. versionadded:: 3.1 

594 """ 

595 

596 code = 421 

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

598 

599 

600class UnprocessableEntity(HTTPException): 

601 """*422* `Unprocessable Entity` 

602 

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

604 incorrect. 

605 """ 

606 

607 code = 422 

608 description = ( 

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

610 " to semantic errors." 

611 ) 

612 

613 

614class Locked(HTTPException): 

615 """*423* `Locked` 

616 

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

618 """ 

619 

620 code = 423 

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

622 

623 

624class FailedDependency(HTTPException): 

625 """*424* `Failed Dependency` 

626 

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

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

629 """ 

630 

631 code = 424 

632 description = ( 

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

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

635 " failed." 

636 ) 

637 

638 

639class PreconditionRequired(HTTPException): 

640 """*428* `Precondition Required` 

641 

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

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

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

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

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

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

648 the resource. 

649 """ 

650 

651 code = 428 

652 description = ( 

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

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

655 ) 

656 

657 

658class _RetryAfter(HTTPException): 

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

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

661 a :class:`~datetime.datetime`. 

662 """ 

663 

664 def __init__( 

665 self, 

666 description: str | None = None, 

667 response: SansIOResponse | None = None, 

668 retry_after: datetime | int | None = None, 

669 ) -> None: 

670 super().__init__(description, response) 

671 self.retry_after = retry_after 

672 

673 def get_headers( 

674 self, 

675 environ: WSGIEnvironment | None = None, 

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

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

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

679 

680 if self.retry_after: 

681 if isinstance(self.retry_after, datetime): 

682 from .http import http_date 

683 

684 value = http_date(self.retry_after) 

685 else: 

686 value = str(self.retry_after) 

687 

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

689 

690 return headers 

691 

692 

693class TooManyRequests(_RetryAfter): 

694 """*429* `Too Many Requests` 

695 

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

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

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

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

700 the user should wait before retrying. 

701 

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

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

704 :class:`~datetime.datetime`. 

705 

706 .. versionchanged:: 1.0 

707 Added ``retry_after`` parameter. 

708 """ 

709 

710 code = 429 

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

712 

713 

714class RequestHeaderFieldsTooLarge(HTTPException): 

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

716 

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

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

719 headers is too large. 

720 """ 

721 

722 code = 431 

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

724 

725 

726class UnavailableForLegalReasons(HTTPException): 

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

728 

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

730 resource as a consequence of a legal demand. 

731 """ 

732 

733 code = 451 

734 description = "Unavailable for legal reasons." 

735 

736 

737class InternalServerError(HTTPException): 

738 """*500* `Internal Server Error` 

739 

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

741 unknown error occurred in the dispatcher. 

742 

743 .. versionchanged:: 1.0.0 

744 Added the :attr:`original_exception` attribute. 

745 """ 

746 

747 code = 500 

748 description = ( 

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

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

751 " there is an error in the application." 

752 ) 

753 

754 def __init__( 

755 self, 

756 description: str | None = None, 

757 response: SansIOResponse | None = None, 

758 original_exception: BaseException | None = None, 

759 ) -> None: 

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

761 #: used by frameworks to provide context when handling 

762 #: unexpected errors. 

763 self.original_exception = original_exception 

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

765 

766 

767class NotImplemented(HTTPException): 

768 """*501* `Not Implemented` 

769 

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

771 browser. 

772 """ 

773 

774 code = 501 

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

776 

777 

778class BadGateway(HTTPException): 

779 """*502* `Bad Gateway` 

780 

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

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

783 in attempting to fulfill the request. 

784 """ 

785 

786 code = 502 

787 description = ( 

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

789 ) 

790 

791 

792class ServiceUnavailable(_RetryAfter): 

793 """*503* `Service Unavailable` 

794 

795 Status code you should return if a service is temporarily 

796 unavailable. 

797 

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

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

800 :class:`~datetime.datetime`. 

801 

802 .. versionchanged:: 1.0 

803 Added ``retry_after`` parameter. 

804 """ 

805 

806 code = 503 

807 description = ( 

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

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

810 " again later." 

811 ) 

812 

813 

814class GatewayTimeout(HTTPException): 

815 """*504* `Gateway Timeout` 

816 

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

818 times out. 

819 """ 

820 

821 code = 504 

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

823 

824 

825class HTTPVersionNotSupported(HTTPException): 

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

827 

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

829 """ 

830 

831 code = 505 

832 description = ( 

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

834 ) 

835 

836 

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

838 

839 

840def _find_exceptions() -> None: 

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

842 try: 

843 is_http_exception = issubclass(obj, HTTPException) 

844 except TypeError: 

845 is_http_exception = False 

846 if not is_http_exception or obj.code is None: 

847 continue 

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

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

850 continue 

851 default_exceptions[obj.code] = obj 

852 

853 

854_find_exceptions() 

855del _find_exceptions 

856 

857 

858class Aborter: 

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

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

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

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

863 

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

865 """ 

866 

867 def __init__( 

868 self, 

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

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

871 ) -> None: 

872 if mapping is None: 

873 mapping = default_exceptions 

874 self.mapping = dict(mapping) 

875 if extra is not None: 

876 self.mapping.update(extra) 

877 

878 def __call__( 

879 self, code: int | SansIOResponse, *args: t.Any, **kwargs: t.Any 

880 ) -> t.NoReturn: 

881 from .sansio.response import Response 

882 

883 if isinstance(code, Response): 

884 raise HTTPException(response=code) 

885 

886 if code not in self.mapping: 

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

888 

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

890 

891 

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

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

894 application. 

895 

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

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

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

899 

900 abort(404) # 404 Not Found 

901 abort(Response('Hello World')) 

902 

903 """ 

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

905 

906 

907_aborter: Aborter = Aborter()