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

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

245 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 as SansIOResponse 

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: SansIOResponse | 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 @t.overload 

133 def get_response( 

134 self, 

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

136 scope: None = None, 

137 ) -> WSGIResponse: ... 

138 @t.overload 

139 def get_response( 

140 self, 

141 environ: None = None, 

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

143 ) -> SansIOResponse: ... 

144 def get_response( 

145 self, 

146 environ: WSGIEnvironment | WSGIRequest | None = None, 

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

148 ) -> WSGIResponse | SansIOResponse: 

149 """Get a response object. 

150 

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

152 used to customize the response based on the request. 

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

154 response based on the request. 

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

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

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

158 ``scope``. 

159 """ 

160 from .wrappers.response import Response 

161 

162 if self.response is not None: 

163 return self.response 

164 if environ is not None: 

165 environ = _get_environ(environ) 

166 headers = self.get_headers(environ, scope) 

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

168 

169 def __call__( 

170 self, environ: WSGIEnvironment, start_response: StartResponse 

171 ) -> t.Iterable[bytes]: 

172 """Call the exception as WSGI application. 

173 

174 :param environ: the WSGI environment. 

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

176 server. 

177 """ 

178 response = self.get_response(environ) 

179 return response(environ, start_response) 

180 

181 def __str__(self) -> str: 

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

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

184 

185 def __repr__(self) -> str: 

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

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

188 

189 

190class BadRequest(HTTPException): 

191 """*400* `Bad Request` 

192 

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

194 or server cannot handle. 

195 """ 

196 

197 code = 400 

198 description = ( 

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

200 ) 

201 

202 

203class BadRequestKeyError(BadRequest, KeyError): 

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

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

206 """ 

207 

208 _description = BadRequest.description 

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

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

211 #: useful in a debug mode. 

212 show_exception = False 

213 

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

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

216 

217 if arg is None: 

218 KeyError.__init__(self) 

219 else: 

220 KeyError.__init__(self, arg) 

221 

222 @property 

223 def description(self) -> str: 

224 if self.show_exception: 

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

226 

227 return self._description 

228 

229 @description.setter 

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

231 self._description = value 

232 

233 

234class ClientDisconnected(BadRequest): 

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

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

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

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

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

240 

241 Since disconnections cannot be reliably detected and are unspecified 

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

243 is gone. 

244 

245 .. versionadded:: 0.8 

246 """ 

247 

248 

249class SecurityError(BadRequest): 

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

251 exactly like a bad request error. 

252 

253 .. versionadded:: 0.9 

254 """ 

255 

256 

257class BadHost(BadRequest): 

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

259 

260 .. versionadded:: 0.11.2 

261 """ 

262 

263 

264class Unauthorized(HTTPException): 

265 """*401* ``Unauthorized`` 

266 

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

268 

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

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

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

272 to create correctly formatted values. Strictly speaking a 401 

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

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

275 

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

277 of the response. 

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

279 WWW-Authenticate header(s). 

280 

281 .. versionchanged:: 2.0 

282 Serialize multiple ``www_authenticate`` items into multiple 

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

284 into a single value, for better interoperability. 

285 

286 .. versionchanged:: 0.15.3 

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

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

289 

290 .. versionchanged:: 0.15.3 

291 The ``response`` argument was restored. 

292 

293 .. versionchanged:: 0.15.1 

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

295 its previous position. 

296 

297 .. versionchanged:: 0.15.0 

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

299 ``description``. 

300 """ 

301 

302 code = 401 

303 description = ( 

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

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

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

307 " how to supply the credentials required." 

308 ) 

309 

310 def __init__( 

311 self, 

312 description: str | None = None, 

313 response: SansIOResponse | None = None, 

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

315 ) -> None: 

316 super().__init__(description, response) 

317 

318 from .datastructures import WWWAuthenticate 

319 

320 if isinstance(www_authenticate, WWWAuthenticate): 

321 www_authenticate = (www_authenticate,) 

322 

323 self.www_authenticate = www_authenticate 

324 

