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
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
51from http import HTTPStatus
53from markupsafe import escape
54from markupsafe import Markup
56from ._internal import _get_environ
58if t.TYPE_CHECKING:
59 from _typeshed.wsgi import StartResponse
60 from _typeshed.wsgi import WSGIEnvironment
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
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.
73 .. versionchanged:: 2.1
74 Removed the ``wrap`` class method.
75 """
77 code: int | None = None
78 description: str | None = None
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
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"
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
109 description = escape(description).replace("\n", Markup("<br>"))
110 return f"<p>{description}</p>"
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 )
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")]
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.
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
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)
171 def __call__(
172 self, environ: WSGIEnvironment, start_response: StartResponse
173 ) -> t.Iterable[bytes]:
174 """Call the exception as WSGI application.
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)
183 def __str__(self) -> str:
184 code = self.code if self.code is not None else "???"
185 return f"{code} {self.name}: {self.description}"
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}'>"
192class BadRequest(HTTPException):
193 """*400* `Bad Request`
195 Raise if the browser sends something to the application the application
196 or server cannot handle.
197 """
199 code = 400
200 description = (
201 "The browser (or proxy) sent a request that this server could not understand."
202 )
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 """
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
216 def __init__(self, arg: object | None = None, *args: t.Any, **kwargs: t.Any):
217 super().__init__(*args, **kwargs)
219 if arg is None:
220 KeyError.__init__(self)
221 else:
222 KeyError.__init__(self, arg)
224 @property
225 def description(self) -> str:
226 if self.show_exception:
227 return f"{self._description}\n{KeyError.__name__}: {KeyError.__str__(self)}"
229 return self._description
231 @description.setter
232 def description(self, value: str) -> None:
233 self._description = value
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.
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.
247 .. versionadded:: 0.8
248 """
251class SecurityError(BadRequest):
252 """Raised if something triggers a security error. This is otherwise
253 exactly like a bad request error.
255 .. versionadded:: 0.9
256 """
259class BadHost(BadRequest):
260 """Raised if the submitted host is badly formatted.
262 .. versionadded:: 0.11.2
263 """
266class Unauthorized(HTTPException):
267 """*401* ``Unauthorized``
269 Raise if the user is not authorized to access a resource.
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.
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).
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.
288 .. versionchanged:: 0.15.3
289 If the ``www_authenticate`` argument is not set, the
290 ``WWW-Authenticate`` header is not set.
292 .. versionchanged:: 0.15.3
293 The ``response`` argument was restored.
295 .. versionchanged:: 0.15.1
296 ``description`` was moved back as the first argument, restoring
297 its previous position.
299 .. versionchanged:: 0.15.0
300 ``www_authenticate`` was added as the first argument, ahead of
301 ``description``.
302 """
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 )
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)
320 from .datastructures import WWWAuthenticate
322 if isinstance(www_authenticate, WWWAuthenticate):
323 www_authenticate = (www_authenticate,)
325 self.www_authenticate = www_authenticate
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
338class Forbidden(HTTPException):
339 """*403* `Forbidden`
341 Raise if the user doesn't have the permission for the requested resource
342 but was authenticated.
343 """
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 )
353class NotFound(HTTPException):
354 """*404* `Not Found`
356 Raise if a resource does not exist and never existed.
357 """
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 )
366class MethodNotAllowed(HTTPException):
367 """*405* `Method Not Allowed`
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.
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 """
377 code = 405
378 description = "The method is not allowed for the requested URL."
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
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
402class NotAcceptable(HTTPException):
403 """*406* `Not Acceptable`
405 Raise if the server can't return any content conforming to the
406 `Accept` headers of the client.
407 """
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 )
418class RequestTimeout(HTTPException):
419 """*408* `Request Timeout`
421 Raise to signalize a timeout.
422 """
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 )
431class Conflict(HTTPException):
432 """*409* `Conflict`
434 Raise to signal that a request cannot be completed because it conflicts
435 with the current state on the server.
437 .. versionadded:: 0.7
438 """
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 )
448class Gone(HTTPException):
449 """*410* `Gone`
451 Raise if a resource existed previously and went away without new location.
452 """
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 )
462class LengthRequired(HTTPException):
463 """*411* `Length Required`
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 """
469 code = 411
470 description = (
471 "A request with this method requires a valid <code>Content-"
472 "Length</code> header."
473 )
476class PreconditionFailed(HTTPException):
477 """*412* `Precondition Failed`
479 Status code used in combination with ``If-Match``, ``If-None-Match``, or
480 ``If-Unmodified-Since``.
481 """
483 code = 412
484 description = (
485 "The precondition on the request for the URL failed positive evaluation."
486 )
489class RequestEntityTooLarge(HTTPException):
490 """*413* `Request Entity Too Large`
492 The status code one should return if the data submitted exceeded a given
493 limit.
494 """
496 code = 413
497 description = "The data value transmitted exceeds the capacity limit."
500class RequestURITooLarge(HTTPException):
501 """*414* `Request URI Too Large`
503 Like *413* but for too long URLs.
504 """
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 )
513class UnsupportedMediaType(HTTPException):
514 """*415* `Unsupported Media Type`
516 The status code returned if the server is unable to handle the media type
517 the client transmitted.
518 """
520 code = 415
521 description = (
522 "The server does not support the media type transmitted in the request."
523 )
526class RequestedRangeNotSatisfiable(HTTPException):
527 """*416* `Requested Range Not Satisfiable`
529 The client asked for an invalid part of the file.
531 .. versionadded:: 0.7
532 """
534 code = 416
535 description = "The server cannot provide the requested range."
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
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
562class ExpectationFailed(HTTPException):
563 """*417* `Expectation Failed`
565 The server cannot meet the requirements of the Expect request-header.
567 .. versionadded:: 0.7
568 """
570 code = 417
571 description = "The server could not meet the requirements of the Expect header"
574class ImATeapot(HTTPException):
575 """*418* `I'm a teapot`
577 The server should return this if it is a teapot and someone attempted
578 to brew coffee with it.
580 .. versionadded:: 0.7
581 """
583 code = 418
584 description = "This server is a teapot, not a coffee machine"
587class MisdirectedRequest(HTTPException):
588 """421 Misdirected Request
590 Indicates that the request was directed to a server that is not able to
591 produce a response.
593 .. versionadded:: 3.1
594 """
596 code = 421
597 description = "The server is not able to produce a response."
600class UnprocessableEntity(HTTPException):
601 """*422* `Unprocessable Entity`
603 Used if the request is well formed, but the instructions are otherwise
604 incorrect.
605 """
607 code = 422
608 description = (
609 "The request was well-formed but was unable to be followed due"
610 " to semantic errors."
611 )
614class Locked(HTTPException):
615 """*423* `Locked`
617 Used if the resource that is being accessed is locked.
618 """
620 code = 423
621 description = "The resource that is being accessed is locked."
624class FailedDependency(HTTPException):
625 """*424* `Failed Dependency`
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 """
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 )
639class PreconditionRequired(HTTPException):
640 """*428* `Precondition Required`
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 """
651 code = 428
652 description = (
653 "This request is required to be conditional; try using"
654 ' "If-Match" or "If-Unmodified-Since".'
655 )
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 """
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
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)
680 if self.retry_after:
681 if isinstance(self.retry_after, datetime):
682 from .http import http_date
684 value = http_date(self.retry_after)
685 else:
686 value = str(self.retry_after)
688 headers.append(("Retry-After", value))
690 return headers
693class TooManyRequests(_RetryAfter):
694 """*429* `Too Many Requests`
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.
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`.
706 .. versionchanged:: 1.0
707 Added ``retry_after`` parameter.
708 """
710 code = 429
711 description = "This user has exceeded an allotted request count. Try again later."
714class RequestHeaderFieldsTooLarge(HTTPException):
715 """*431* `Request Header Fields Too Large`
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 """
722 code = 431
723 description = "One or more header fields exceeds the maximum size."
726class UnavailableForLegalReasons(HTTPException):
727 """*451* `Unavailable For Legal Reasons`
729 This status code indicates that the server is denying access to the
730 resource as a consequence of a legal demand.
731 """
733 code = 451
734 description = "Unavailable for legal reasons."
737class InternalServerError(HTTPException):
738 """*500* `Internal Server Error`
740 Raise if an internal server error occurred. This is a good fallback if an
741 unknown error occurred in the dispatcher.
743 .. versionchanged:: 1.0.0
744 Added the :attr:`original_exception` attribute.
745 """
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 )
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)
767class NotImplemented(HTTPException):
768 """*501* `Not Implemented`
770 Raise if the application does not support the action requested by the
771 browser.
772 """
774 code = 501
775 description = "The server does not support the action requested by the browser."
778class BadGateway(HTTPException):
779 """*502* `Bad Gateway`
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 """
786 code = 502
787 description = (
788 "The proxy server received an invalid response from an upstream server."
789 )
792class ServiceUnavailable(_RetryAfter):
793 """*503* `Service Unavailable`
795 Status code you should return if a service is temporarily
796 unavailable.
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`.
802 .. versionchanged:: 1.0
803 Added ``retry_after`` parameter.
804 """
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 )
814class GatewayTimeout(HTTPException):
815 """*504* `Gateway Timeout`
817 Status code you should return if a connection to an upstream server
818 times out.
819 """
821 code = 504
822 description = "The connection to an upstream server timed out."
825class HTTPVersionNotSupported(HTTPException):
826 """*505* `HTTP Version Not Supported`
828 The server does not support the HTTP protocol version used in the request.
829 """
831 code = 505
832 description = (
833 "The server does not support the HTTP protocol version used in the request."
834 )
837default_exceptions: dict[int, type[HTTPException]] = {}
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
854_find_exceptions()
855del _find_exceptions
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.
864 The rest of the arguments are forwarded to the exception constructor.
865 """
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)
878 def __call__(
879 self, code: int | SansIOResponse, *args: t.Any, **kwargs: t.Any
880 ) -> t.NoReturn:
881 from .sansio.response import Response
883 if isinstance(code, Response):
884 raise HTTPException(response=code)
886 if code not in self.mapping:
887 raise LookupError(f"no exception for {code!r}")
889 raise self.mapping[code](*args, **kwargs)
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.
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::
900 abort(404) # 404 Not Found
901 abort(Response('Hello World'))
903 """
904 _aborter(status, *args, **kwargs)
907_aborter: Aborter = Aborter()