]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/werkzeug/exceptions.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / werkzeug / exceptions.py
1 """Implements a number of Python exceptions which can be raised from within
2 a view to trigger a standard HTTP non-200 response.
3
4 Usage 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
22 As you can see from this example those exceptions are callable WSGI
23 applications. However, they are not Werkzeug response objects. You
24 can get a response object by calling ``get_response()`` on a HTTP
25 exception.
26
27 Keep in mind that you may have to pass an environ (WSGI) or scope
28 (ASGI) to ``get_response()`` because some errors fetch additional
29 information relating to the request.
30
31 If you want to hook in a different exception page to say, a 404 status
32 code, 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 from __future__ import annotations
47
48 import typing as t
49 from datetime import datetime
50
51 from markupsafe import escape
52 from markupsafe import Markup
53
54 from ._internal import _get_environ
55
56 if t.TYPE_CHECKING:
57 from _typeshed.wsgi import StartResponse
58 from _typeshed.wsgi import WSGIEnvironment
59 from .datastructures import WWWAuthenticate
60 from .sansio.response import Response
61 from .wrappers.request import Request as WSGIRequest
62 from .wrappers.response import Response as WSGIResponse
63
64
65 class HTTPException(Exception):
66 """The base class for all HTTP exceptions. This exception can be called as a WSGI
67 application to render a default error page or you can catch the subclasses
68 of it independently and render nicer error messages.
69
70 .. versionchanged:: 2.1
71 Removed the ``wrap`` class method.
72 """
73
74 code: int | None = None
75 description: str | None = None
76
77 def __init__(
78 self,
79 description: str | None = None,
80 response: Response | None = None,
81 ) -> None:
82 super().__init__()
83 if description is not None:
84 self.description = description
85 self.response = response
86
87 @property
88 def name(self) -> str:
89 """The status name."""
90 from .http import HTTP_STATUS_CODES
91
92 return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore
93
94 def get_description(
95 self,
96 environ: WSGIEnvironment | None = None,
97 scope: dict | None = None,
98 ) -> str:
99 """Get the description."""
100 if self.description is None:
101 description = ""
102 else:
103 description = self.description
104
105 description = escape(description).replace("\n", Markup("<br>"))
106 return f"<p>{description}</p>"
107
108 def get_body(
109 self,
110 environ: WSGIEnvironment | None = None,
111 scope: dict | None = None,
112 ) -> str:
113 """Get the HTML body."""
114 return (
115 "<!doctype html>\n"
116 "<html lang=en>\n"
117 f"<title>{self.code} {escape(self.name)}</title>\n"
118 f"<h1>{escape(self.name)}</h1>\n"
119 f"{self.get_description(environ)}\n"
120 )
121
122 def get_headers(
123 self,
124 environ: WSGIEnvironment | None = None,
125 scope: dict | None = None,
126 ) -> list[tuple[str, str]]:
127 """Get a list of headers."""
128 return [("Content-Type", "text/html; charset=utf-8")]
129
130 def get_response(
131 self,
132 environ: WSGIEnvironment | WSGIRequest | None = None,
133 scope: dict | None = None,
134 ) -> Response:
135 """Get a response object. If one was passed to the exception
136 it's returned directly.
137
138 :param environ: the optional environ for the request. This
139 can be used to modify the response depending
140 on how the request looked like.
141 :return: a :class:`Response` object or a subclass thereof.
142 """
143 from .wrappers.response import Response as WSGIResponse # noqa: F811
144
145 if self.response is not None:
146 return self.response
147 if environ is not None:
148 environ = _get_environ(environ)
149 headers = self.get_headers(environ, scope)
150 return WSGIResponse(self.get_body(environ, scope), self.code, headers)
151
152 def __call__(
153 self, environ: WSGIEnvironment, start_response: StartResponse
154 ) -> t.Iterable[bytes]:
155 """Call the exception as WSGI application.
156
157 :param environ: the WSGI environment.
158 :param start_response: the response callable provided by the WSGI
159 server.
160 """
161 response = t.cast("WSGIResponse", self.get_response(environ))
162 return response(environ, start_response)
163
164 def __str__(self) -> str:
165 code = self.code if self.code is not None else "???"
166 return f"{code} {self.name}: {self.description}"
167
168 def __repr__(self) -> str:
169 code = self.code if self.code is not None else "???"
170 return f"<{type(self).__name__} '{code}: {self.name}'>"
171
172
173 class BadRequest(HTTPException):
174 """*400* `Bad Request`
175
176 Raise if the browser sends something to the application the application
177 or server cannot handle.
178 """
179
180 code = 400
181 description = (
182 "The browser (or proxy) sent a request that this server could "
183 "not understand."
184 )
185
186
187 class BadRequestKeyError(BadRequest, KeyError):
188 """An exception that is used to signal both a :exc:`KeyError` and a
189 :exc:`BadRequest`. Used by many of the datastructures.
190 """
191
192 _description = BadRequest.description
193 #: Show the KeyError along with the HTTP error message in the
194 #: response. This should be disabled in production, but can be
195 #: useful in a debug mode.
196 show_exception = False
197
198 def __init__(self, arg: str | None = None, *args: t.Any, **kwargs: t.Any):
199 super().__init__(*args, **kwargs)
200
201 if arg is None:
202 KeyError.__init__(self)
203 else:
204 KeyError.__init__(self, arg)
205
206 @property # type: ignore
207 def description(self) -> str:
208 if self.show_exception:
209 return (
210 f"{self._description}\n"
211 f"{KeyError.__name__}: {KeyError.__str__(self)}"
212 )
213
214 return self._description
215
216 @description.setter
217 def description(self, value: str) -> None:
218 self._description = value
219
220
221 class ClientDisconnected(BadRequest):
222 """Internal exception that is raised if Werkzeug detects a disconnected
223 client. Since the client is already gone at that point attempting to
224 send the error message to the client might not work and might ultimately
225 result in another exception in the server. Mainly this is here so that
226 it is silenced by default as far as Werkzeug is concerned.
227
228 Since disconnections cannot be reliably detected and are unspecified
229 by WSGI to a large extent this might or might not be raised if a client
230 is gone.
231
232 .. versionadded:: 0.8
233 """
234
235
236 class SecurityError(BadRequest):
237 """Raised if something triggers a security error. This is otherwise
238 exactly like a bad request error.
239
240 .. versionadded:: 0.9
241 """
242
243
244 class BadHost(BadRequest):
245 """Raised if the submitted host is badly formatted.
246
247 .. versionadded:: 0.11.2
248 """
249
250
251 class Unauthorized(HTTPException):
252 """*401* ``Unauthorized``
253
254 Raise if the user is not authorized to access a resource.
255
256 The ``www_authenticate`` argument should be used to set the
257 ``WWW-Authenticate`` header. This is used for HTTP basic auth and
258 other schemes. Use :class:`~werkzeug.datastructures.WWWAuthenticate`
259 to create correctly formatted values. Strictly speaking a 401
260 response is invalid if it doesn't provide at least one value for
261 this header, although real clients typically don't care.
262
263 :param description: Override the default message used for the body
264 of the response.
265 :param www-authenticate: A single value, or list of values, for the
266 WWW-Authenticate header(s).
267
268 .. versionchanged:: 2.0
269 Serialize multiple ``www_authenticate`` items into multiple
270 ``WWW-Authenticate`` headers, rather than joining them
271 into a single value, for better interoperability.
272
273 .. versionchanged:: 0.15.3
274 If the ``www_authenticate`` argument is not set, the
275 ``WWW-Authenticate`` header is not set.
276
277 .. versionchanged:: 0.15.3
278 The ``response`` argument was restored.
279
280 .. versionchanged:: 0.15.1
281 ``description`` was moved back as the first argument, restoring
282 its previous position.
283
284 .. versionchanged:: 0.15.0
285 ``www_authenticate`` was added as the first argument, ahead of
286 ``description``.
287 """
288
289 code = 401
290 description = (
291 "The server could not verify that you are authorized to access"
292 " the URL requested. You either supplied the wrong credentials"
293 " (e.g. a bad password), or your browser doesn't understand"
294 " how to supply the credentials required."
295 )
296
297 def __init__(
298 self,
299 description: str | None = None,
300 response: Response | None = None,
301 www_authenticate: None | (WWWAuthenticate | t.Iterable[WWWAuthenticate]) = None,
302 ) -> None:
303 super().__init__(description, response)
304
305 from .datastructures import WWWAuthenticate
306
307 if isinstance(www_authenticate, WWWAuthenticate):
308 www_authenticate = (www_authenticate,)
309
310 self.www_authenticate = www_authenticate
311
312 def get_headers(
313 self,
314 environ: WSGIEnvironment | None = None,
315 scope: dict | None = None,
316 ) -> list[tuple[str, str]]:
317 headers = super().get_headers(environ, scope)
318 if self.www_authenticate:
319 headers.extend(("WWW-Authenticate", str(x)) for x in self.www_authenticate)
320 return headers
321
322
323 class Forbidden(HTTPException):
324 """*403* `Forbidden`
325
326 Raise if the user doesn't have the permission for the requested resource
327 but was authenticated.
328 """
329
330 code = 403
331 description = (
332 "You don't have the permission to access the requested"
333 " resource. It is either read-protected or not readable by the"
334 " server."
335 )
336
337
338 class NotFound(HTTPException):
339 """*404* `Not Found`
340
341 Raise if a resource does not exist and never existed.
342 """
343
344 code = 404
345 description = (
346 "The requested URL was not found on the server. If you entered"
347 " the URL manually please check your spelling and try again."
348 )
349
350
351 class MethodNotAllowed(HTTPException):
352 """*405* `Method Not Allowed`
353
354 Raise if the server used a method the resource does not handle. For
355 example `POST` if the resource is view only. Especially useful for REST.
356
357 The first argument for this exception should be a list of allowed methods.
358 Strictly speaking the response would be invalid if you don't provide valid
359 methods in the header which you can do with that list.
360 """
361
362 code = 405
363 description = "The method is not allowed for the requested URL."
364
365 def __init__(
366 self,
367 valid_methods: t.Iterable[str] | None = None,
368 description: str | None = None,
369 response: Response | None = None,
370 ) -> None:
371 """Takes an optional list of valid http methods
372 starting with werkzeug 0.3 the list will be mandatory."""
373 super().__init__(description=description, response=response)
374 self.valid_methods = valid_methods
375
376 def get_headers(
377 self,
378 environ: WSGIEnvironment | None = None,
379 scope: dict | None = None,
380 ) -> list[tuple[str, str]]:
381 headers = super().get_headers(environ, scope)
382 if self.valid_methods:
383 headers.append(("Allow", ", ".join(self.valid_methods)))
384 return headers
385
386
387 class NotAcceptable(HTTPException):
388 """*406* `Not Acceptable`
389
390 Raise if the server can't return any content conforming to the
391 `Accept` headers of the client.
392 """
393
394 code = 406
395 description = (
396 "The resource identified by the request is only capable of"
397 " generating response entities which have content"
398 " characteristics not acceptable according to the accept"
399 " headers sent in the request."
400 )
401
402
403 class RequestTimeout(HTTPException):
404 """*408* `Request Timeout`
405
406 Raise to signalize a timeout.
407 """
408
409 code = 408
410 description = (
411 "The server closed the network connection because the browser"
412 " didn't finish the request within the specified time."
413 )
414
415
416 class Conflict(HTTPException):
417 """*409* `Conflict`
418
419 Raise to signal that a request cannot be completed because it conflicts
420 with the current state on the server.
421
422 .. versionadded:: 0.7
423 """
424
425 code = 409
426 description = (
427 "A conflict happened while processing the request. The"
428 " resource might have been modified while the request was being"
429 " processed."
430 )
431
432
433 class Gone(HTTPException):
434 """*410* `Gone`
435
436 Raise if a resource existed previously and went away without new location.
437 """
438
439 code = 410
440 description = (
441 "The requested URL is no longer available on this server and"
442 " there is no forwarding address. If you followed a link from a"
443 " foreign page, please contact the author of this page."
444 )
445
446
447 class LengthRequired(HTTPException):
448 """*411* `Length Required`
449
450 Raise if the browser submitted data but no ``Content-Length`` header which
451 is required for the kind of processing the server does.
452 """
453
454 code = 411
455 description = (
456 "A request with this method requires a valid <code>Content-"
457 "Length</code> header."
458 )
459
460
461 class PreconditionFailed(HTTPException):
462 """*412* `Precondition Failed`
463
464 Status code used in combination with ``If-Match``, ``If-None-Match``, or
465 ``If-Unmodified-Since``.
466 """
467
468 code = 412
469 description = (
470 "The precondition on the request for the URL failed positive evaluation."
471 )
472
473
474 class RequestEntityTooLarge(HTTPException):
475 """*413* `Request Entity Too Large`
476
477 The status code one should return if the data submitted exceeded a given
478 limit.
479 """
480
481 code = 413
482 description = "The data value transmitted exceeds the capacity limit."
483
484
485 class RequestURITooLarge(HTTPException):
486 """*414* `Request URI Too Large`
487
488 Like *413* but for too long URLs.
489 """
490
491 code = 414
492 description = (
493 "The length of the requested URL exceeds the capacity limit for"
494 " this server. The request cannot be processed."
495 )
496
497
498 class UnsupportedMediaType(HTTPException):
499 """*415* `Unsupported Media Type`
500
501 The status code returned if the server is unable to handle the media type
502 the client transmitted.
503 """
504
505 code = 415
506 description = (
507 "The server does not support the media type transmitted in the request."
508 )
509
510
511 class RequestedRangeNotSatisfiable(HTTPException):
512 """*416* `Requested Range Not Satisfiable`
513
514 The client asked for an invalid part of the file.
515
516 .. versionadded:: 0.7
517 """
518
519 code = 416
520 description = "The server cannot provide the requested range."
521
522 def __init__(
523 self,
524 length: int | None = None,
525 units: str = "bytes",
526 description: str | None = None,
527 response: Response | None = None,
528 ) -> None:
529 """Takes an optional `Content-Range` header value based on ``length``
530 parameter.
531 """
532 super().__init__(description=description, response=response)
533 self.length = length
534 self.units = units
535
536 def get_headers(
537 self,
538 environ: WSGIEnvironment | None = None,
539 scope: dict | None = None,
540 ) -> list[tuple[str, str]]:
541 headers = super().get_headers(environ, scope)
542 if self.length is not None:
543 headers.append(("Content-Range", f"{self.units} */{self.length}"))
544 return headers
545
546
547 class ExpectationFailed(HTTPException):
548 """*417* `Expectation Failed`
549
550 The server cannot meet the requirements of the Expect request-header.
551
552 .. versionadded:: 0.7
553 """
554
555 code = 417
556 description = "The server could not meet the requirements of the Expect header"
557
558
559 class ImATeapot(HTTPException):
560 """*418* `I'm a teapot`
561
562 The server should return this if it is a teapot and someone attempted
563 to brew coffee with it.
564
565 .. versionadded:: 0.7
566 """
567
568 code = 418
569 description = "This server is a teapot, not a coffee machine"
570
571
572 class UnprocessableEntity(HTTPException):
573 """*422* `Unprocessable Entity`
574
575 Used if the request is well formed, but the instructions are otherwise
576 incorrect.
577 """
578
579 code = 422
580 description = (
581 "The request was well-formed but was unable to be followed due"
582 " to semantic errors."
583 )
584
585
586 class Locked(HTTPException):
587 """*423* `Locked`
588
589 Used if the resource that is being accessed is locked.
590 """
591
592 code = 423
593 description = "The resource that is being accessed is locked."
594
595
596 class FailedDependency(HTTPException):
597 """*424* `Failed Dependency`
598
599 Used if the method could not be performed on the resource
600 because the requested action depended on another action and that action failed.
601 """
602
603 code = 424
604 description = (
605 "The method could not be performed on the resource because the"
606 " requested action depended on another action and that action"
607 " failed."
608 )
609
610
611 class PreconditionRequired(HTTPException):
612 """*428* `Precondition Required`
613
614 The server requires this request to be conditional, typically to prevent
615 the lost update problem, which is a race condition between two or more
616 clients attempting to update a resource through PUT or DELETE. By requiring
617 each client to include a conditional header ("If-Match" or "If-Unmodified-
618 Since") with the proper value retained from a recent GET request, the
619 server ensures that each client has at least seen the previous revision of
620 the resource.
621 """
622
623 code = 428
624 description = (
625 "This request is required to be conditional; try using"
626 ' "If-Match" or "If-Unmodified-Since".'
627 )
628
629
630 class _RetryAfter(HTTPException):
631 """Adds an optional ``retry_after`` parameter which will set the
632 ``Retry-After`` header. May be an :class:`int` number of seconds or
633 a :class:`~datetime.datetime`.
634 """
635
636 def __init__(
637 self,
638 description: str | None = None,
639 response: Response | None = None,
640 retry_after: datetime | int | None = None,
641 ) -> None:
642 super().__init__(description, response)
643 self.retry_after = retry_after
644
645 def get_headers(
646 self,
647 environ: WSGIEnvironment | None = None,
648 scope: dict | None = None,
649 ) -> list[tuple[str, str]]:
650 headers = super().get_headers(environ, scope)
651
652 if self.retry_after:
653 if isinstance(self.retry_after, datetime):
654 from .http import http_date
655
656 value = http_date(self.retry_after)
657 else:
658 value = str(self.retry_after)
659
660 headers.append(("Retry-After", value))
661
662 return headers
663
664
665 class TooManyRequests(_RetryAfter):
666 """*429* `Too Many Requests`
667
668 The server is limiting the rate at which this user receives
669 responses, and this request exceeds that rate. (The server may use
670 any convenient method to identify users and their request rates).
671 The server may include a "Retry-After" header to indicate how long
672 the user should wait before retrying.
673
674 :param retry_after: If given, set the ``Retry-After`` header to this
675 value. May be an :class:`int` number of seconds or a
676 :class:`~datetime.datetime`.
677
678 .. versionchanged:: 1.0
679 Added ``retry_after`` parameter.
680 """
681
682 code = 429
683 description = "This user has exceeded an allotted request count. Try again later."
684
685
686 class RequestHeaderFieldsTooLarge(HTTPException):
687 """*431* `Request Header Fields Too Large`
688
689 The server refuses to process the request because the header fields are too
690 large. One or more individual fields may be too large, or the set of all
691 headers is too large.
692 """
693
694 code = 431
695 description = "One or more header fields exceeds the maximum size."
696
697
698 class UnavailableForLegalReasons(HTTPException):
699 """*451* `Unavailable For Legal Reasons`
700
701 This status code indicates that the server is denying access to the
702 resource as a consequence of a legal demand.
703 """
704
705 code = 451
706 description = "Unavailable for legal reasons."
707
708
709 class InternalServerError(HTTPException):
710 """*500* `Internal Server Error`
711
712 Raise if an internal server error occurred. This is a good fallback if an
713 unknown error occurred in the dispatcher.
714
715 .. versionchanged:: 1.0.0
716 Added the :attr:`original_exception` attribute.
717 """
718
719 code = 500
720 description = (
721 "The server encountered an internal error and was unable to"
722 " complete your request. Either the server is overloaded or"
723 " there is an error in the application."
724 )
725
726 def __init__(
727 self,
728 description: str | None = None,
729 response: Response | None = None,
730 original_exception: BaseException | None = None,
731 ) -> None:
732 #: The original exception that caused this 500 error. Can be
733 #: used by frameworks to provide context when handling
734 #: unexpected errors.
735 self.original_exception = original_exception
736 super().__init__(description=description, response=response)
737
738
739 class NotImplemented(HTTPException):
740 """*501* `Not Implemented`
741
742 Raise if the application does not support the action requested by the
743 browser.
744 """
745
746 code = 501
747 description = "The server does not support the action requested by the browser."
748
749
750 class BadGateway(HTTPException):
751 """*502* `Bad Gateway`
752
753 If you do proxying in your application you should return this status code
754 if you received an invalid response from the upstream server it accessed
755 in attempting to fulfill the request.
756 """
757
758 code = 502
759 description = (
760 "The proxy server received an invalid response from an upstream server."
761 )
762
763
764 class ServiceUnavailable(_RetryAfter):
765 """*503* `Service Unavailable`
766
767 Status code you should return if a service is temporarily
768 unavailable.
769
770 :param retry_after: If given, set the ``Retry-After`` header to this
771 value. May be an :class:`int` number of seconds or a
772 :class:`~datetime.datetime`.
773
774 .. versionchanged:: 1.0
775 Added ``retry_after`` parameter.
776 """
777
778 code = 503
779 description = (
780 "The server is temporarily unable to service your request due"
781 " to maintenance downtime or capacity problems. Please try"
782 " again later."
783 )
784
785
786 class GatewayTimeout(HTTPException):
787 """*504* `Gateway Timeout`
788
789 Status code you should return if a connection to an upstream server
790 times out.
791 """
792
793 code = 504
794 description = "The connection to an upstream server timed out."
795
796
797 class HTTPVersionNotSupported(HTTPException):
798 """*505* `HTTP Version Not Supported`
799
800 The server does not support the HTTP protocol version used in the request.
801 """
802
803 code = 505
804 description = (
805 "The server does not support the HTTP protocol version used in the request."
806 )
807
808
809 default_exceptions: dict[int, type[HTTPException]] = {}
810
811
812 def _find_exceptions() -> None:
813 for obj in globals().values():
814 try:
815 is_http_exception = issubclass(obj, HTTPException)
816 except TypeError:
817 is_http_exception = False
818 if not is_http_exception or obj.code is None:
819 continue
820 old_obj = default_exceptions.get(obj.code, None)
821 if old_obj is not None and issubclass(obj, old_obj):
822 continue
823 default_exceptions[obj.code] = obj
824
825
826 _find_exceptions()
827 del _find_exceptions
828
829
830 class Aborter:
831 """When passed a dict of code -> exception items it can be used as
832 callable that raises exceptions. If the first argument to the
833 callable is an integer it will be looked up in the mapping, if it's
834 a WSGI application it will be raised in a proxy exception.
835
836 The rest of the arguments are forwarded to the exception constructor.
837 """
838
839 def __init__(
840 self,
841 mapping: dict[int, type[HTTPException]] | None = None,
842 extra: dict[int, type[HTTPException]] | None = None,
843 ) -> None:
844 if mapping is None:
845 mapping = default_exceptions
846 self.mapping = dict(mapping)
847 if extra is not None:
848 self.mapping.update(extra)
849
850 def __call__(
851 self, code: int | Response, *args: t.Any, **kwargs: t.Any
852 ) -> t.NoReturn:
853 from .sansio.response import Response
854
855 if isinstance(code, Response):
856 raise HTTPException(response=code)
857
858 if code not in self.mapping:
859 raise LookupError(f"no exception for {code!r}")
860
861 raise self.mapping[code](*args, **kwargs)
862
863
864 def abort(status: int | Response, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
865 """Raises an :py:exc:`HTTPException` for the given status code or WSGI
866 application.
867
868 If a status code is given, it will be looked up in the list of
869 exceptions and will raise that exception. If passed a WSGI application,
870 it will wrap it in a proxy WSGI exception and raise that::
871
872 abort(404) # 404 Not Found
873 abort(Response('Hello World'))
874
875 """
876 _aborter(status, *args, **kwargs)
877
878
879 _aborter: Aborter = Aborter()