Map httpcore exceptions for Response read methods (#1190)

* Map httpcore exceptions for `Response.iter_*` methods

* Tweak

* Change wording

Co-authored-by: Florimond Manca <florimond.manca@gmail.com>

Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
Co-authored-by: Tom Christie <tom@tomchristie.com>
This commit is contained in:
Joe 2020-08-19 19:10:04 +08:00 committed by GitHub
parent d10b7cdc51
commit 03cd88c336
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 5 deletions

View File

@ -24,6 +24,7 @@ from ._decoders import (
TextDecoder,
)
from ._exceptions import (
HTTPCORE_EXC_MAP,
CookieConflict,
HTTPStatusError,
InvalidURL,
@ -32,6 +33,7 @@ from ._exceptions import (
ResponseClosed,
ResponseNotRead,
StreamConsumed,
map_exceptions,
)
from ._status_codes import codes
from ._types import (
@ -931,8 +933,9 @@ class Response:
raise ResponseClosed()
self.is_stream_consumed = True
for part in self._raw_stream:
yield part
with map_exceptions(HTTPCORE_EXC_MAP, request=self.request):
for part in self._raw_stream:
yield part
self.close()
def next(self) -> "Response":
@ -1007,8 +1010,9 @@ class Response:
raise ResponseClosed()
self.is_stream_consumed = True
async for part in self._raw_stream:
yield part
with map_exceptions(HTTPCORE_EXC_MAP, request=self.request):
async for part in self._raw_stream:
yield part
await self.aclose()
async def anext(self) -> "Response":

View File

@ -76,6 +76,8 @@ async def app(scope, receive, send):
assert scope["type"] == "http"
if scope["path"].startswith("/slow_response"):
await slow_response(scope, receive, send)
elif scope["path"].startswith("/slow_stream_response"):
await slow_stream_response(scope, receive, send)
elif scope["path"].startswith("/status"):
await status_code(scope, receive, send)
elif scope["path"].startswith("/echo_body"):
@ -111,6 +113,19 @@ async def slow_response(scope, receive, send):
await send({"type": "http.response.body", "body": b"Hello, world!"})
async def slow_stream_response(scope, receive, send):
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [[b"content-type", b"text/plain"]],
}
)
await sleep(1)
await send({"type": "http.response.body", "body": b"", "more_body": False})
async def status_code(scope, receive, send):
status_code = int(scope["path"].replace("/status/", ""))
await send(

View File

@ -24,7 +24,7 @@ def test_httpcore_all_exceptions_mapped() -> None:
pytest.fail(f"Unmapped httpcore exceptions: {not_mapped}")
def test_httpcore_exception_mapping() -> None:
def test_httpcore_exception_mapping(server) -> None:
"""
HTTPCore exception mapping works as expected.
"""
@ -33,6 +33,13 @@ def test_httpcore_exception_mapping() -> None:
with pytest.raises(httpx.ConnectError):
httpx.get("http://doesnotexist")
# Make sure streaming methods also map exceptions.
url = server.url.copy_with(path="/slow_stream_response")
timeout = httpx.Timeout(None, read=0.1)
with httpx.stream("GET", url, timeout=timeout) as stream:
with pytest.raises(httpx.ReadTimeout):
stream.read()
# Make sure it also works with custom transports.
class MockTransport(httpcore.SyncHTTPTransport):
def request(self, *args: Any, **kwargs: Any) -> Any: