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
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 as SansIOResponse
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: SansIOResponse | 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 @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.
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
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)
169 def __call__(
170 self, environ: WSGIEnvironment, start_response: StartResponse
171 ) -> t.Iterable[bytes]:
172 """Call the exception as WSGI application.
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)
181 def __str__(self) -> str:
182 code = self.code if self.code is not None else "???"
183 return f"{code} {self.name}: {self.description}"
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}'>"
190class BadRequest(HTTPException):
191 """*400* `Bad Request`
193 Raise if the browser sends something to the application the application
194 or server cannot handle.
195 """
197 code = 400
198 description = (
199 "The browser (or proxy) sent a request that this server could not understand."
200 )
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 """
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
214 def __init__(self, arg: object | None = None, *args: t.Any, **kwargs: t.Any):
215 super().__init__(*args, **kwargs)
217 if arg is None:
218 KeyError.__init__(self)
219 else:
220 KeyError.__init__(self, arg)
222 @property
223 def description(self) -> str:
224 if self.show_exception:
225 return f"{self._description}\n{KeyError.__name__}: {KeyError.__str__(self)}"
227 return self._description
229 @description.setter
230 def description(self, value: str) -> None:
231 self._description = value
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.
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.
245 .. versionadded:: 0.8
246 """
249class SecurityError(BadRequest):
250 """Raised if something triggers a security error. This is otherwise
251 exactly like a bad request error.
253 .. versionadded:: 0.9
254 """
257class BadHost(BadRequest):
258 """Raised if the submitted host is badly formatted.
260 .. versionadded:: 0.11.2
261 """
264class Unauthorized(HTTPException):
265 """*401* ``Unauthorized``
267 Raise if the user is not authorized to access a resource.
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.
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).
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.
286 .. versionchanged:: 0.15.3
287 If the ``www_authenticate`` argument is not set, the
288 ``WWW-Authenticate`` header is not set.
290 .. versionchanged:: 0.15.3
291 The ``response`` argument was restored.
293 .. versionchanged:: 0.15.1
294 ``description`` was moved back as the first argument, restoring
295 its previous position.
297 .. versionchanged:: 0.15.0
298 ``www_authenticate`` was added as the first argument, ahead of
299 ``description``.
300 """
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 )
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)
318 from .datastructures import WWWAuthenticate
320 if isinstance(www_authenticate, WWWAuthenticate):
321 www_authenticate = (www_authenticate,)
323 self.www_authenticate = www_authenticate
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
336class Forbidden(HTTPException):
337 """*403* `Forbidden`
339 Raise if the user doesn't have the permission for the requested resource
340 but was authenticated.
341 """
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 )
351class NotFound(HTTPException):
352 """*404* `Not Found`
354 Raise if a resource does not exist and never existed.
355 """
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 )
364class MethodNotAllowed(HTTPException):
365 """*405* `Method Not Allowed`
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.
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 """
375 code = 405
376 description = "The method is not allowed for the requested URL."
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
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
400class NotAcceptable(HTTPException):
401 """*406* `Not Acceptable`
403 Raise if the server can't return any content conforming to the
404 `Accept` headers of the client.
405 """
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 )
416class RequestTimeout(HTTPException):
417 """*408* `Request Timeout`
419 Raise to signalize a timeout.
420 """
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 )
429class Conflict(HTTPException):
430 """*409* `Conflict`
432 Raise to signal that a request cannot be completed because it conflicts
433 with the current state on the server.
435 .. versionadded:: 0.7
436 """
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 )
446class Gone(HTTPException):
447 """*410* `Gone`
449 Raise if a resource existed previously and went away without new location.
450 """
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 )
460class LengthRequired(HTTPException):
461 """*411* `Length Required`
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 """
467 code = 411
468 description = (
469 "A request with this method requires a valid <code>Content-"
470 "Length</code> header."
471 )
474class PreconditionFailed(HTTPException):
475 """*412* `Precondition Failed`
477 Status code used in combination with ``If-Match``, ``If-None-Match``, or
478 ``If-Unmodified-Since``.
479 """
481 code = 412
482 description = (
483 "The precondition on the request for the URL failed positive evaluation."
484 )
487class RequestEntityTooLarge(HTTPException):
488 """*413* `Request Entity Too Large`
490 The status code one should return if the data submitted exceeded a given
491 limit.
492 """
494 code = 413
495 description = "The data value transmitted exceeds the capacity limit."
498class RequestURITooLarge(HTTPException):
499 """*414* `Request URI Too Large`
501 Like *413* but for too long URLs.
502 """
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 )
511class UnsupportedMediaType(HTTPException):
512 """*415* `Unsupported Media Type`
514 The status code returned if the server is unable to handle the media type
515 the client transmitted.
516 """
518 code = 415
519 description = (
520 "The server does not support the media type transmitted in the request."
521 )
524class RequestedRangeNotSatisfiable(HTTPException):
525 """*416* `Requested Range Not Satisfiable`
527 The client asked for an invalid part of the file.
529 .. versionadded:: 0.7
530 """
532 code = 416
533 description = "The server cannot provide the requested range."
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
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
560class ExpectationFailed(HTTPException):
561 """*417* `Expectation Failed`
563 The server cannot meet the requirements of the Expect request-header.
565 .. versionadded:: 0.7
566 """
568 code = 417
569 description = "The server could not meet the requirements of the Expect header"
572class ImATeapot(HTTPException):
573 """*418* `I'm a teapot`
575 The server should return this if it is a teapot and someone attempted
576 to brew coffee with it.
578 .. versionadded:: 0.7
579 """
581 code = 418
582 description = "This server is a teapot, not a coffee machine"
585class MisdirectedRequest(HTTPException):
586 """421 Misdirected Request
588 Indicates that the request was directed to a server that is not able to
589 produce a response.
591 .. versionadded:: 3.1
592 """
594 code = 421
595 description = "The server is not able to produce a response."
598class UnprocessableEntity(HTTPException):
599 """*422* `Unprocessable Entity`
601 Used if the request is well formed, but the instructions are otherwise
602 incorrect.
603 """
605 code = 422
606 description = (
607 "The request was well-formed but was unable to be followed due"
608 " to semantic errors."
609 )
612class Locked(HTTPException):
613 """*423* `Locked`
615 Used if the resource that is being accessed is locked.
616 """
618 code = 423
619 description = "The resource that is being accessed is locked."
622class FailedDependency(HTTPException):
623 """*424* `Failed Dependency`
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 """
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 )
637class PreconditionRequired(HTTPException):
638 """*428* `Precondition Required`
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 """
649 code = 428
650 description = (
651 "This request is required to be conditional; try using"
652 ' "If-Match" or "If-Unmodified-Since".'
653 )
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 """
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
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)
678 if self.retry_after:
679 if isinstance(self.retry_after, datetime):
680 from .http import http_date
682 value = http_date(self.retry_after)
683 else:
684 value = str(self.retry_after)
686 headers.append(("Retry-After", value))
688 return headers
691class TooManyRequests(_RetryAfter):
692 """*429* `Too Many Requests`
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.
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`.
704 .. versionchanged:: 1.0
705 Added ``retry_after`` parameter.
706 """
708 code = 429
709 description = "This user has exceeded an allotted request count. Try again later."
712class RequestHeaderFieldsTooLarge(HTTPException):
713 """*431* `Request Header Fields Too Large`
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 """
720 code = 431
721 description = "One or more header fields exceeds the maximum size."
724class UnavailableForLegalReasons(HTTPException):
725 """*451* `Unavailable For Legal Reasons`
727 This status code indicates that the server is denying access to the
728 resource as a consequence of a legal demand.
729 """
731 code = 451
732 description = "Unavailable for legal reasons."
735class InternalServerError(HTTPException):
736 """*500* `Internal Server Error`
738 Raise if an internal server error occurred. This is a good fallback if an
739 unknown error occurred in the dispatcher.
741 .. versionchanged:: 1.0.0
742 Added the :attr:`original_exception` attribute.
743 """
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 )
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)
765class NotImplemented(HTTPException):
766 """*501* `Not Implemented`
768 Raise if the application does not support the action requested by the
769 browser.
770 """
772 code = 501
773 description = "The server does not support the action requested by the browser."
776class BadGateway(HTTPException):
777 """*502* `Bad Gateway`
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 """
784 code = 502
785 description = (
786 "The proxy server received an invalid response from an upstream server."
787 )
790class ServiceUnavailable(_RetryAfter):
791 """*503* `Service Unavailable`
793 Status code you should return if a service is temporarily
794 unavailable.
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`.
800 .. versionchanged:: 1.0
801 Added ``retry_after`` parameter.
802 """
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 )
812class GatewayTimeout(HTTPException):
813 """*504* `Gateway Timeout`
815 Status code you should return if a connection to an upstream server
816 times out.
817 """
819 code = 504
820 description = "The connection to an upstream server timed out."
823class HTTPVersionNotSupported(HTTPException):
824 """*505* `HTTP Version Not Supported`
826 The server does not support the HTTP protocol version used in the request.
827 """
829 code = 505
830 description = (
831 "The server does not support the HTTP protocol version used in the request."
832 )
835default_exceptions: dict[int, type[HTTPException]] = {}
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
852_find_exceptions()
853del _find_exceptions
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.
862 The rest of the arguments are forwarded to the exception constructor.
863 """
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)
876 def __call__(
877 self, code: int | SansIOResponse, *args: t.Any, **kwargs: t.Any
878 ) -> t.NoReturn:
879 from .sansio.response import Response
881 if isinstance(code, Response):
882 raise HTTPException(response=code)
884 if code not in self.mapping:
885 raise LookupError(f"no exception for {code!r}")
887 raise self.mapping[code](*args, **kwargs)
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.
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::
898 abort(404) # 404 Not Found
899 abort(Response('Hello World'))
901 """
902 _aborter(status, *args, **kwargs)
905_aborter: Aborter = Aborter()