v0.163.0
  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