Skip to content

Commit be718bb

Browse files
tdivisemlove
authored andcommitted
Parsing json response funcion is now customizable (still defaults to … (#1)
* Parsing json response funcion is now customizable (still defaults to `json.loads`). This allows to implement JSON-RPC class hinting or any other conversions. * Tests import cleanup * Don't override aiohttp's default json loads In the case where our users aren't specifying a manual json.loads method, we'd rather omit that argument, so that aiohttp can use its logic to determine the default json.loads method. * Only allow loads as a keyword argument We're not going to commit to loads being in a particular position in the argument order on our API. * Python 3.5 compatible assertion
1 parent 7eb278d commit be718bb

File tree

2 files changed

+36
-12
lines changed

2 files changed

+36
-12
lines changed

jsonrpc_async/jsonrpc.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
class Server(jsonrpc_base.Server):
1010
"""A connection to a HTTP JSON-RPC server, backed by aiohttp"""
1111

12-
def __init__(self, url, session=None, **post_kwargs):
12+
def __init__(self, url, session=None, *, loads=None, **post_kwargs):
1313
super().__init__()
1414
object.__setattr__(self, 'session', session or aiohttp.ClientSession())
1515
post_kwargs['headers'] = post_kwargs.get('headers', {})
@@ -19,6 +19,10 @@ def __init__(self, url, session=None, **post_kwargs):
1919
'Accept', 'application/json-rpc')
2020
self._request = functools.partial(self.session.post, url, **post_kwargs)
2121

22+
self._json_args = {}
23+
if loads is not None:
24+
self._json_args['loads'] = loads
25+
2226
@asyncio.coroutine
2327
def send_message(self, message):
2428
"""Send the HTTP message to the server and return the message response.
@@ -38,7 +42,7 @@ def send_message(self, message):
3842
return None
3943

4044
try:
41-
response_data = yield from response.json()
45+
response_data = yield from response.json(**self._json_args)
4246
except ValueError as value_error:
4347
raise TransportError('Cannot deserialize response body', message, value_error)
4448

tests.py

+30-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import asyncio
2+
from unittest import mock
23
import unittest
34
import random
45
import json
5-
import inspect
66
import os
77

88
import aiohttp
@@ -15,11 +15,6 @@
1515
import jsonrpc_base
1616
from jsonrpc_async import Server, ProtocolError, TransportError
1717

18-
try:
19-
# python 3.3
20-
from unittest.mock import Mock
21-
except ImportError:
22-
from mock import Mock
2318

2419
class JsonTestClient(aiohttp.test_utils.TestClient):
2520
def __init__(self, app, **kwargs):
@@ -31,6 +26,7 @@ def request(self, method, path, *args, **kwargs):
3126
self.request_callback(method, path, *args, **kwargs)
3227
return super().request(method, path, *args, **kwargs)
3328

29+
3430
class TestCase(unittest.TestCase):
3531
def assertSameJSON(self, json1, json2):
3632
"""Tells whether two json strings, once decoded, are the same dictionary"""
@@ -40,8 +36,7 @@ def assertRaisesRegex(self, *args, **kwargs):
4036
return super(TestCase, self).assertRaisesRegex(*args, **kwargs)
4137

4238

43-
class TestJSONRPCClient(TestCase):
44-
39+
class TestJSONRPCClientBase(TestCase):
4540
def setUp(self):
4641
self.loop = setup_test_loop()
4742
self.app = self.get_app()
@@ -53,8 +48,11 @@ def create_client(app, loop):
5348
self.client = self.loop.run_until_complete(
5449
create_client(self.app, self.loop))
5550
self.loop.run_until_complete(self.client.start_server())
56-
random.randint = Mock(return_value=1)
57-
self.server = Server('/xmlrpc', session=self.client, timeout=0.2)
51+
random.randint = mock.Mock(return_value=1)
52+
self.server = self.get_server()
53+
54+
def get_server(self):
55+
return Server('/xmlrpc', session=self.client, timeout=0.2)
5856

5957
def tearDown(self):
6058
self.loop.run_until_complete(self.client.close())
@@ -68,6 +66,8 @@ def response_func(request):
6866
app.router.add_post('/xmlrpc', response_func)
6967
return app
7068

69+
70+
class TestJSONRPCClient(TestJSONRPCClientBase):
7171
def test_pep8_conformance(self):
7272
"""Test that we conform to PEP8."""
7373

@@ -249,5 +249,25 @@ def handler(request):
249249
self.assertIsNone((yield from self.server.subtract(42, 23, _notification=True)))
250250

251251

252+
class TestJSONRPCClientCustomLoads(TestJSONRPCClientBase):
253+
def get_server(self):
254+
self.loads_mock = mock.Mock(wraps=json.loads)
255+
return Server('/xmlrpc', session=self.client, loads=self.loads_mock, timeout=0.2)
256+
257+
@unittest_run_loop
258+
@asyncio.coroutine
259+
def test_custom_loads(self):
260+
# rpc call with positional parameters:
261+
@asyncio.coroutine
262+
def handler1(request):
263+
request_message = yield from request.json()
264+
self.assertEqual(request_message["params"], [42, 23])
265+
return aiohttp.web.Response(text='{"jsonrpc": "2.0", "result": 19, "id": 1}', content_type='application/json')
266+
267+
self.handler = handler1
268+
self.assertEqual((yield from self.server.subtract(42, 23)), 19)
269+
self.assertEqual(self.loads_mock.call_count, 1)
270+
271+
252272
if __name__ == '__main__':
253273
unittest.main()

0 commit comments

Comments
 (0)