Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/werkzeug/exceptions.py: 65%
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: str | 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 UnprocessableEntity(HTTPException):
575 """*422* `Unprocessable Entity`
577 Used if the request is well formed, but the instructions are otherwise
578 incorrect.
579 """
581 code = 422
582 description = (
583 "The request was well-formed but was unable to be followed due"
584 " to semantic errors."
585 )
588class Locked(HTTPException):
589 """*423* `Locked`
591 Used if the resource that is being accessed is locked.
592 """
594 code = 423
595 description = "The resource that is being accessed is locked."
598class FailedDependency(HTTPException):
599 """*424* `Failed Dependency`
601 Used if the method could not be performed on the resource
602 because the requested action depended on another action and that action failed.
603 """
605 code = 424
606 description = (
607 "The method could not be performed on the resource because the"
608 " requested action depended on another action and that action"
609 " failed."
610 )
613class PreconditionRequired(HTTPException):
614 """*428* `Precondition Required`
616 The server requires this request to be conditional, typically to prevent
617 the lost update problem, which is a race condition between two or more
618 clients attempting to update a resource through PUT or DELETE. By requiring
619 each client to include a conditional header ("If-Match" or "If-Unmodified-
620 Since") with the proper value retained from a recent GET request, the
621 server ensures that each client has at least seen the previous revision of
622 the resource.
623 """
625 code = 428
626 description = (
627 "This request is required to be conditional; try using"
628 ' "If-Match" or "If-Unmodified-Since".'
629 )
632class _RetryAfter(HTTPException):
633 """Adds an optional ``retry_after`` parameter which will set the
634 ``Retry-After`` header. May be an :class:`int` number of seconds or
635 a :class:`~datetime.datetime`.
636 """
638 def __init__(
639 self,
640 description: str | None = None,
641 response: Response | None = None,
642 retry_after: datetime | int | None = None,
643 ) -> None:
644 super().__init__(description, response)
645 self.retry_after = retry_after
647 def get_headers(
648 self,
649 environ: WSGIEnvironment | None = None,
650 scope: dict[str, t.Any] | None = None,
651 ) -> list[tuple[str, str]]:
652 headers = super().get_headers(environ, scope)
654 if self.retry_after:
655 if isinstance(self.retry_after, datetime):
656 from .http import http_date
658 value = http_date(self.retry_after)
659 else:
660 value = str(self.retry_after)
662 headers.append(("Retry-After", value))
664 return headers
667class TooManyRequests(_RetryAfter):
668 """*429* `Too Many Requests`
670 The server is limiting the rate at which this user receives
671 responses, and this request exceeds that rate. (The server may use
672 any convenient method to identify users and their request rates).
673 The server may include a "Retry-After" header to indicate how long
674 the user should wait before retrying.
676 :param retry_after: If given, set the ``Retry-After`` header to this
677 value. May be an :class:`int` number of seconds or a
678 :class:`~datetime.datetime`.
680 .. versionchanged:: 1.0
681 Added ``retry_after`` parameter.
682 """
684 code = 429
685 description = "This user has exceeded an allotted request count. Try again later."
688class RequestHeaderFieldsTooLarge(HTTPException):
689 """*431* `Request Header Fields Too Large`
691 The server refuses to process the request because the header fields are too
692 large. One or more individual fields may be too large, or the set of all
693 headers is too large.
694 """
696 code = 431
697 description = "One or more header fields exceeds the maximum size."
700class UnavailableForLegalReasons(HTTPException):
701 """*451* `Unavailable For Legal Reasons`
703 This status code indicates that the server is denying access to the
704 resource as a consequence of a legal demand.
705 """
707 code = 451
708 description = "Unavailable for legal reasons."
711class InternalServerError(HTTPException):
712 """*500* `Internal Server Error`
714 Raise if an internal server error occurred. This is a good fallback if an
715 unknown error occurred in the dispatcher.
717 .. versionchanged:: 1.0.0
718 Added the :attr:`original_exception` attribute.
719 """
721 code = 500
722 description = (
723 "The server encountered an internal error and was unable to"
724 " complete your request. Either the server is overloaded or"
725 " there is an error in the application."
726 )
728 def __init__(
729 self,
730 description: str | None = None,
731 response: Response | None = None,
732 original_exception: BaseException | None = None,
733 ) -> None:
734 #: The original exception that caused this 500 error. Can be
735 #: used by frameworks to provide context when handling
736 #: unexpected errors.
737 self.original_exception = original_exception
738 super().__init__(description=description, response=response)
741class NotImplemented(HTTPException):
742 """*501* `Not Implemented`
744 Raise if the application does not support the action requested by the
745 browser.
746 """
748 code = 501
749 description = "The server does not support the action requested by the browser."
752class BadGateway(HTTPException):
753 """*502* `Bad Gateway`
755 If you do proxying in your application you should return this status code
756 if you received an invalid response from the upstream server it accessed
757 in attempting to fulfill the request.
758 """
760 code = 502
761 description = (
762 "The proxy server received an invalid response from an upstream server."
763 )
766class ServiceUnavailable(_RetryAfter):
767 """*503* `Service Unavailable`
769 Status code you should return if a service is temporarily
770 unavailable.
772 :param retry_after: If given, set the ``Retry-After`` header to this
773 value. May be an :class:`int` number of seconds or a
774 :class:`~datetime.datetime`.
776 .. versionchanged:: 1.0
777 Added ``retry_after`` parameter.
778 """
780 code = 503
781 description = (
782 "The server is temporarily unable to service your request due"
783 " to maintenance downtime or capacity problems. Please try"
784 " again later."
785 )
788class GatewayTimeout(HTTPException):
789 """*504* `Gateway Timeout`
791 Status code you should return if a connection to an upstream server
792 times out.
793 """
795 code = 504
796 description = "The connection to an upstream server timed out."
799class HTTPVersionNotSupported(HTTPException):
800 """*505* `HTTP Version Not Supported`
802 The server does not support the HTTP protocol version used in the request.
803 """
805 code = 505
806 description = (
807 "The server does not support the HTTP protocol version used in the request."
808 )
811default_exceptions: dict[int, type[HTTPException]] = {}
814def _find_exceptions() -> None:
815 for obj in globals().values():
816 try:
817 is_http_exception = issubclass(obj, HTTPException)
818 except TypeError:
819 is_http_exception = False
820 if not is_http_exception or obj.code is None:
821 continue
822 old_obj = default_exceptions.get(obj.code, None)
823 if old_obj is not None and issubclass(obj, old_obj):
824 continue
825 default_exceptions[obj.code] = obj
828_find_exceptions()
829del _find_exceptions
832class Aborter:
833 """When passed a dict of code -> exception items it can be used as
834 callable that raises exceptions. If the first argument to the
835 callable is an integer it will be looked up in the mapping, if it's
836 a WSGI application it will be raised in a proxy exception.
838 The rest of the arguments are forwarded to the exception constructor.
839 """
841 def __init__(
842 self,
843 mapping: dict[int, type[HTTPException]] | None = None,
844 extra: dict[int, type[HTTPException]] | None = None,
845 ) -> None:
846 if mapping is None:
847 mapping = default_exceptions
848 self.mapping = dict(mapping)
849 if extra is not None:
850 self.mapping.update(extra)
852 def __call__(
853 self, code: int | Response, *args: t.Any, **kwargs: t.Any
854 ) -> t.NoReturn:
855 from .sansio.response import Response
857 if isinstance(code, Response):
858 raise HTTPException(response=code)
860 if code not in self.mapping:
861 raise LookupError(f"no exception for {code!r}")
863 raise self.mapping[code](*args, **kwargs)
866def abort(status: int | Response, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
867 """Raises an :py:exc:`HTTPException` for the given status code or WSGI
868 application.
870 If a status code is given, it will be looked up in the list of
871 exceptions and will raise that exception. If passed a WSGI application,
872 it will wrap it in a proxy WSGI exception and raise that::
874 abort(404) # 404 Not Found
875 abort(Response('Hello World'))
877 """
878 _aborter(status, *args, **kwargs)
881_aborter: Aborter = Aborter()