Skip to content

Commit 1f89b9a

Browse files
Improve error HTTP handlin
If HTTP error occurred thorough a response (e.g., non-2xx status code) then save the response test in the exception. Should allow for easier debugging, as the API sends errors as a JSON.
1 parent 29e63da commit 1f89b9a

4 files changed

Lines changed: 37 additions & 7 deletions

File tree

docs/usage.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,14 @@ from simplejustwatchapi import search, JustWatchHttpError
290290
try:
291291
results = search("The Matrix", count="five")
292292
except JustWatchHttpError as e:
293-
print(str(e))
293+
print(str(e), e.response)
294294
```
295295

296+
Aside from exception message, the error can also contains optional field `response` with
297+
full response text as string. This will only contain data if error is related to the
298+
**response** message, otherwise it's `None`. Usually it is a JSON (in string form) with
299+
API errors, if these specific errors are also causing non-`2xx` status codes.
300+
296301
!!! note "Numeric strings instead of `int`"
297302
Since requests are send as a JSON you can use strings for `int` arguments, as long
298303
as they are numeric strings, like `5`, instead of `five`:

src/simplejustwatchapi/exceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,15 @@ class JustWatchHttpError(JustWatchError):
4040
4141
This is a general exception for any HTTP-related errors, such as non-`2xx` status
4242
codes, network errors, timeouts, etc.
43+
44+
Attributes:
45+
msg (str): Error message describing the HTTP error.
46+
response (str | None): Optional text of the HTTP response, if available.
47+
Usucally contains JSON with error responses from the API.
48+
4349
"""
50+
51+
def __init__(self, msg: str, response: str | None = None) -> None:
52+
"""Init JustWatchHttpError with error message and optional response text."""
53+
super().__init__(msg)
54+
self.response = response

src/simplejustwatchapi/justwatch.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
country code. |
4848
"""
4949

50-
from httpx import HTTPError, post
50+
from httpx import HTTPError, HTTPStatusError, post
5151

5252
from simplejustwatchapi.exceptions import JustWatchHttpError
5353
from simplejustwatchapi.query import (
@@ -538,6 +538,8 @@ def _post_to_jw_graphql_api(request_json: dict) -> dict:
538538
try:
539539
response = post(_GRAPHQL_API_URL, json=request_json)
540540
response.raise_for_status()
541+
return response.json()
542+
except HTTPStatusError as e:
543+
raise JustWatchHttpError(str(e), e.response.text) from e
541544
except HTTPError as e:
542545
raise JustWatchHttpError(str(e)) from e
543-
return response.json()

test/simplejustwatchapi/test_justwatch.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
DUMMY_RESPONSE = {"dummy": "response"}
2828
ENTRIES = [MagicMock(), MagicMock(), None]
2929

30+
REQUEST_ERROR_MESSAGE = "HTTP request error"
31+
RESPONSE_ERROR_STATUS_CODE = 420
32+
RESPONSE_ERROR_MESSAGE = "HTTP response error"
33+
3034

3135
@fixture
3236
def post_mock_success(mocker):
@@ -39,7 +43,7 @@ def post_mock_success(mocker):
3943
@fixture
4044
def post_mock_request_error(mocker):
4145
post_mock = mocker.patch("simplejustwatchapi.justwatch.post")
42-
post_mock.side_effect = RequestError("HTTP request error")
46+
post_mock.side_effect = RequestError(REQUEST_ERROR_MESSAGE)
4347
mock_request = Request(method="POST", url=JUSTWATCH_GRAPHQL_URL)
4448
post_mock.return_value = Response(status_code=200, request=mock_request)
4549
# Technically setting the return value is not necessary, since the side effect will
@@ -52,7 +56,11 @@ def post_mock_request_error(mocker):
5256
def post_mock_status_error(mocker):
5357
post_mock = mocker.patch("simplejustwatchapi.justwatch.post")
5458
mock_request = Request(method="POST", url=JUSTWATCH_GRAPHQL_URL)
55-
post_mock.return_value = Response(status_code=420, request=mock_request)
59+
post_mock.return_value = Response(
60+
status_code=RESPONSE_ERROR_STATUS_CODE,
61+
request=mock_request,
62+
text=RESPONSE_ERROR_MESSAGE,
63+
)
5664
return post_mock
5765

5866

@@ -158,8 +166,10 @@ def test_providers(requests_mock, parser_mock, post_mock_success):
158166
)
159167
def test_http_request_error(prepare_name, function, inputs, post_mock_request_error):
160168
full_mock_name = f"simplejustwatchapi.justwatch.{prepare_name}"
161-
with patch(full_mock_name), raises(JustWatchHttpError):
169+
with patch(full_mock_name), raises(JustWatchHttpError) as e:
162170
function(*inputs)
171+
assert str(e.value) == REQUEST_ERROR_MESSAGE
172+
assert e.value.response is None
163173

164174

165175
@mark.parametrize(
@@ -176,5 +186,7 @@ def test_http_request_error(prepare_name, function, inputs, post_mock_request_er
176186
)
177187
def test_http_status_error(prepare_name, function, inputs, post_mock_status_error):
178188
full_mock_name = f"simplejustwatchapi.justwatch.{prepare_name}"
179-
with patch(full_mock_name), raises(JustWatchHttpError):
189+
with patch(full_mock_name), raises(JustWatchHttpError) as e:
180190
function(*inputs)
191+
assert str(RESPONSE_ERROR_STATUS_CODE) in str(e.value)
192+
assert e.value.response == RESPONSE_ERROR_MESSAGE

0 commit comments

Comments
 (0)