Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion apiclient/request_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -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`."""
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions tests/test_request_strategies.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down