1from enum import IntEnum
2
3
4class StatusCode(IntEnum):
5    """HTTP status codes and reason phrases
6    Status codes from the following RFCs are all observed:
7        * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616
8        * RFC 6585: Additional HTTP Status Codes
9        * RFC 3229: Delta encoding in HTTP
10        * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518
11        * RFC 5842: Binding Extensions to WebDAV
12        * RFC 7238: Permanent Redirect
13        * RFC 2295: Transparent Content Negotiation in HTTP
14        * RFC 2774: An HTTP Extension Framework
15        * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2)
16        * RFC 2324: Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)
17        * RFC 7725: An HTTP Status Code to Report Legal Obstacles
18    """
19
20    def __new__(cls, value: int, phrase: str = "") -> "StatusCode":
21        obj = int.__new__(cls, value)  # type: ignore
22        obj._value_ = value
23
24        obj.phrase = phrase
25        return obj
26
27    def __str__(self) -> str:
28        return str(self.value)
29
30    @classmethod
31    def get_reason_phrase(cls, value: int) -> str:
32        try:
33            return StatusCode(value).phrase  # type: ignore
34        except ValueError:
35            return ""
36
37    @classmethod
38    def is_redirect(cls, value: int) -> bool:
39        return value in (
40            # 301 (Cacheable redirect. Method may change to GET.)
41            StatusCode.MOVED_PERMANENTLY,
42            # 302 (Uncacheable redirect. Method may change to GET.)
43            StatusCode.FOUND,
44            # 303 (Client should make a GET or HEAD request.)
45            StatusCode.SEE_OTHER,
46            # 307 (Equiv. 302, but retain method)
47            StatusCode.TEMPORARY_REDIRECT,
48            # 308 (Equiv. 301, but retain method)
49            StatusCode.PERMANENT_REDIRECT,
50        )
51
52    @classmethod
53    def is_error(cls, value: int) -> bool:
54        return 400 <= value <= 599
55
56    @classmethod
57    def is_client_error(cls, value: int) -> bool:
58        return 400 <= value <= 499
59
60    @classmethod
61    def is_server_error(cls, value: int) -> bool:
62        return 500 <= value <= 599
63
64    # informational
65    CONTINUE = 100, "Continue"
66    SWITCHING_PROTOCOLS = 101, "Switching Protocols"
67    PROCESSING = 102, "Processing"
68
69    # success
70    OK = 200, "OK"
71    CREATED = 201, "Created"
72    ACCEPTED = 202, "Accepted"
73    NON_AUTHORITATIVE_INFORMATION = 203, "Non-Authoritative Information"
74    NO_CONTENT = 204, "No Content"
75    RESET_CONTENT = 205, "Reset Content"
76    PARTIAL_CONTENT = 206, "Partial Content"
77    MULTI_STATUS = 207, "Multi-Status"
78    ALREADY_REPORTED = 208, "Already Reported"
79    IM_USED = 226, "IM Used"
80
81    # redirection
82    MULTIPLE_CHOICES = 300, "Multiple Choices"
83    MOVED_PERMANENTLY = 301, "Moved Permanently"
84    FOUND = 302, "Found"
85    SEE_OTHER = 303, "See Other"
86    NOT_MODIFIED = 304, "Not Modified"
87    USE_PROXY = 305, "Use Proxy"
88    TEMPORARY_REDIRECT = 307, "Temporary Redirect"
89    PERMANENT_REDIRECT = 308, "Permanent Redirect"
90
91    # client error
92    BAD_REQUEST = 400, "Bad Request"
93    UNAUTHORIZED = 401, "Unauthorized"
94    PAYMENT_REQUIRED = 402, "Payment Required"
95    FORBIDDEN = 403, "Forbidden"
96    NOT_FOUND = 404, "Not Found"
97    METHOD_NOT_ALLOWED = 405, "Method Not Allowed"
98    NOT_ACCEPTABLE = 406, "Not Acceptable"
99    PROXY_AUTHENTICATION_REQUIRED = 407, "Proxy Authentication Required"
100    REQUEST_TIMEOUT = 408, "Request Timeout"
101    CONFLICT = 409, "Conflict"
102    GONE = 410, "Gone"
103    LENGTH_REQUIRED = 411, "Length Required"
104    PRECONDITION_FAILED = 412, "Precondition Failed"
105    REQUEST_ENTITY_TOO_LARGE = 413, "Request Entity Too Large"
106    REQUEST_URI_TOO_LONG = 414, "Request-URI Too Long"
107    UNSUPPORTED_MEDIA_TYPE = 415, "Unsupported Media Type"
108    REQUESTED_RANGE_NOT_SATISFIABLE = 416, "Requested Range Not Satisfiable"
109    EXPECTATION_FAILED = 417, "Expectation Failed"
110    IM_A_TEAPOT = 418, "I'm a teapot"
111    MISDIRECTED_REQUEST = 421, "Misdirected Request"
112    UNPROCESSABLE_ENTITY = 422, "Unprocessable Entity"
113    LOCKED = 423, "Locked"
114    FAILED_DEPENDENCY = 424, "Failed Dependency"
115    UPGRADE_REQUIRED = 426, "Upgrade Required"
116    PRECONDITION_REQUIRED = 428, "Precondition Required"
117    TOO_MANY_REQUESTS = 429, "Too Many Requests"
118    REQUEST_HEADER_FIELDS_TOO_LARGE = 431, "Request Header Fields Too Large"
119    UNAVAILABLE_FOR_LEGAL_REASONS = 451, "Unavailable For Legal Reasons"
120
121    # server errors
122    INTERNAL_SERVER_ERROR = 500, "Internal Server Error"
123    NOT_IMPLEMENTED = 501, "Not Implemented"
124    BAD_GATEWAY = 502, "Bad Gateway"
125    SERVICE_UNAVAILABLE = 503, "Service Unavailable"
126    GATEWAY_TIMEOUT = 504, "Gateway Timeout"
127    HTTP_VERSION_NOT_SUPPORTED = 505, "HTTP Version Not Supported"
128    VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates"
129    INSUFFICIENT_STORAGE = 507, "Insufficient Storage"
130    LOOP_DETECTED = 508, "Loop Detected"
131    NOT_EXTENDED = 510, "Not Extended"
132    NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required"
133
134
135codes = StatusCode
136
137#  Include lower-case styles for `requests` compatibility.
138for code in codes:
139    setattr(codes, code._name_.lower(), int(code))
140