Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/werkzeug/exceptions.py: 66%
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
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
1"""Implements a number of Python exceptions which can be raised from within
2a view to trigger a standard HTTP non-200 response.
4Usage Example
5-------------
7.. code-block:: python
9 from werkzeug.wrappers.request import Request
10 from werkzeug.exceptions import HTTPException, NotFound
12 def view(request):
13 raise NotFound()
15 @Request.application
16 def application(request):
17 try:
18 return view(request)
19 except HTTPException as e:
20 return e
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.
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.
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:
34.. code-block:: python
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
45"""
47from __future__ import annotations
49import typing as t
50from datetime import datetime
52from markupsafe import escape
53from markupsafe import Markup
55from ._internal import _get_environ
57if t.TYPE_CHECKING:
58 from _typeshed.wsgi import StartResponse
59 from _typeshed.wsgi import WSGIEnvironment
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
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.
72 .. versionchanged:: 2.1
73 Removed the ``wrap`` class method.
74 """
76 code: int | None = None
77 description: str | None = None
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
89 @property
90 def name(self) -> str:
91 """The status name."""
92 from .http import HTTP_STATUS_CODES
94 return HTTP_STATUS_CODES.get(self.code, "Unknown Error") # type: ignore
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
107 description = escape(description).replace("\n", Markup("<br>"))
108 return f"<p>{description}</p>"
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 )
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")]
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.
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
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)
154 def __call__(
155 self, environ: WSGIEnvironment, start_response: StartResponse
156 ) -> t.Iterable[bytes]:
157 """Call the exception as WSGI application.
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)
166 def __str__(self) -> str:
167 code = self.code if self.code is not None else "???"
168 return f"{code} {self.name}: {self.description}"
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}'>"
175class BadRequest(HTTPException):
176 """*400* `Bad Request`
178 Raise if the browser sends something to the application the application
179 or server cannot handle.
180 """
182 code = 400
183 description = (
184 "The browser (or proxy) sent a request that this server could "
185 "not understand."
186 )
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 """
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
200 def __init__(self, arg: object | None = None, *args: t.Any, **kwargs: t.Any):
201 super().__init__(*args, **kwargs)
203 if arg is None:
204 KeyError.__init__(self)
205 else:
206 KeyError.__init__(self, arg)
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 )
216 return self._description
218 @description.setter
219 def description(self, value: str) -> None:
220 self._description = value
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.
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.
234 .. versionadded:: 0.8
235 """
238class SecurityError(BadRequest):
239 """Raised if something triggers a security error. This is otherwise
240 exactly like a bad request error.
242 .. versionadded:: 0.9
243 """
246class BadHost(BadRequest):
247 """Raised if the submitted host is badly formatted.
249 .. versionadded:: 0.11.2
250 """
253class Unauthorized(HTTPException):
254 """*401* ``Unauthorized``
256 Raise if the user is not authorized to access a resource.
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.
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).
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.
275 .. versionchanged:: 0.15.3
276 If the ``www_authenticate`` argument is not set, the
277 ``WWW-Authenticate`` header is not set.
279 .. versionchanged:: 0.15.3
280 The ``response`` argument was restored.
282 .. versionchanged:: 0.15.1
283 ``description`` was moved back as the first argument, restoring
284 its previous position.
286 .. versionchanged:: 0.15.0
287 ``www_authenticate`` was added as the first argument, ahead of
288 ``description``.
289 """
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 )
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)
307 from .datastructures import WWWAuthenticate
309 if isinstance(www_authenticate, WWWAuthenticate):
310 www_authenticate = (www_authenticate,)
312 self.www_authenticate = www_authenticate
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
325class Forbidden(HTTPException):
326 """*403* `Forbidden`
328 Raise if the user doesn't have the permission for the requested resource
329 but was authenticated.
330 """
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 )
340class NotFound(HTTPException):
341 """*404* `Not Found`
343 Raise if a resource does not exist and never existed.
344 """
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 )
353class MethodNotAllowed(HTTPException):
354 """*405* `Method Not Allowed`
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.
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 """
364 code = 405
365 description = "The method is not allowed for the requested URL."
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
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
389class NotAcceptable(HTTPException):
390 """*406* `Not Acceptable`
392 Raise if the server can't return any content conforming to the
393 `Accept` headers of the client.
394 """
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 )
405class RequestTimeout(HTTPException):
406 """*408* `Request Timeout`
408 Raise to signalize a timeout.
409 """
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 )
418class Conflict(HTTPException):
419 """*409* `Conflict`
421 Raise to signal that a request cannot be completed because it conflicts
422 with the current state on the server.
424 .. versionadded:: 0.7
425 """
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 )
435class Gone(HTTPException):
436 """*410* `Gone`
438 Raise if a resource existed previously and went away without new location.
439 """
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 )
449class LengthRequired(HTTPException):
450 """*411* `Length Required`
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 """
456 code = 411
457 description = (
458 "A request with this method requires a valid <code>Content-"
459 "Length</code> header."
460 )
463class PreconditionFailed(HTTPException):
464 """*412* `Precondition Failed`
466 Status code used in combination with ``If-Match``, ``If-None-Match``, or
467 ``If-Unmodified-Since``.
468 """
470 code = 412
471 description = (
472 "The precondition on the request for the URL failed positive evaluation."
473 )
476class RequestEntityTooLarge(HTTPException):
477 """*413* `Request Entity Too Large`
479 The status code one should return if the data submitted exceeded a given
480 limit.
481 """
483 code = 413
484 description = "The data value transmitted exceeds the capacity limit."
487class RequestURITooLarge(HTTPException):
488 """*414* `Request URI Too Large`
490 Like *413* but for too long URLs.
491 """
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 )
500class UnsupportedMediaType(HTTPException):
501 """*415* `Unsupported Media Type`
503 The status code returned if the server is unable to handle the media type
504 the client transmitted.
505 """
507 code = 415
508 description = (
509 "The server does not support the media type transmitted in the request."
510 )
513class RequestedRangeNotSatisfiable(HTTPException):
514 """*416* `Requested Range Not Satisfiable`
516 The client asked for an invalid part of the file.
518 .. versionadded:: 0.7
519 """
521 code = 416
522 description = "The server cannot provide the requested range."
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
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
549class ExpectationFailed(HTTPException):
550 """*417* `Expectation Failed`
552 The server cannot meet the requirements of the Expect request-header.
554 .. versionadded:: 0.7
555 """
557 code = 417
558 description = "The server could not meet the requirements of the Expect header"
561class ImATeapot(HTTPException):
562 """*418* `I'm a teapot`
564 The server should return this if it is a teapot and someone attempted
565 to brew coffee with it.
567 .. versionadded:: 0.7
568 """
570 code = 418
571 description = "This server is a teapot, not a coffee machine"
574class MisdirectedRequest(HTTPException):
575 """421 Misdirected Request
577 Indicates that the request was directed to a server that is not able to
578 produce a response.
580 .. versionadded:: 3.1
581 """
583 code = 421
584 description = "The server is not able to produce a response."
587class UnprocessableEntity(HTTPException):
588 """*422* `Unprocessable Entity`
590 Used if the request is well formed, but the instructions are otherwise
591 incorrect.
592 """
594 code = 422
595 description = (
596 "The request was well-formed but was unable to be followed due"
597 " to semantic errors."
598 )
601class Locked(HTTPException):
602 """*423* `Locked`
604 Used if the resource that is being accessed is locked.
605 """
607 code = 423
608 description = "The resource that is being accessed is locked."
611class FailedDependency(HTTPException):
612 """*424* `Failed Dependency`
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 """
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 )
626class PreconditionRequired(HTTPException):
627 """*428* `Precondition Required`
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 """
638 code = 428
639 description = (
640 "This request is required to be conditional; try using"
641 ' "If-Match" or "If-Unmodified-Since".'
642 )
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 """
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
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)
667 if self.retry_after:
668 if isinstance(self.retry_after, datetime):
669 from .http import http_date
671 value = http_date(self.retry_after)
672 else:
673 value = str(self.retry_after)
675 headers.append(("Retry-After", value))
677 return headers
680class TooManyRequests(_RetryAfter):
681 """*429* `Too Many Requests`
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.
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`.
693 .. versionchanged:: 1.0
694 Added ``retry_after`` parameter.
695 """
697 code = 429
698 description = "This user has exceeded an allotted request count. Try again later."
701class RequestHeaderFieldsTooLarge(HTTPException):
702 """*431* `Request Header Fields Too Large`
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 """
709 code = 431
710 description = "One or more header fields exceeds the maximum size."
713class UnavailableForLegalReasons(HTTPException):
714 """*451* `Unavailable For Legal Reasons`
716 This status code indicates that the server is denying access to the
717 resource as a consequence of a legal demand.
718 """
720 code = 451
721 description = "Unavailable for legal reasons."
724class InternalServerError(HTTPException):
725 """*500* `Internal Server Error`
727 Raise if an internal server error occurred. This is a good fallback if an
728 unknown error occurred in the dispatcher.
730 .. versionchanged:: 1.0.0
731 Added the :attr:`original_exception` attribute.
732 """
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 )
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)
754class NotImplemented(HTTPException):
755 """*501* `Not Implemented`
757 Raise if the application does not support the action requested by the
758 browser.
759 """
761 code = 501
762 description = "The server does not support the action requested by the browser."
765class BadGateway(HTTPException):
766 """*502* `Bad Gateway`
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 """
773 code = 502
774 description = (
775 "The proxy server received an invalid response from an upstream server."
776 )
779class ServiceUnavailable(_RetryAfter):
780 """*503* `Service Unavailable`
782 Status code you should return if a service is temporarily
783 unavailable.
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`.
789 .. versionchanged:: 1.0
790 Added ``retry_after`` parameter.
791 """
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 )
801class GatewayTimeout(HTTPException):
802 """*504* `Gateway Timeout`
804 Status code you should return if a connection to an upstream server
805 times out.
806 """
808 code = 504
809 description = "The connection to an upstream server timed out."
812class HTTPVersionNotSupported(HTTPException):
813 """*505* `HTTP Version Not Supported`
815 The server does not support the HTTP protocol version used in the request.
816 """
818 code = 505
819 description = (
820 "The server does not support the HTTP protocol version used in the request."
821 )
824default_exceptions: dict[int, type[HTTPException]] = {}
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
841_find_exceptions()
842del _find_exceptions
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.
851 The rest of the arguments are forwarded to the exception constructor.
852 """
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)
865 def __call__(
866 self, code: int | Response, *args: t.Any, **kwargs: t.Any
867 ) -> t.NoReturn:
868 from .sansio.response import Response
870 if isinstance(code, Response):
871 raise HTTPException(response=code)
873 if code not in self.mapping:
874 raise LookupError(f"no exception for {code!r}")
876 raise self.mapping[code](*args, **kwargs)
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.
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::
887 abort(404) # 404 Not Found
888 abort(Response('Hello World'))
890 """
891 _aborter(status, *args, **kwargs)
894_aborter: Aborter = Aborter()