From 6b2454658d1753d5bfbe6d9bd0cffb48a26e8453 Mon Sep 17 00:00:00 2001 From: Jonas Lundberg Date: Mon, 15 Nov 2021 16:35:12 +0100 Subject: [PATCH] Support HTTPX 0.21.0 (#189) --- respx/mocks.py | 60 +++++++++++++++++++++++++-------------------- setup.py | 2 +- tests/test_mock.py | 21 ++++++++-------- tests/test_stats.py | 8 +++--- 4 files changed, 48 insertions(+), 43 deletions(-) diff --git a/respx/mocks.py b/respx/mocks.py index 2892ad5..6ea97ef 100644 --- a/respx/mocks.py +++ b/respx/mocks.py @@ -4,8 +4,8 @@ from typing import TYPE_CHECKING, ClassVar, Dict, List, Type from unittest import mock +import httpcore import httpx -from httpcore import AsyncIteratorByteStream, IteratorByteStream from .models import AllMockedAssertionError, PassThrough from .transports import TryTransport @@ -166,6 +166,10 @@ def _transport_for_url(self, *args, **kwargs): class AbstractRequestMocker(Mocker): @classmethod def mock(cls, spec): + if spec.__name__ not in cls.target_methods: + # Prevent mocking mock + return spec + argspec = inspect.getfullargspec(spec) def mock(self, *args, **kwargs): @@ -256,9 +260,9 @@ async def from_async_httpx_response(cls, httpx_response, target, **kwargs): class HTTPCoreMocker(AbstractRequestMocker): name = "httpcore" targets = [ - "httpcore._sync.connection.SyncHTTPConnection", - "httpcore._sync.connection_pool.SyncConnectionPool", - "httpcore._sync.http_proxy.SyncHTTPProxy", + "httpcore._sync.connection.HTTPConnection", + "httpcore._sync.connection_pool.ConnectionPool", + "httpcore._sync.http_proxy.HTTPProxy", "httpcore._async.connection.AsyncHTTPConnection", "httpcore._async.connection_pool.AsyncConnectionPool", "httpcore._async.http_proxy.AsyncHTTPProxy", @@ -268,59 +272,61 @@ class HTTPCoreMocker(AbstractRequestMocker): @classmethod def prepare_sync_request(cls, httpx_request, **kwargs): """ - Sync pre-read request body, and update transport request args. + Sync pre-read request body, and update transport request arg. """ httpx_request, kwargs = super().prepare_sync_request(httpx_request, **kwargs) - kwargs["stream"] = httpx_request.stream + kwargs["request"].stream = httpx_request.stream return httpx_request, kwargs @classmethod async def prepare_async_request(cls, httpx_request, **kwargs): """ - Async pre-read request body, and update transport request args. + Async pre-read request body, and update transport request arg. """ httpx_request, kwargs = await super().prepare_async_request( httpx_request, **kwargs ) - kwargs["stream"] = httpx_request.stream + kwargs["request"].stream = httpx_request.stream return httpx_request, kwargs @classmethod def to_httpx_request(cls, **kwargs): """ - Create a `HTTPX` request from transport request args. + Create a `HTTPX` request from transport request arg. """ + request = kwargs["request"] + raw_url = ( + request.url.scheme, + request.url.host, + request.url.port, + request.url.target, + ) return httpx.Request( - kwargs["method"], - kwargs["url"], - headers=kwargs.get("headers"), - stream=kwargs.get("stream"), - extensions=kwargs.get("extensions"), + request.method, + raw_url, + headers=request.headers, + stream=request.stream, + extensions=request.extensions, ) @classmethod def from_sync_httpx_response(cls, httpx_response, target, **kwargs): """ - Create a transport return tuple from `HTTPX` response. + Create a `httpcore` response from a `HTTPX` response. """ - return ( - httpx_response.status_code, - httpx_response.headers.raw, - IteratorByteStream(httpx_response.stream.__iter__()), - httpx_response.extensions, + return httpcore.Response( + status=httpx_response.status_code, + headers=httpx_response.headers.raw, + content=httpx_response.stream, + extensions=httpx_response.extensions, ) @classmethod async def from_async_httpx_response(cls, httpx_response, target, **kwargs): """ - Create a transport return tuple from `HTTPX` response. + Create a `httpcore` response from a `HTTPX` response. """ - return ( - httpx_response.status_code, - httpx_response.headers.raw, - AsyncIteratorByteStream(httpx_response.stream.__aiter__()), - httpx_response.extensions, - ) + return cls.from_sync_httpx_response(httpx_response, target, **kwargs) DEFAULT_MOCKER: str = HTTPCoreMocker.name diff --git a/setup.py b/setup.py index 35f6f2c..a1e41c9 100644 --- a/setup.py +++ b/setup.py @@ -40,5 +40,5 @@ include_package_data=True, zip_safe=False, python_requires=">=3.6", - install_requires=["httpx>=0.20.0"], + install_requires=["httpx>=0.21.0"], ) diff --git a/tests/test_mock.py b/tests/test_mock.py index 30f6b4a..6583968 100644 --- a/tests/test_mock.py +++ b/tests/test_mock.py @@ -456,7 +456,7 @@ async def test_assert_all_mocked(client, assert_all_mocked, raises): def test_add_remove_targets(): from respx.mocks import HTTPCoreMocker - target = "httpcore._sync.connection.SyncHTTPConnection" + target = "httpcore._sync.connection.HTTPConnection" assert HTTPCoreMocker.targets.count(target) == 1 HTTPCoreMocker.add_targets(target) assert HTTPCoreMocker.targets.count(target) == 1 @@ -709,20 +709,19 @@ async def test_httpcore_request(url, port): async with MockRouter(using="httpcore") as router: router.get(url) % dict(text="foobar") - with httpcore.SyncConnectionPool() as http: - (status_code, headers, stream, ext) = http.handle_request( - method=b"GET", url=(b"https", b"foo.bar", port, b"/") - ) + request = httpcore.Request( + b"GET", + httpcore.URL(scheme=b"https", host=b"foo.bar", port=port, target=b"/"), + ) - body = b"".join([chunk for chunk in stream]) + with httpcore.ConnectionPool() as http: + response = http.handle_request(request) + body = response.read() assert body == b"foobar" async with httpcore.AsyncConnectionPool() as http: - (status_code, headers, stream, ext) = await http.handle_async_request( - method=b"GET", url=(b"https", b"foo.bar", port, b"/") - ) - - body = b"".join([chunk async for chunk in stream]) + response = await http.handle_async_request(request) + body = await response.aread() assert body == b"foobar" diff --git a/tests/test_stats.py b/tests/test_stats.py index 045357f..4089d34 100644 --- a/tests/test_stats.py +++ b/tests/test_stats.py @@ -92,9 +92,9 @@ async def backend_test(backend): def test_asyncio(): import asyncio - from httpcore._backends.asyncio import AsyncioBackend + from httpcore.backends.asyncio import AsyncIOBackend - backend = AsyncioBackend() + backend = AsyncIOBackend() # TODO: Why instantiate a backend? loop = asyncio.new_event_loop() try: loop.run_until_complete(backend_test(backend)) @@ -104,7 +104,7 @@ def test_asyncio(): def test_trio(): # pragma: nocover import trio - from httpcore._backends.trio import TrioBackend + from httpcore.backends.trio import TrioBackend - backend = TrioBackend() + backend = TrioBackend() # TODO: Why instantiate a backend? trio.run(backend_test, backend)