Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single consistent name for status codes #1088

Merged
merged 3 commits into from
Jul 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion httpx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"ResponseClosed",
"ResponseNotRead",
"RequestNotRead",
"StatusCode",
"StreamConsumed",
"StreamError",
"ProxyError",
Expand All @@ -90,7 +91,6 @@
"URL",
"URLLib3Transport",
"URLLib3ProxyTransport",
"StatusCode",
"Cookies",
"Headers",
"QueryParams",
Expand Down
12 changes: 6 additions & 6 deletions httpx/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
ResponseNotRead,
StreamConsumed,
)
from ._status_codes import StatusCode
from ._status_codes import codes
from ._types import (
CookieTypes,
HeaderTypes,
Expand Down Expand Up @@ -660,7 +660,7 @@ def elapsed(self) -> datetime.timedelta:

@property
def reason_phrase(self) -> str:
return StatusCode.get_reason_phrase(self.status_code)
return codes.get_reason_phrase(self.status_code)

@property
def url(self) -> typing.Optional[URL]:
Expand Down Expand Up @@ -758,11 +758,11 @@ def decoder(self) -> Decoder:

@property
def is_error(self) -> bool:
return StatusCode.is_error(self.status_code)
return codes.is_error(self.status_code)

@property
def is_redirect(self) -> bool:
return StatusCode.is_redirect(self.status_code) and "location" in self.headers
return codes.is_redirect(self.status_code) and "location" in self.headers

def raise_for_status(self) -> None:
"""
Expand All @@ -773,10 +773,10 @@ def raise_for_status(self) -> None:
"For more information check: https://httpstatuses.com/{0.status_code}"
)

if StatusCode.is_client_error(self.status_code):
if codes.is_client_error(self.status_code):
message = message.format(self, error_type="Client Error")
raise HTTPStatusError(message, response=self)
elif StatusCode.is_server_error(self.status_code):
elif codes.is_server_error(self.status_code):
message = message.format(self, error_type="Server Error")
raise HTTPStatusError(message, response=self)

Expand Down
39 changes: 29 additions & 10 deletions httpx/_status_codes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import warnings
from enum import IntEnum


class StatusCode(IntEnum):
class codes(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
Expand All @@ -17,7 +18,7 @@ class StatusCode(IntEnum):
* RFC 7725: An HTTP Status Code to Report Legal Obstacles
"""

def __new__(cls, value: int, phrase: str = "") -> "StatusCode":
def __new__(cls, value: int, phrase: str = "") -> "codes":
obj = int.__new__(cls, value) # type: ignore
obj._value_ = value

Expand All @@ -30,23 +31,23 @@ def __str__(self) -> str:
@classmethod
def get_reason_phrase(cls, value: int) -> str:
try:
return StatusCode(value).phrase # type: ignore
return codes(value).phrase # type: ignore
except ValueError:
return ""

@classmethod
def is_redirect(cls, value: int) -> bool:
return value in (
# 301 (Cacheable redirect. Method may change to GET.)
StatusCode.MOVED_PERMANENTLY,
codes.MOVED_PERMANENTLY,
# 302 (Uncacheable redirect. Method may change to GET.)
StatusCode.FOUND,
codes.FOUND,
# 303 (Client should make a GET or HEAD request.)
StatusCode.SEE_OTHER,
codes.SEE_OTHER,
# 307 (Equiv. 302, but retain method)
StatusCode.TEMPORARY_REDIRECT,
codes.TEMPORARY_REDIRECT,
# 308 (Equiv. 301, but retain method)
StatusCode.PERMANENT_REDIRECT,
codes.PERMANENT_REDIRECT,
)

@classmethod
Expand Down Expand Up @@ -132,8 +133,26 @@ def is_server_error(cls, value: int) -> bool:
NETWORK_AUTHENTICATION_REQUIRED = 511, "Network Authentication Required"


codes = StatusCode

#  Include lower-case styles for `requests` compatibility.
for code in codes:
setattr(codes, code._name_.lower(), int(code))


class StatusCodeCompat:
def __call__(self, *args, **kwargs): # type: ignore
message = "`httpx.StatusCode` is deprecated. Use `httpx.codes` instead."
warnings.warn(message, DeprecationWarning)
return codes(*args, **kwargs)

def __getattr__(self, attr): # type: ignore
message = "`httpx.StatusCode` is deprecated. Use `httpx.codes` instead."
warnings.warn(message, DeprecationWarning)
return getattr(codes, attr)

def __getitem__(self, item): # type: ignore
message = "`httpx.StatusCode` is deprecated. Use `httpx.codes` instead."
warnings.warn(message, DeprecationWarning)
return codes[item]


StatusCode = StatusCodeCompat()
21 changes: 21 additions & 0 deletions tests/test_status_codes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

import httpx


Expand All @@ -6,6 +8,14 @@ def test_status_code_as_int():
assert str(httpx.codes.NOT_FOUND) == "404"


def test_status_code_value_lookup():
assert httpx.codes(404) == 404


def test_status_code_phrase_lookup():
assert httpx.codes["NOT_FOUND"] == 404


def test_lowercase_status_code():
assert httpx.codes.not_found == 404 # type: ignore

Expand All @@ -16,3 +26,14 @@ def test_reason_phrase_for_status_code():

def test_reason_phrase_for_unknown_status_code():
assert httpx.codes.get_reason_phrase(499) == ""


def test_deprecated_status_code_class():
with pytest.warns(DeprecationWarning):
assert httpx.StatusCode.NOT_FOUND == 404

with pytest.warns(DeprecationWarning):
assert httpx.StatusCode(404) == 404

with pytest.warns(DeprecationWarning):
assert httpx.StatusCode["NOT_FOUND"] == 404