1"""
2HTTP exceptions that map to HTTP status codes.
3
4Raise these (or your own subclasses) from views, middleware, or helpers to
5abort with a specific status. The framework reads `status_code` off the
6exception and renders the matching error response.
7"""
8
9from .response import is_valid_status_code
10
11
12class HTTPException(Exception):
13 """Base class for exceptions that map to HTTP status codes.
14
15 Subclass to define your own:
16
17 class PaymentRequiredError402(HTTPException):
18 status_code = 402
19 """
20
21 status_code: int = 500
22
23 def __init_subclass__(cls, **kwargs: object) -> None:
24 super().__init_subclass__(**kwargs)
25 # Catch a bad status at the line that wrote it. Response
26 # construction rejects statuses outside this range, so a broken
27 # subclass (e.g. a 1xx, a string, or None) would otherwise crash
28 # the error renderers at request time instead of failing at
29 # import time.
30 if not is_valid_status_code(cls.status_code):
31 raise ValueError(
32 f"{cls.__name__}.status_code must be an integer from "
33 f"200 to 599, got {cls.status_code!r}."
34 )
35
36
37class BadRequestError400(HTTPException):
38 """The request is malformed and cannot be processed (HTTP 400)"""
39
40 status_code = 400
41
42
43class ForbiddenError403(HTTPException):
44 """The user did not have permission to do that (HTTP 403)"""
45
46 status_code = 403
47
48
49class NotFoundError404(HTTPException):
50 """The requested resource was not found (HTTP 404)"""
51
52 status_code = 404
53
54
55class UnsupportedMediaTypeError415(HTTPException):
56 """The request body is in a media type the server does not parse (HTTP 415)"""
57
58 status_code = 415
59
60
61class SuspiciousOperationError400(BadRequestError400):
62 """The user did something suspicious (HTTP 400)"""
63
64
65class SuspiciousMultipartFormError400(SuspiciousOperationError400):
66 """Suspect MIME request in multipart form data"""
67
68
69class SuspiciousFileOperationError400(SuspiciousOperationError400):
70 """A Suspicious filesystem operation was attempted"""
71
72
73class TooManyFieldsSentError400(SuspiciousOperationError400):
74 """
75 The number of fields in a GET or POST request exceeded
76 settings.DATA_UPLOAD_MAX_NUMBER_FIELDS.
77 """
78
79
80class TooManyFilesSentError400(SuspiciousOperationError400):
81 """
82 The number of fields in a GET or POST request exceeded
83 settings.DATA_UPLOAD_MAX_NUMBER_FILES.
84 """
85
86
87class ContentTooLargeError413(HTTPException):
88 """The request body is larger than the server or app accepts (HTTP 413).
89
90 Raised when a body exceeds settings.SERVER_MAX_REQUEST_BODY_SIZE at the
91 server edge, or when the bytes read into memory (excluding file
92 uploads) exceed settings.DATA_UPLOAD_MAX_MEMORY_SIZE.
93 """
94
95 status_code = 413
96
97
98def status_for_exception(exc: Exception) -> int:
99 """Status code for rendering an exception as an error response.
100
101 An `HTTPException`'s `status_code`, 500 for anything else — clamped
102 to what Response construction accepts, so a mutated or nonsense
103 status (subclass definitions are validated, instances can be
104 poked) can never crash an error renderer.
105 """
106 status = exc.status_code if isinstance(exc, HTTPException) else 500
107 return status if is_valid_status_code(status) else 500