From 89c04e775f48efc573d37a88eb7fc4a6c3251718 Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 7 Jun 2026 19:19:15 +0100 Subject: [PATCH] feat: add overridable _handle_request_error hook (#92) --- apiclient/request_strategies.py | 10 +++++++++- tests/test_request_strategies.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/apiclient/request_strategies.py b/apiclient/request_strategies.py index 98dfb15..d149fa2 100644 --- a/apiclient/request_strategies.py +++ b/apiclient/request_strategies.py @@ -37,6 +37,14 @@ def patch(self, *args, **kwargs): # pragma: no cover def delete(self, *args, **kwargs): # pragma: no cover raise NotImplementedError + def _handle_request_error(self, error: Exception, endpoint: str) -> None: + """Translate a transport-level error into a client exception. + + Override to add custom handling (e.g. retries on rate limits) without + reimplementing the request flow. + """ + raise UnexpectedError(f"Error when contacting '{endpoint}'") from error + class RequestStrategy(BaseRequestStrategy): """Requests strategy that uses the `requests` lib with a `requests.session`.""" @@ -99,7 +107,7 @@ def _make_request( ) ) except requests.RequestException as error: - raise UnexpectedError(f"Error when contacting '{endpoint}'") from error + self._handle_request_error(error, endpoint) else: self._check_response(response) return self._decode_response_data(response) diff --git a/tests/test_request_strategies.py b/tests/test_request_strategies.py index 1deedbf..f59a332 100644 --- a/tests/test_request_strategies.py +++ b/tests/test_request_strategies.py @@ -151,6 +151,23 @@ def test_request_strategy_does_not_wrap_non_request_errors(mock_requests, mock_c strategy.get("mock://testserver.com") +def test_request_strategy_uses_overridden_request_error_handler(mock_requests, mock_client): + mock_requests.get("mock://testserver.com", exc=requests.exceptions.ConnectTimeout) + + class CustomError(Exception): + pass + + class CustomStrategy(RequestStrategy): + def _handle_request_error(self, error, endpoint): + raise CustomError(endpoint) from error + + strategy = CustomStrategy() + strategy.set_client(mock_client.client) + + with pytest.raises(CustomError): + strategy.get("mock://testserver.com") + + def test_request_strategy_delete_method_delegates_to_parent_handlers(mock_requests, mock_client): mock_requests.delete("mock://testserver.com", json={"active": True}, status_code=200)