From b6339f8796c115bde09fdb05861d7dba6a25ec55 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Sun, 4 Oct 2026 13:22:17 +0530 Subject: [PATCH] fix: preserve HTTP error subclasses when pickling --- python_http_client/exceptions.py | 2 +- tests/test_unit.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/python_http_client/exceptions.py b/python_http_client/exceptions.py index 2a8c179..1c72396 100644 --- a/python_http_client/exceptions.py +++ b/python_http_client/exceptions.py @@ -18,7 +18,7 @@ def __init__(self, *args): def __reduce__(self): return ( - HTTPError, + type(self), (self.status_code, self.reason, self.body, self.headers) ) diff --git a/tests/test_unit.py b/tests/test_unit.py index be85543..d411aea 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -9,6 +9,7 @@ ServiceUnavailableError, UnauthorizedError, UnsupportedMediaTypeError, + err_dict, ) try: @@ -209,6 +210,21 @@ def test_client_pickle_unpickle(self): "original client and unpickled client must have the same state" ) + def test_pickle_error_preserves_exception_type(self): + error_types = list(err_dict.items()) + [(418, SG_HTTPError)] + for status_code, error_type in error_types: + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + error = error_type( + status_code, 'REASON', b'{"error": "failed"}', + {'Retry-After': '30'}, + ) + restored = pickle.loads(pickle.dumps(error, protocol)) + self.assertIs(type(restored), error_type) + self.assertEqual(restored.status_code, error.status_code) + self.assertEqual(restored.reason, error.reason) + self.assertEqual(restored.body, error.body) + self.assertEqual(restored.headers, error.headers) + @mock.patch('python_http_client.client.urllib') def test_pickle_error(self, mock_lib): mock_opener = MockOpener()