From 0eef52cefc43068a47c3e3cd04e3dd1ee485426d Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Wed, 21 Feb 2024 22:40:07 -0800 Subject: [PATCH] Make HttpStatusError and RequestError pickleable If an error has keyword-only arguments, it needs this extra `__reduce__` method to ensure that unpickling doesn't raise `TypeError: missing (n) required keyword-only argument(s)` --- httpx/_exceptions.py | 7 +++++++ tests/test_pickle.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/test_pickle.py diff --git a/httpx/_exceptions.py b/httpx/_exceptions.py index 11424621c0..f4efa10137 100644 --- a/httpx/_exceptions.py +++ b/httpx/_exceptions.py @@ -71,6 +71,13 @@ def request(self) -> Request: def request(self, request: Request) -> None: self._request = request + def __reduce__( + self, + ) -> typing.Tuple[ + typing.Callable[..., Exception], typing.Tuple[typing.Any], dict[str, typing.Any] + ]: + return (Exception.__new__, (type(self),) + self.args, self.__dict__) + class RequestError(HTTPError): """ diff --git a/tests/test_pickle.py b/tests/test_pickle.py new file mode 100644 index 0000000000..7a8368701f --- /dev/null +++ b/tests/test_pickle.py @@ -0,0 +1,16 @@ +import pickle + +from httpx import HTTPStatusError, RequestError + + +def test_pickle(): + req_err = RequestError("hi!", request=1) + req_err_clone = pickle.loads(pickle.dumps(req_err)) + assert req_err.args == req_err_clone.args + assert req_err.request == req_err_clone.request + + status_err = HTTPStatusError("hi", request="request", response="response") + status_err_clone = pickle.loads(pickle.dumps(status_err)) + assert status_err.args == status_err_clone.args + assert status_err.request == status_err_clone.request + assert status_err.response == status_err_clone.response