325 def get_headers( 

326 self, 

327 environ: WSGIEnvironment | None = None, 

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

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

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

331 if self.www_authenticate: 

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

333 return headers 

334 

335 

336class Forbidden(HTTPException): 

337 """*403* `Forbidden` 

338 

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

340 but was authenticated. 

341 """ 

342 

343 code = 403 

344 description = ( 

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

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

347 " server." 

348 ) 

349 

350 

351class NotFound(HTTPException): 

352 """*404* `Not Found` 

353 

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

355 """ 

356 

357 code = 404 

358 description = ( 

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

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

361 ) 

362 

363 

364class MethodNotAllowed(HTTPException): 

365 """*405* `Method Not Allowed` 

366 

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

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

369 

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

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

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

373 """ 

374 

375 code = 405 

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

377 

378 def __init__( 

379 self, 

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

381 description: str | None = None, 

382 response: SansIOResponse | None = None, 

383 ) -> None: 

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

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

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

387 self.valid_methods = valid_methods 

388 

389 def get_headers( 

390 self, 

391 environ: WSGIEnvironment | None = None, 

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

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

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

395 if self.valid_methods: 

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

397 return headers 

398 

399 

400class NotAcceptable(HTTPException): 

401 """*406* `Not Acceptable` 

402 

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

404 `Accept` headers of the client. 

405 """ 

406 

407 code = 406 

408 description = ( 

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

410 " generating response entities which have content" 

411 " characteristics not acceptable according to the accept" 

412 " headers sent in the request." 

413 ) 

414 

415 

416class RequestTimeout(HTTPException): 

417 """*408* `Request Timeout` 

418 

419 Raise to signalize a timeout. 

420 """ 

421 

422 code = 408 

423 description = ( 

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

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

426 ) 

427 

428 

429class Conflict(HTTPException): 

430 """*409* `Conflict` 

431 

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

433 with the current state on the server. 

434 

435 .. versionadded:: 0.7 

436 """ 

437 

438 code = 409 

439 description = ( 

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

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

442 " processed." 

443 ) 

444 

445 

446class Gone(HTTPException): 

447 """*410* `Gone` 

448 

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

450 """ 

451 

452 code = 410 

453 description = ( 

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

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

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

457 ) 

458 

459 

460class LengthRequired(HTTPException): 

461 """*411* `Length Required` 

462 

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

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

465 """ 

466 

467 code = 411 

468 description = ( 

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

470 "Length</code> header." 

471 ) 

472 

473 

474class PreconditionFailed(HTTPException): 

475 """*412* `Precondition Failed` 

476 

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

478 ``If-Unmodified-Since``. 

479 """ 

480 

481 code = 412 

482 description = ( 

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

484 ) 

485 

486 

487class RequestEntityTooLarge(HTTPException): 

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

489 

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

491 limit. 

492 """ 

493 

494 code = 413 

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

496 

497 

498class RequestURITooLarge(HTTPException): 

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

500 

501 Like *413* but for too long URLs. 

502 """ 

503 

504 code = 414 

505 description = ( 

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

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

508 ) 

509 

510 

511class UnsupportedMediaType(HTTPException): 

512 """*415* `Unsupported Media Type` 

513 

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

515 the client transmitted. 

516 """ 

517 

518 code = 415 

519 description = ( 

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

521 ) 

522 

523 

524class RequestedRangeNotSatisfiable(HTTPException): 

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

526 

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

528 

529 .. versionadded:: 0.7 

530 """ 

531 

532 code = 416 

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

534 

535 def __init__( 

536 self, 

537 length: int | None = None, 

538 units: str = "bytes", 

539 description: str | None = None, 

540 response: SansIOResponse | None = None, 

541 ) -> None: 

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

543 parameter. 

544 """ 

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

546 self.length = length 

547 self.units = units 

548 

549 def get_headers( 

550 self, 

551 environ: WSGIEnvironment | None = None, 

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

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

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

555 if self.length is not None: 

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

557 return headers 

558 

559 

560class ExpectationFailed(HTTPException): 

561 """*417* `Expectation Failed` 

562 

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

564 

565 .. versionadded:: 0.7 

566 """ 

567 

568 code = 417 

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

570 

571 

572class ImATeapot(HTTPException): 

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

574 

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

576 to brew coffee with it. 

577 

578 .. versionadded:: 0.7 

579 """ 

580 

581 code = 418 

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

583 

584 

585class MisdirectedRequest(HTTPException): 

586 """421 Misdirected Request 

587 

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

589 produce a response. 

590 

591 .. versionadded:: 3.1 

592 """ 

593 

594 code = 421 

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

596 

597 

598class UnprocessableEntity(HTTPException): 

599 """*422* `Unprocessable Entity` 

600 

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

602 incorrect. 

603 """ 

604 

605 code = 422 

606 description = ( 

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

608 " to semantic errors." 

609 ) 

610 

611 

612class Locked(HTTPException): 

613 """*423* `Locked` 

614 

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

616 """ 

617 

618 code = 423 

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

620 

621 

622class FailedDependency(HTTPException): 

623 """*424* `Failed Dependency` 

624 

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

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

627 """ 

628 

629 code = 424 

630 description = ( 

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

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

633 " failed." 

634 ) 

635 

636 

637class PreconditionRequired(HTTPException): 

638 """*428* `Precondition Required` 

639 

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

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

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

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

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

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

646 the resource. 

647 """ 

648 

649 code = 428 

650 description = ( 

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

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

653 ) 

654 

655 

656class _RetryAfter(HTTPException): 

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

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

659 a :class:`~datetime.datetime`. 

660 """ 

661 

662 def __init__( 

663 self, 

664 description: str | None = None, 

665 response: SansIOResponse | None = None, 

666 retry_after: datetime | int | None = None, 

667 ) -> None: 

668 super().__init__(description, response) 

669 self.retry_after = retry_after 

670 

671 def get_headers( 

672 self, 

673 environ: WSGIEnvironment | None = None, 

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

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

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

677 

678 if self.retry_after: 

679 if isinstance(self.retry_after, datetime): 

680 from .http import http_date 

681 

682 value = http_date(self.retry_after) 

683 else: 

684 value = str(self.retry_after) 

685 

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

687 

688 return headers 

689 

690 

691class TooManyRequests(_RetryAfter): 

692 """*429* `Too Many Requests` 

693 

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

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

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

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

698 the user should wait before retrying. 

699 

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

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

702 :class:`~datetime.datetime`. 

703 

704 .. versionchanged:: 1.0 

705 Added ``retry_after`` parameter. 

706 """ 

707 

708 code = 429 

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

710 

711 

712class RequestHeaderFieldsTooLarge(HTTPException): 

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

714 

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

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

717 headers is too large. 

718 """ 

719 

720 code = 431 

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

722 

723 

724class UnavailableForLegalReasons(HTTPException): 

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

726 

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

728 resource as a consequence of a legal demand. 

729 """ 

730 

731 code = 451 

732 description = "Unavailable for legal reasons." 

733 

734 

735class InternalServerError(HTTPException): 

736 """*500* `Internal Server Error` 

737 

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

739 unknown error occurred in the dispatcher. 

740 

741 .. versionchanged:: 1.0.0 

742 Added the :attr:`original_exception` attribute. 

743 """ 

744 

745 code = 500 

746 description = ( 

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

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

749 " there is an error in the application." 

750 ) 

751 

752 def __init__( 

753 self, 

754 description: str | None = None, 

755 response: SansIOResponse | None = None, 

756 original_exception: BaseException | None = None, 

757 ) -> None: 

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

759 #: used by frameworks to provide context when handling 

760 #: unexpected errors. 

761 self.original_exception = original_exception 

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

763 

764 

765class NotImplemented(HTTPException): 

766 """*501* `Not Implemented` 

767 

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

769 browser. 

770 """ 

771 

772 code = 501 

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

774 

775 

776class BadGateway(HTTPException): 

777 """*502* `Bad Gateway` 

778 

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

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

781 in attempting to fulfill the request. 

782 """ 

783 

784 code = 502 

785 description = ( 

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

787 ) 

788 

789 

790class ServiceUnavailable(_RetryAfter): 

791 """*503* `Service Unavailable` 

792 

793 Status code you should return if a service is temporarily 

794 unavailable. 

795 

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

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

798 :class:`~datetime.datetime`. 

799 

800 .. versionchanged:: 1.0 

801 Added ``retry_after`` parameter. 

802 """ 

803 

804 code = 503 

805 description = ( 

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

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

808 " again later." 

809 ) 

810 

811 

812class GatewayTimeout(HTTPException): 

813 """*504* `Gateway Timeout` 

814 

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

816 times out. 

817 """ 

818 

819 code = 504 

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

821 

822 

823class HTTPVersionNotSupported(HTTPException): 

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

825 

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

827 """ 

828 

829 code = 505 

830 description = ( 

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

832 ) 

833 

834 

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

836 

837 

838def _find_exceptions() -> None: 

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

840 try: 

841 is_http_exception = issubclass(obj, HTTPException) 

842 except TypeError: 

843 is_http_exception = False 

844 if not is_http_exception or obj.code is None: 

845 continue 

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

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

848 continue 

849 default_exceptions[obj.code] = obj 

850 

851 

852_find_exceptions() 

853del _find_exceptions 

854 

855 

856class Aborter: 

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

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

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

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

861 

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

863 """ 

864 

865 def __init__( 

866 self, 

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

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

869 ) -> None: 

870 if mapping is None: 

871 mapping = default_exceptions 

872 self.mapping = dict(mapping) 

873 if extra is not None: 

874 self.mapping.update(extra) 

875 

876 def __call__( 

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

878 ) -> t.NoReturn: 

879 from .sansio.response import Response 

880 

881 if isinstance(code, Response): 

882 raise HTTPException(response=code) 

883 

884 if code not in self.mapping: 

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

886 

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

888 

889 

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

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

892 application. 

893 

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

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

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

897 

898 abort(404) # 404 Not Found 

899 abort(Response('Hello World')) 

900 

901 """ 

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

903 

904 

905_aborter: Aborter = Aborter()