diff --git a/docs/api.md b/docs/api.md index 2dff987f74..297465db18 100644 --- a/docs/api.md +++ b/docs/api.md @@ -62,12 +62,12 @@ the total elapsed seconds. * `def .raise_for_status()` - **None** * `def .json()` - **Any** -* `def .read()` - **bytes** +* `def .aread()` - **bytes** * `def .stream_raw()` - **async bytes iterator** * `def .stream_bytes()` - **async bytes iterator** * `def .stream_text()` - **async text iterator** * `def .stream_lines()` - **async text iterator** -* `def .close()` - **None** +* `def .aclose()` - **None** * `def .next()` - **Response** ## `Request` diff --git a/docs/compatibility.md b/docs/compatibility.md index 506340c3ab..6f67660ba5 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -32,7 +32,7 @@ Within a `stream()` block request data is made available with: * `.aiter_text()` - Instead of `response.iter_content(decode_unicode=True)` * `.aiter_lines()` - Instead of `response.iter_lines()` * `.aiter_raw()` - Use this instead of `response.raw` -* `.read()` - Read the entire response body, making `request.text` and `response.content` available. +* `.aread()` - Read the entire response body, making `request.text` and `response.content` available. ## SSL configuration diff --git a/docs/quickstart.md b/docs/quickstart.md index 8ef8e959ee..faa34959f0 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -339,7 +339,7 @@ If you're using streaming responses in any of these ways then the `response.cont ``` >>> async with httpx.stream("GET", "https://www.example.com") as r: ... if r.headers['Content-Length'] < TOO_LONG: -... await r.read() +... await r.aread() ... print(r.text) ``` diff --git a/httpx/client.py b/httpx/client.py index be9c881a36..72a552a32e 100644 --- a/httpx/client.py +++ b/httpx/client.py @@ -411,9 +411,9 @@ async def send( if not stream: try: - await response.read() + await response.aread() finally: - await response.close() + await response.aclose() return response @@ -470,7 +470,7 @@ async def send_handling_redirects( if not response.is_redirect: return response - await response.read() + await response.aread() request = self.build_redirect_request(request, response) history = history + [response] @@ -596,11 +596,11 @@ async def send_handling_auth( except StopIteration: return response except BaseException as exc: - await response.close() + await response.aclose() raise exc from None else: request = next_request - await response.close() + await response.aclose() async def send_single_request( self, @@ -980,6 +980,6 @@ async def __aexit__( exc_value: BaseException = None, traceback: TracebackType = None, ) -> None: - await self.response.close() + await self.response.aclose() if self.close_client: await self.client.close() diff --git a/httpx/dispatch/proxy_http.py b/httpx/dispatch/proxy_http.py index e2a5248e1d..204fdcdf6a 100644 --- a/httpx/dispatch/proxy_http.py +++ b/httpx/dispatch/proxy_http.py @@ -159,7 +159,7 @@ async def request_tunnel_proxy_connection(self, origin: Origin) -> HTTPConnectio f"response={proxy_response!r}" ) if not (200 <= proxy_response.status_code <= 299): - await proxy_response.read() + await proxy_response.aread() raise ProxyError( f"Non-2XX response received from HTTP proxy " f"({proxy_response.status_code})", diff --git a/httpx/models.py b/httpx/models.py index 0a44d3258e..4aa0c7c337 100644 --- a/httpx/models.py +++ b/httpx/models.py @@ -850,7 +850,15 @@ def links(self) -> typing.Dict[typing.Optional[str], typing.Dict[str, str]]: def __repr__(self) -> str: return f"" - async def read(self) -> bytes: + @property + def read(self) -> typing.Callable: + warnings.warn( + "Response.read() is due to be deprecated. Use Response.aread() instead.", + category=DeprecationWarning, + ) + return self.aread + + async def aread(self) -> bytes: """ Read and return the response content. """ @@ -862,7 +870,8 @@ async def read(self) -> bytes: def stream(self): # type: ignore warnings.warn( "Response.stream() is due to be deprecated. " - "Use Response.aiter_bytes() instead." + "Use Response.aiter_bytes() instead.", + category=DeprecationWarning, ) return self.aiter_bytes @@ -870,7 +879,8 @@ def stream(self): # type: ignore def raw(self): # type: ignore warnings.warn( "Response.raw() is due to be deprecated. " - "Use Response.aiter_raw() instead." + "Use Response.aiter_raw() instead.", + category=DeprecationWarning, ) return self.aiter_raw @@ -920,7 +930,7 @@ async def aiter_raw(self) -> typing.AsyncIterator[bytes]: self.is_stream_consumed = True async for part in self._raw_stream: yield part - await self.close() + await self.aclose() async def next(self) -> "Response": """ @@ -931,7 +941,16 @@ async def next(self) -> "Response": assert self.call_next is not None return await self.call_next() - async def close(self) -> None: + @property + def close(self) -> typing.Callable: + warnings.warn( + "Response.close() is due to be deprecated. " + "Use Response.aclose() instead.", + category=DeprecationWarning, + ) + return self.aclose + + async def aclose(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. diff --git a/tests/client/test_async_client.py b/tests/client/test_async_client.py index 8180b0dbf7..20216b6f5d 100644 --- a/tests/client/test_async_client.py +++ b/tests/client/test_async_client.py @@ -53,7 +53,7 @@ async def test_post_json(server): async def test_stream_response(server): async with httpx.Client() as client: async with client.stream("GET", server.url) as response: - body = await response.read() + body = await response.aread() assert response.status_code == 200 assert body == b"Hello, world!" diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 851896146e..05abedf1f5 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -59,7 +59,7 @@ async def test_post_json(server): async def test_stream_response(server): async with httpx.Client() as client: async with client.stream("GET", server.url) as response: - content = await response.read() + content = await response.aread() assert response.status_code == 200 assert content == b"Hello, world!" diff --git a/tests/dispatch/test_connection_pools.py b/tests/dispatch/test_connection_pools.py index d108bbd503..5e1fce3e07 100644 --- a/tests/dispatch/test_connection_pools.py +++ b/tests/dispatch/test_connection_pools.py @@ -11,12 +11,12 @@ async def test_keepalive_connections(server): """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -28,7 +28,7 @@ async def test_keepalive_timeout(server): """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -42,7 +42,7 @@ async def test_keepalive_timeout(server): http.KEEP_ALIVE_EXPIRY = 0.0 response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -60,12 +60,12 @@ async def test_differing_connection_keys(server): """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 response = await http.request("GET", "http://localhost:8000/") - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 2 @@ -79,12 +79,12 @@ async def test_soft_limit(server): async with ConnectionPool(pool_limits=pool_limits) as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 response = await http.request("GET", "http://localhost:8000/") - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -99,7 +99,7 @@ async def test_streaming_response_holds_connection(server): assert len(http.active_connections) == 1 assert len(http.keepalive_connections) == 0 - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -119,11 +119,11 @@ async def test_multiple_concurrent_connections(server): assert len(http.active_connections) == 2 assert len(http.keepalive_connections) == 0 - await response_b.read() + await response_b.aread() assert len(http.active_connections) == 1 assert len(http.keepalive_connections) == 1 - await response_a.read() + await response_a.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 2 @@ -136,7 +136,7 @@ async def test_close_connections(server): headers = [(b"connection", b"close")] async with ConnectionPool() as http: response = await http.request("GET", server.url, headers=headers) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 0 @@ -148,8 +148,8 @@ async def test_standard_response_close(server): """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() - await response.close() + await response.aread() + await response.aclose() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -161,7 +161,7 @@ async def test_premature_response_close(server): """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.close() + await response.aclose() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 0 @@ -174,13 +174,13 @@ async def test_keepalive_connection_closed_by_server_is_reestablished(server, re """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() # Shutdown the server to close the keep-alive connection await restart(server) response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -195,13 +195,13 @@ async def test_keepalive_http2_connection_closed_by_server_is_reestablished( """ async with ConnectionPool() as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() # Shutdown the server to close the keep-alive connection await restart(server) response = await http.request("GET", server.url) - await response.read() + await response.aread() assert len(http.active_connections) == 0 assert len(http.keepalive_connections) == 1 @@ -214,7 +214,7 @@ async def test_connection_closed_free_semaphore_on_acquire(server, restart): """ async with ConnectionPool(pool_limits=httpx.PoolLimits(hard_limit=1)) as http: response = await http.request("GET", server.url) - await response.read() + await response.aread() # Close the connection so we're forced to recycle it await restart(server) diff --git a/tests/dispatch/test_connections.py b/tests/dispatch/test_connections.py index 2f8b0a9c50..319bd42833 100644 --- a/tests/dispatch/test_connections.py +++ b/tests/dispatch/test_connections.py @@ -8,7 +8,7 @@ async def test_get(server): async with HTTPConnection(origin=server.url) as conn: response = await conn.request("GET", server.url) - await response.read() + await response.aread() assert response.status_code == 200 assert response.content == b"Hello, world!" @@ -27,7 +27,7 @@ async def test_premature_close(server): response = await conn.request( "GET", server.url.copy_with(path="/premature_close") ) - await response.read() + await response.aread() @pytest.mark.usefixtures("async_environment") @@ -37,7 +37,7 @@ async def test_https_get_with_ssl_defaults(https_server, ca_cert_pem_file): """ async with HTTPConnection(origin=https_server.url, verify=ca_cert_pem_file) as conn: response = await conn.request("GET", https_server.url) - await response.read() + await response.aread() assert response.status_code == 200 assert response.content == b"Hello, world!" @@ -49,6 +49,6 @@ async def test_https_get_with_sll_overrides(https_server, ca_cert_pem_file): """ async with HTTPConnection(origin=https_server.url) as conn: response = await conn.request("GET", https_server.url, verify=ca_cert_pem_file) - await response.read() + await response.aread() assert response.status_code == 200 assert response.content == b"Hello, world!" diff --git a/tests/dispatch/test_proxy_http.py b/tests/dispatch/test_proxy_http.py index 428b86718e..5acb6579d0 100644 --- a/tests/dispatch/test_proxy_http.py +++ b/tests/dispatch/test_proxy_http.py @@ -119,7 +119,7 @@ async def test_proxy_tunnel_start_tls(): assert resp.request.url == "https://example.com" assert resp.request.headers["Host"] == "example.com" - await resp.read() + await resp.aread() # Make another request to see that the tunnel is re-used. resp = await proxy.request("GET", "https://example.com/target") @@ -131,7 +131,7 @@ async def test_proxy_tunnel_start_tls(): assert resp.request.url == "https://example.com/target" assert resp.request.headers["Host"] == "example.com" - await resp.read() + await resp.aread() recv = raw_io.received_data assert len(recv) == 5 diff --git a/tests/models/test_responses.py b/tests/models/test_responses.py index 3133e03dd4..4726028f7e 100644 --- a/tests/models/test_responses.py +++ b/tests/models/test_responses.py @@ -131,7 +131,7 @@ async def test_read_response(): assert response.encoding == "ascii" assert response.is_closed - content = await response.read() + content = await response.aread() assert content == b"Hello, world!" assert response.content == b"Hello, world!" @@ -162,7 +162,7 @@ async def test_bytes_interface(): async def test_text_interface(): response = httpx.Response(200, content=b"Hello, world!", request=REQUEST) - await response.read() + await response.aread() content = "" async for part in response.aiter_text(): @@ -174,7 +174,7 @@ async def test_text_interface(): async def test_lines_interface(): response = httpx.Response(200, content=b"Hello,\nworld!", request=REQUEST) - await response.read() + await response.aread() content = [] async for line in response.aiter_lines(): @@ -186,7 +186,7 @@ async def test_lines_interface(): async def test_stream_interface_after_read(): response = httpx.Response(200, content=b"Hello, world!", request=REQUEST) - await response.read() + await response.aread() content = b"" async for part in response.aiter_bytes(): @@ -202,7 +202,7 @@ async def test_streaming_response(): assert response.status_code == 200 assert not response.is_closed - content = await response.read() + content = await response.aread() assert content == b"Hello, world!" assert response.content == b"Hello, world!" @@ -219,7 +219,7 @@ async def test_cannot_read_after_stream_consumed(): content += part with pytest.raises(httpx.StreamConsumed): - await response.read() + await response.aread() @pytest.mark.asyncio @@ -227,10 +227,10 @@ async def test_cannot_read_after_response_closed(): stream = AsyncIteratorStream(aiterator=async_streaming_body()) response = httpx.Response(200, stream=stream, request=REQUEST) - await response.close() + await response.aclose() with pytest.raises(httpx.ResponseClosed): - await response.read() + await response.aread() def test_unknown_status_code(): @@ -296,3 +296,22 @@ def test_json_without_specified_encoding_decode_error(): def test_link_headers(headers, expected): response = httpx.Response(200, content=None, headers=headers, request=REQUEST) assert response.links == expected + + +@pytest.mark.asyncio +async def test_response_deprecations() -> None: + response = httpx.Response(200, request=REQUEST) + + with pytest.deprecated_call(match="aread"): + await response.read() + + with pytest.deprecated_call(match="aiter_raw"): + async for _ in response.raw(): + pass + + with pytest.deprecated_call(match="aiter_bytes"): + async for _ in response.stream(): + pass + + with pytest.deprecated_call(match="aclose"): + await response.close() diff --git a/tests/test_api.py b/tests/test_api.py index 9624842b01..1c658e91a0 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -69,7 +69,7 @@ async def test_delete(server): @pytest.mark.asyncio async def test_stream(server): async with httpx.stream("GET", server.url) as response: - await response.read() + await response.aread() assert response.status_code == 200 assert response.reason_phrase == "OK" diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 307ab5b23e..5eb26d305e 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -100,7 +100,7 @@ async def compress(body): stream = AsyncIteratorStream(aiterator=compress(body)) response = httpx.Response(200, headers=headers, stream=stream, request=REQUEST) assert not hasattr(response, "body") - assert await response.read() == body + assert await response.aread() == body @pytest.mark.parametrize("header_value", (b"deflate", b"gzip", b"br", b"identity")) @@ -157,7 +157,7 @@ async def iterator(): stream = AsyncIteratorStream(aiterator=iterator()) response = httpx.Response(200, stream=stream, request=REQUEST) - await response.read() + await response.aread() assert response.text == (b"".join(data)).decode(encoding) @@ -176,7 +176,7 @@ async def iterator(): request=REQUEST, ) - await response.read() + await response.aread() assert "".join(response.text) == "トラベル"