Colin-b / pytest_httpx

pytest fixture to mock HTTPX
https://colin-b.github.io/pytest_httpx/
MIT License
344 stars 32 forks source link

httpx does not read the response if an event_hook raises an exception #118

Closed warvariuc closed 11 months ago

warvariuc commented 11 months ago

This code is based on https://github.com/Colin-b/pytest_httpx#reply-with-custom-body :

def test_str_body(httpx_mock: HTTPXMock):
    httpx_mock.add_response(text="This is my UTF-8 content", status_code=httpx.codes.BAD_REQUEST)

    with httpx.Client(
        event_hooks={
            "response": [lambda response: response.raise_for_status()]
        }
    ) as client:
        try:
            client.get("https://test_url")
        except httpx.HTTPStatusError as exc:
            assert exc.response.text == "This is my UTF-8 content"

It fails with this error:

httpx.ResponseNotRead: Attempted to access streaming response content, without having called `read()`.
Colin-b commented 11 months ago

Hello @warvariuc ,

This is not related to pytest-httpx as this is httpx current behavior. Your attempt to access the content of the response before reading it is an invalid use case as stated in httpx documentation

If you need access to the response body inside an event hook, you'll need to call response.read().

And in your case, as you throw an exception in your event hook, you are still in the same context when you try to access the body (via response.text) in the except block.

You can try it out without mocking, on a url that does not exists, you will have the exact same issue:

import httpx

with httpx.Client(
        event_hooks={
            "response": [lambda response: response.raise_for_status()]
        }
) as client:
    try:
        client.get("https://github.com/thisdoesnotexists")
        print("np")
    except httpx.HTTPStatusError as exc:
        print(exc.response.text)

This will output the error you describe:

Traceback (most recent call last): line 9, in client.get("https://github.com/thisdoesnotexists") \lib\site-packages\httpx_client.py", line 1041, in get return self.request( \lib\site-packages\httpx_client.py", line 814, in request return self.send(request, auth=auth, follow_redirects=follow_redirects) \lib\site-packages\httpx_client.py", line 901, in send response = self._send_handling_auth( \lib\site-packages\httpx_client.py", line 929, in _send_handling_auth response = self._send_handling_redirects( \lib\site-packages\httpx_client.py", line 986, in _send_handling_redirects raise exc \lib\site-packages\httpx_client.py", line 969, in _send_handling_redirects hook(response) line 5, in "response": [lambda response: response.raise_for_status()] \lib\site-packages\httpx_models.py", line 749, in raise_for_status raise HTTPStatusError(message, request=request, response=self) httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://github.com/thisdoesnotexists' For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404

During handling of the above exception, another exception occurred:

Traceback (most recent call last): line 12, in print(exc.response.text) \lib\site-packages\httpx_models.py", line 574, in text content = self.content \lib\site-packages\httpx_models.py", line 568, in content raise ResponseNotRead() httpx.ResponseNotRead: Attempted to access streaming response content, without having called read().

warvariuc commented 11 months ago

Thanks, Colin, for looking into this. I found these: https://github.com/encode/httpx/discussions/1973 https://github.com/encode/httpx/discussions/2224