Skip to content

Commit a4a2c41

Browse files
echarrodclaude
andcommitted
client: Raise error for non-JSON-serializable parameters
Fixes issue #69 by providing clear error messages when parameters cannot be serialized to JSON, instead of silently suppressing the error. Changes: - Catch only TypeError instead of all Exceptions when serializing parameters - Raise TypeError with descriptive message for non-serializable types - Add test cases for Decimal and custom objects - Verify None requests still work correctly 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 1156d88 commit a4a2c41

File tree

2 files changed

+33
-3
lines changed

2 files changed

+33
-3
lines changed

luno_python/base_client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,13 @@ def do(self, method, path, req=None, auth=False):
7070
:type req: object
7171
:type auth: bool
7272
"""
73-
try:
74-
params = json.loads(json.dumps(req))
75-
except Exception:
73+
if req is None:
7674
params = None
75+
else:
76+
try:
77+
params = json.loads(json.dumps(req))
78+
except TypeError as e:
79+
raise TypeError('luno: request parameters must be JSON-serializable: %s' % str(e))
7780
headers = {'User-Agent': self.make_user_agent()}
7881
args = dict(timeout=self.timeout, params=params, headers=headers)
7982
if auth:

tests/test_client.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
import requests
33
import requests_mock
4+
from decimal import Decimal
45

56
try:
67
from json.decoder import JSONDecodeError
@@ -219,3 +220,29 @@ def test_get_balances_with_malformed_response():
219220
# Test without account_id on malformed response
220221
result = c.get_balances()
221222
assert result == mock_response
223+
224+
225+
def test_client_do_with_non_serializable_params():
226+
"""Test that non-JSON-serializable parameters raise a clear error."""
227+
c = Client()
228+
c.set_base_url('mock://test/')
229+
230+
# Test with Decimal (not JSON-serializable)
231+
with pytest.raises(TypeError) as exc_info:
232+
c.do('GET', '/', req={'amount': Decimal('10.5')})
233+
assert 'JSON-serializable' in str(exc_info.value)
234+
235+
# Test with custom object (not JSON-serializable)
236+
class CustomObject:
237+
pass
238+
239+
with pytest.raises(TypeError) as exc_info:
240+
c.do('GET', '/', req={'obj': CustomObject()})
241+
assert 'JSON-serializable' in str(exc_info.value)
242+
243+
# Test with None request (should not raise)
244+
adapter = requests_mock.Adapter()
245+
c.session.mount('mock', adapter)
246+
adapter.register_uri('GET', 'mock://test/', json={'result': 'ok'})
247+
result = c.do('GET', '/', req=None)
248+
assert result['result'] == 'ok'

0 commit comments

Comments
 (0)