ResponseNotRead,
StreamConsumed,
)
-from .status_codes import codes
-from .utils import (
- get_reason_phrase,
- is_known_encoding,
- normalize_header_key,
- normalize_header_value,
-)
+from .status_codes import StatusCode
+from .utils import is_known_encoding, normalize_header_key, normalize_header_value
URLTypes = typing.Union["URL", str]
request: Request = None,
history: typing.List["Response"] = None,
):
- try:
- # Use a StatusCode IntEnum if possible, for a nicer representation.
- self.status_code = codes(status_code) # type: int
- except ValueError:
- self.status_code = status_code
- self.reason_phrase = reason_phrase or get_reason_phrase(status_code)
+ self.status_code = StatusCode.enum_or_int(status_code)
+ self.reason_phrase = StatusCode.get_reason_phrase(status_code)
self.protocol = protocol
self.headers = Headers(headers)
@property
def is_redirect(self) -> bool:
- return (
- self.status_code
- in (
- codes.MOVED_PERMANENTLY,
- codes.FOUND,
- codes.SEE_OTHER,
- codes.TEMPORARY_REDIRECT,
- codes.PERMANENT_REDIRECT,
- )
- and "location" in self.headers
- )
+ return StatusCode.is_redirect(self.status_code) and "location" in self.headers
def raise_for_status(self) -> None:
"""
"For more information check: https://httpstatuses.com/{0.status_code}"
)
- if 400 <= self.status_code < 500:
+ if StatusCode.is_client_error(self.status_code):
message = message.format(self, error_type="Client Error")
- elif 500 <= self.status_code < 600:
+ elif StatusCode.is_server_error(self.status_code):
message = message.format(self, error_type="Server Error")
else:
message = ""
-from http import HTTPStatus
+from enum import IntEnum
-codes = HTTPStatus
+
+class StatusCode(IntEnum):
+ """HTTP status codes and reason phrases
+ Status codes from the following RFCs are all observed:
+ * RFC 7231: Hypertext Transfer Protocol (HTTP/1.1), obsoletes 2616
+ * RFC 6585: Additional HTTP Status Codes
+ * RFC 3229: Delta encoding in HTTP
+ * RFC 4918: HTTP Extensions for WebDAV, obsoletes 2518
+ * RFC 5842: Binding Extensions to WebDAV
+ * RFC 7238: Permanent Redirect
+ * RFC 2295: Transparent Content Negotiation in HTTP
+ * RFC 2774: An HTTP Extension Framework
+ * RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2)
+ """
+
+ def __new__(cls, value: int, phrase: str = "") -> "StatusCode":
+ obj = int.__new__(cls, value) # type: ignore
+ obj._value_ = value
+
+ obj.phrase = phrase
+ return obj
+
+ def __str__(self) -> str:
+ return str(self.value)
+
+ @classmethod
+ def enum_or_int(cls, value: int) -> int:
+ try:
+ return StatusCode(value)
+ except ValueError:
+ return value
+
+ @classmethod
+ def get_reason_phrase(cls, value: int) -> str:
+ try:
+ return StatusCode(value).phrase # type: ignore
+ except ValueError:
+ return ""
+
+ @classmethod
+ def is_redirect(cls, value: int) -> bool:
+ return value in (
+ StatusCode.MOVED_PERMANENTLY, # 301 (Cacheable redirect. Method may change to GET.)
+ StatusCode.FOUND, # 302 (Uncacheable redirect. Method may change to GET.)
+ StatusCode.SEE_OTHER, # 303 (Client should make a GET or HEAD request.)
+ StatusCode.TEMPORARY_REDIRECT, # 307 (Equiv. 302, but retain method)
+ StatusCode.PERMANENT_REDIRECT, # 308 (Equiv. 301, but retain method)
+ )
+
+ @classmethod
+ def is_client_error(cls, value: int) -> bool:
+ return value >= 400 and value <= 499
+
+ @classmethod
+ def is_server_error(cls, value: int) -> bool:
+ return value >= 500 and value <= 599
+
+ # informational
+ CONTINUE = 100, "Continue"
+ SWITCHING_PROTOCOLS = 101, "Switching Protocols"
+ PROCESSING = 102, "Processing"
+
+ # success
+ OK = 200, "OK"
+ CREATED = 201, "Created"
+ ACCEPTED = 202, "Accepted"
+ NON_AUTHORITATIVE_INFORMATION = 203, "Non-Authoritative Information"
+ NO_CONTENT = 204, "No Content"
+ RESET_CONTENT = 205, "Reset Content"
+ PARTIAL_CONTENT = 206, "Partial Content"
+ MULTI_STATUS = 207, "Multi-Status"
+ ALREADY_REPORTED = 208, "Already Reported"
+ IM_USED = 226, "IM Used"
+
+ # redirection
+ MULTIPLE_CHOICES = 300, "Multiple Choices"
+ MOVED_PERMANENTLY = 301, "Moved Permanently"
+ FOUND = 302, "Found"
+ SEE_OTHER = 303, "See Other"
+ NOT_MODIFIED = 304, "Not Modified"
+ USE_PROXY = 305, "Use Proxy"
+ TEMPORARY_REDIRECT = 307, "Temporary Redirect"
+ PERMANENT_REDIRECT = 308, "Permanent Redirect"
+
+ # client error
+ BAD_REQUEST = 400, "Bad Request"
+ UNAUTHORIZED = 401, "Unauthorized"
+ PAYMENT_REQUIRED = 402, "Payment Required"
+ FORBIDDEN = 403, "Forbidden"
+ NOT_FOUND = 404, "Not Found"
+ METHOD_NOT_ALLOWED = 405, "Method Not Allowed"
+ NOT_ACCEPTABLE = 406, "Not Acceptable"
+ PROXY_AUTHENTICATION_REQUIRED = 407, "Proxy Authentication Required"
+ REQUEST_TIMEOUT = 408, "Request Timeout"
+ CONFLICT = 409, "Conflict"
+ GONE = 410, "Gone"
+ LENGTH_REQUIRED = 411, "Length Required"
+ PRECONDITION_FAILED = 412, "Precondition Failed"
+ REQUEST_ENTITY_TOO_LARGE = 413, "Request Entity Too Large"
+ REQUEST_URI_TOO_LONG = 414, "Request-URI Too Long"
+ UNSUPPORTED_MEDIA_TYPE = 415, "Unsupported Media Type"
+ REQUESTED_RANGE_NOT_SATISFIABLE = 416, "Requested Range Not Satisfiable"
+ EXPECTATION_FAILED = 417, "Expectation Failed"
+ MISDIRECTED_REQUEST = 421, "Misdirected Request"
+ UNPROCESSABLE_ENTITY = 422, "Unprocessable Entity"
+ LOCKED = 423, "Locked"
+ FAILED_DEPENDENCY = 424, "Failed Dependency"
+ UPGRADE_REQUIRED = 426, "Upgrade Required"
+ PRECONDITION_REQUIRED = 428, "Precondition Required"
+ TOO_MANY_REQUESTS = 429, "Too Many Requests"
+ REQUEST_HEADER_FIELDS_TOO_LARGE = 431, "Request Header Fields Too Large"
+
+ # server errors
+ INTERNAL_SERVER_ERROR = 500, "Internal Server Error"
+ NOT_IMPLEMENTED = 501, "Not Implemented"
+ BAD_GATEWAY = 502, "Bad Gateway"
+ SERVICE_UNAVAILABLE = 503, "Service Unavailable"
+ GATEWAY_TIMEOUT = 504, "Gateway Timeout"
+ HTTP_VERSION_NOT_SUPPORTED = 505, "HTTP Version Not Supported"
+ VARIANT_ALSO_NEGOTIATES = 506, "Variant Also Negotiates"
+ INSUFFICIENT_STORAGE = 507, "Insufficient Storage"
+ LOOP_DETECTED = 508, "Loop Detected"
+ NOT_EXTENDED = 510, "Not Extended"
+ NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required"
+
+
+codes = StatusCode