Skip to content

Commit 94de078

Browse files
committed
chore(sdk): Add status_code to ApiError when available from response
1 parent 60243d4 commit 94de078

File tree

2 files changed

+55
-6
lines changed

2 files changed

+55
-6
lines changed

src/thecompaniesapi/sdk.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,7 @@ def _make_request(
133133
return {'data': response.text, 'status': response.status_code}
134134

135135
except requests.exceptions.RequestException as e:
136-
# Handle request errors
137-
raise ApiError(f"Request failed: {str(e)}") from e
136+
raise ApiError.from_request_exception(e, f"Request failed: {str(e)}") from e
138137

139138
def get(
140139
self,
@@ -186,8 +185,27 @@ def delete(
186185

187186

188187
class ApiError(Exception):
189-
"""Custom exception for API errors."""
190-
pass
188+
"""
189+
Custom exception for API errors.
190+
"""
191+
def __init__(self, message: str, status_code: int | None = None) -> None:
192+
super().__init__(message)
193+
self.status_code = status_code
194+
195+
@classmethod
196+
def from_request_exception(cls, e: ApiError, message: str) -> "ApiError":
197+
status_code = None
198+
cause = e.__cause__
199+
if isinstance(cause, requests.HTTPError) and cause.response is not None:
200+
status_code = cause.response.status_code
201+
elif isinstance(cause, requests.exceptions.RetryError):
202+
# Retry exhausted - try to extract status from message
203+
# e.g., "too many 429 error responses"
204+
import re
205+
206+
if match := re.search(r"(\d{3})", str(cause)):
207+
status_code = int(match.group(1))
208+
return cls(message, status_code)
191209

192210

193211
class Client:

tests/test_client.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22
import pytest
33
import responses
4+
import requests
5+
46
from unittest.mock import Mock, patch
57

68
from src.thecompaniesapi import Client, HttpClient, ApiError
@@ -154,8 +156,26 @@ def test_request_error_handling(self):
154156

155157
client = HttpClient(api_token="test-token")
156158

157-
with pytest.raises(ApiError, match="Request failed"):
159+
with pytest.raises(ApiError, match="Request failed") as exc_info:
160+
client.get("/v2/error")
161+
assert exc_info.value.status_code == 404
162+
163+
@responses.activate
164+
def test_request_error_timeout_handling(self):
165+
"""Test error handling for HTTP errors."""
166+
responses.add(
167+
responses.GET,
168+
"https://api.thecompaniesapi.com/v2/error",
169+
body=requests.exceptions.Timeout() # Simulate a request failure
170+
)
171+
172+
client = HttpClient(api_token="test-token")
173+
174+
try:
158175
client.get("/v2/error")
176+
except Exception as e:
177+
assert e.status_code is None
178+
159179

160180
@responses.activate
161181
def test_non_json_response(self):
@@ -255,4 +275,15 @@ def test_api_error_creation(self):
255275
assert str(error) == "Test error message"
256276

257277
with pytest.raises(ApiError, match="Test error message"):
258-
raise error
278+
raise error
279+
280+
281+
def test_api_error_status_code(self):
282+
"""Test ApiError with status code attribute."""
283+
error = ApiError("Error with status", status_code=404)
284+
assert str(error) == "Error with status"
285+
assert error.status_code == 404
286+
287+
with pytest.raises(ApiError) as exc_info:
288+
raise error
289+
assert exc_info.value.status_code == 404

0 commit comments

Comments
 (0)