Skip to content

Commit

Permalink
Support pickling HTTPStatusError
Browse files Browse the repository at this point in the history
  • Loading branch information
camillol committed May 22, 2024
1 parent 37593c1 commit f182a48
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 0 deletions.
17 changes: 17 additions & 0 deletions httpx/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from __future__ import annotations

import contextlib
import functools
import typing

if typing.TYPE_CHECKING:
Expand Down Expand Up @@ -267,6 +268,22 @@ def __init__(self, message: str, *, request: Request, response: Response) -> Non
self.request = request
self.response = response

def __reduce__(self):
# BaseException has a custom __reduce__ that can't handle subclasses with
# required keyword-only arguments.
return (
functools.partial(
self.__class__, request=self.request, response=self.response
),
self.args,
# In case user code adds attributes to the exception instance.
{
k: v
for k, v in self.__dict__.items()
if k not in ("_request", "response")
},
)


class InvalidURL(Exception):
"""
Expand Down
17 changes: 17 additions & 0 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import pickle
import typing

import httpcore
Expand Down Expand Up @@ -61,3 +62,19 @@ def test_request_attribute() -> None:
request = httpx.Request("GET", "https://www.example.com")
exc = httpx.ReadTimeout("Read operation timed out", request=request)
assert exc.request == request


def test_pickle_error(server):
with httpx.Client() as client:
response = client.request("GET", server.url.copy_with(path="/status/404"))
with pytest.raises(httpx.HTTPStatusError) as exc_info:
response.raise_for_status()
error = exc_info.value
assert isinstance(error, httpx.HTTPStatusError)
pickled_error = pickle.dumps(error)
unpickled_error = pickle.loads(pickled_error)
# Note that the unpickled error will not be equal to the original error
# because requests and responses are compared by identity.
assert str(unpickled_error) == str(error)
assert unpickled_error.request is not None
assert unpickled_error.response is not None

0 comments on commit f182a48

Please sign in to comment.