From fb6d14f9e8c3ed2d7f3628906c3a1ac100a58b24 Mon Sep 17 00:00:00 2001 From: Matus Ferech Date: Sat, 4 May 2019 17:42:27 +0200 Subject: [PATCH 1/4] Add `raise_for_status` to `Response` --- httpcore/models.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/httpcore/models.py b/httpcore/models.py index c7ed5ae2..97a63cc8 100644 --- a/httpcore/models.py +++ b/httpcore/models.py @@ -14,7 +14,13 @@ from .decoders import ( IdentityDecoder, MultiDecoder, ) -from .exceptions import InvalidURL, ResponseClosed, ResponseNotRead, StreamConsumed +from .exceptions import ( + HttpError, + InvalidURL, + ResponseClosed, + ResponseNotRead, + StreamConsumed, +) from .utils import ( get_reason_phrase, is_known_encoding, @@ -627,3 +633,22 @@ class Response: def __repr__(self) -> str: class_name = self.__class__.__name__ return f"<{class_name}(status_code={self.status_code})>" + + def raise_for_status(self) -> None: + """ + Raise the `HttpError` if one occurred. + """ + message = ( + "{0.status_code} {error_type}: {0.reason_phrase} for url: {0.url}\n" + "For more information check: https://httpstatuses.com/{0.status_code}" + ) + + if 400 <= self.status_code < 500: + message = message.format(self, error_type="Client Error") + elif 500 <= self.status_code < 600: + message = message.format(self, error_type="Server Error") + else: + message = "" + + if message: + raise HttpError(message) From 21970153864d541af243d7adba3eb4510cbdfd5f Mon Sep 17 00:00:00 2001 From: Matus Ferech Date: Sat, 4 May 2019 17:42:55 +0200 Subject: [PATCH 2/4] Add `HttpError` --- httpcore/exceptions.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/httpcore/exceptions.py b/httpcore/exceptions.py index 55d377e0..ad040132 100644 --- a/httpcore/exceptions.py +++ b/httpcore/exceptions.py @@ -34,6 +34,12 @@ class PoolTimeout(Timeout): # HTTP exceptions... +class HttpError(Exception): + """ + An Http error occurred. + """ + + class ProtocolError(Exception): """ Malformed HTTP. From 0629e55f98dd315bcc31ec894b523f424afe3125 Mon Sep 17 00:00:00 2001 From: Matus Ferech Date: Sat, 4 May 2019 17:44:06 +0200 Subject: [PATCH 3/4] Add `status_code` endpoint --- tests/conftest.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index 3ceaeec0..aab371cf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,8 @@ async def app(scope, receive, send): assert scope["type"] == "http" if scope["path"] == "/slow_response": await slow_response(scope, receive, send) + elif scope["path"].startswith("/status"): + await status_code(scope, receive, send) else: await hello_world(scope, receive, send) @@ -36,6 +38,18 @@ async def slow_response(scope, receive, send): await send({"type": "http.response.body", "body": b"Hello, world!"}) +async def status_code(scope, receive, send): + status_code = int(scope["path"].replace("/status/", "")) + await send( + { + "type": "http.response.start", + "status": status_code, + "headers": [[b"content-type", b"text/plain"]], + } + ) + await send({"type": "http.response.body", "body": b"Hello, world!"}) + + @pytest.fixture async def server(): config = Config(app=app, lifespan="off") From c628eb061ced1c213c9dca8046930ac2657c5589 Mon Sep 17 00:00:00 2001 From: Matus Ferech Date: Sat, 4 May 2019 17:44:35 +0200 Subject: [PATCH 4/4] Test `raise_for_status` --- tests/test_client.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_client.py b/tests/test_client.py index 43a33bef..1d33379c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -50,3 +50,18 @@ async def test_stream_request(server): "POST", "http://127.0.0.1:8000/", content=hello_world() ) assert response.status_code == 200 + + +@pytest.mark.asyncio +async def test_raise_for_status(server): + async with httpcore.Client() as client: + for status_code in (200, 400, 404, 500, 505): + response = await client.request( + "GET", f"http://127.0.0.1:8000/status/{status_code}" + ) + + if 400 <= status_code < 600: + with pytest.raises(httpcore.exceptions.HttpError): + response.raise_for_status() + else: + assert response.raise_for_status() is None