From 5afe732432e5216b0e60770ee46eb92f30595f66 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 4 Sep 2014 09:11:24 +0800 Subject: [PATCH 01/85] add custom exception, so version is updated --- changelog | 4 ++++ jsonrpc/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index 364b51b..14390e7 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,7 @@ +2014-09-04 kirill.pavlov@phystech.edu + + * Add custom exception functionality by @julianhille + 2014-05-25 kirill.pavlov@phystech.edu * Add python 2.6 support diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 99df112..e5532ad 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 6, 0) +__version = (1, 7, 0) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 78a729d7ec57f4a2cc7317ff442d57d350ba7ecf Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 16 Oct 2014 16:17:45 +0800 Subject: [PATCH 02/85] split manager handler --- jsonrpc/manager.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index 23c1235..06b601a 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -55,6 +55,20 @@ def handle(cls, request_str, dispatcher): except JSONRPCInvalidRequestException: return JSONRPC20Response(error=JSONRPCInvalidRequest()._data) + return cls.handle_request(request, dispatcher) + + @classmethod + def handle_request(cls, request, dispatcher): + """ Handle request data. + + At this moment request has correct jsonrpc format. + + :param dict request: data parsed from request_str. + :param jsonrpc.dispatcher.Dispatcher dispatcher: + + .. versionadded: 1.8.0 + + """ rs = request if isinstance(request, JSONRPC20BatchRequest) \ else [request] responses = [r for r in cls._get_responses(rs, dispatcher) @@ -77,8 +91,9 @@ def _get_responses(cls, requests, dispatcher): """ for request in requests: - response = lambda **kwargs: cls.RESPONSE_CLASS_MAP[ - request.JSONRPC_VERSION](_id=request._id, **kwargs) + def response(**kwargs): + return cls.RESPONSE_CLASS_MAP[request.JSONRPC_VERSION]( + _id=request._id, **kwargs) try: method = dispatcher[request.method] From f855f42c749e2c06c220b97d2db886086bf512ac Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 16 Oct 2014 16:19:36 +0800 Subject: [PATCH 03/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index e5532ad..e2e1d82 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 7, 0) +__version = (1, 8, 0) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From bfd0c291ac36cbd630199d50f52a4c6761a29f64 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 1 Dec 2014 13:07:17 +0800 Subject: [PATCH 04/85] init django backend --- jsonrpc/backend/__init__.py | 0 jsonrpc/backend/django.py | 68 +++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 jsonrpc/backend/__init__.py create mode 100644 jsonrpc/backend/django.py diff --git a/jsonrpc/backend/__init__.py b/jsonrpc/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py new file mode 100644 index 0000000..c68a77b --- /dev/null +++ b/jsonrpc/backend/django.py @@ -0,0 +1,68 @@ +from django.conf.urls import url +from django.http import HttpResponse +from django.views.decorators.http import require_http_methods +import json + +from ..exceptions import JSONRPCInvalidRequestException +from ..jsonrpc import JSONRPCRequest +from ..manager import JSONRPCResponseManager +from ..utils import DatetimeDecimalEncoder +from ..dispatcher import Dispatcher + + +class JSONRPCAPI(object): + def __init__(self, dispatcher=None): + self.dispatcher = dispatcher or Dispatcher() + + def urls(self): + urls = [ + url(r'^$', self.jsonrpc), + url(r'^/map$', self.jsonrpc_map), + ] + + return urls + + @require_http_methods(["POST"]) + def jsonrpc(self, request): + """ JSON-RPC 2.0 handler. + + NOTE: step1 -> move dirty views to json-rpc calls. + step2 -> move models communication to resources. + + """ + request_str = request.body + try: + jsonrpc_request = JSONRPCRequest.from_json(request_str) + except (TypeError, ValueError, JSONRPCInvalidRequestException): + response = JSONRPCResponseManager.handle( + request_str, self.dispatcher) + else: + jsonrpc_request.params = jsonrpc_request.params or {} + if isinstance(jsonrpc_request.params, dict): + jsonrpc_request.params.update(request=request) + response = JSONRPCResponseManager.handle_request( + jsonrpc_request, self.dispatcher) + + if response: + def serialize(s): + return json.dumps(s, cls=DatetimeDecimalEncoder) + + response.serialize = serialize + response = response.json + + return HttpResponse(response, content_type="application/json") + + def jsonrpc_map(self, request): + """ Map of json-rpc available calls. + + :return str: + + """ + result = "

JSON-RPC map

{}
".format("\n\n".join([ + "{}: {}".format(fname, f.__doc__) + for fname, f in self.dispatcher.items() + ])) + return HttpResponse(result) + + +api = JSONRPCAPI() From 18719a26241e47e7b766a78aa754c600d6c054e6 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 1 Dec 2014 13:09:05 +0800 Subject: [PATCH 05/85] remove unrelated comments --- jsonrpc/backend/django.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index c68a77b..7e8d39b 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -24,12 +24,7 @@ def urls(self): @require_http_methods(["POST"]) def jsonrpc(self, request): - """ JSON-RPC 2.0 handler. - - NOTE: step1 -> move dirty views to json-rpc calls. - step2 -> move models communication to resources. - - """ + """ JSON-RPC 2.0 handler.""" request_str = request.body try: jsonrpc_request = JSONRPCRequest.from_json(request_str) From 7f7ba5cf3819ef61d40f5c2dde944277e945d660 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sat, 6 Dec 2014 16:18:38 +0800 Subject: [PATCH 06/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index e2e1d82..2eb3313 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 8, 0) +__version = (1, 8, 1) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From b04c6e74220f433da05d536f8e0b381819f247ce Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 16:01:22 +0800 Subject: [PATCH 07/85] support python3.4 --- .travis.yml | 1 + tox.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e5c503c..a44ef91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ env: - TOXENV=py27 - TOXENV=py32 - TOXENV=py33 + - TOXENV=py34 - TOXENV=cov branches: diff --git a/tox.ini b/tox.ini index 56e8f6c..a09a172 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py32, py33, pep8, cov +envlist = py26, py27, py32, py33, py34, pep8, cov [testenv] commands = nosetests From 75b17c429881c108373faf817c3b47e83eb9271e Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 16:17:37 +0800 Subject: [PATCH 08/85] add django to tests --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index a09a172..d3e7ea3 100644 --- a/tox.ini +++ b/tox.ini @@ -6,6 +6,7 @@ commands = nosetests deps = nose mock + django [testenv:py26] deps = From 831530ab2828f0792fb626e2b4e2d5280242e66c Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 16:18:05 +0800 Subject: [PATCH 09/85] init tests for django module --- jsonrpc/backend/django.py | 5 ++++- jsonrpc/tests/test_backend_django.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 jsonrpc/tests/test_backend_django.py diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index 7e8d39b..05ff8c8 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import + from django.conf.urls import url from django.http import HttpResponse from django.views.decorators.http import require_http_methods @@ -14,6 +16,7 @@ class JSONRPCAPI(object): def __init__(self, dispatcher=None): self.dispatcher = dispatcher or Dispatcher() + @property def urls(self): urls = [ url(r'^$', self.jsonrpc), @@ -25,7 +28,7 @@ def urls(self): @require_http_methods(["POST"]) def jsonrpc(self, request): """ JSON-RPC 2.0 handler.""" - request_str = request.body + request_str = request.body.decode('utf8') try: jsonrpc_request = JSONRPCRequest.from_json(request_str) except (TypeError, ValueError, JSONRPCInvalidRequestException): diff --git a/jsonrpc/tests/test_backend_django.py b/jsonrpc/tests/test_backend_django.py new file mode 100644 index 0000000..79b00f1 --- /dev/null +++ b/jsonrpc/tests/test_backend_django.py @@ -0,0 +1,15 @@ +""" Test Django Backend.""" +from __future__ import absolute_import +import sys +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + + +from ..backend.django import JSONRPCAPI, api + + +class TestDjangoBackend(unittest.TestCase): + def test_urls(self): + self.assertTrue(isinstance(api.urls, list)) From 6477dac20ce27d6c0c7548d242ea9cd837da1033 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 16:23:07 +0800 Subject: [PATCH 10/85] specify django version for tests --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d3e7ea3..7dd0cc1 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ commands = nosetests deps = nose mock - django + django==1.6 [testenv:py26] deps = From e3dd89b2d658b4557002b52c075ae8ba3fd970f6 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 17:50:37 +0800 Subject: [PATCH 11/85] support python 3.4 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 9f54230..738a67e 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,7 @@ def read(fname): "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries :: Python Modules", ], license="MIT", From 0e8724dd389cf7ef1ba756f3e826d97615851422 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 17:51:14 +0800 Subject: [PATCH 12/85] add tests for django --- README.rst | 5 ++ jsonrpc/backend/django.py | 7 +-- jsonrpc/tests/test_backend_django.py | 15 ------ jsonrpc/tests/test_backend_django/__init__.py | 0 jsonrpc/tests/test_backend_django/settings.py | 10 ++++ jsonrpc/tests/test_backend_django/tests.py | 46 +++++++++++++++++++ jsonrpc/tests/test_backend_django/urls.py | 7 +++ tox.ini | 1 + 8 files changed, 73 insertions(+), 18 deletions(-) delete mode 100644 jsonrpc/tests/test_backend_django.py create mode 100644 jsonrpc/tests/test_backend_django/__init__.py create mode 100644 jsonrpc/tests/test_backend_django/settings.py create mode 100644 jsonrpc/tests/test_backend_django/tests.py create mode 100644 jsonrpc/tests/test_backend_django/urls.py diff --git a/README.rst b/README.rst index 4a2d88f..73ffbb8 100644 --- a/README.rst +++ b/README.rst @@ -111,3 +111,8 @@ Client (uses `requests `_) Competitors ----------- There are `several libraries `_ implementing JSON-RPC protocol. List below represents python libraries, none of the supports python3. tinyrpc looks better than others. + + +Testing +------- +json-rpc is python library, it supports pythons: 2.6, 2.7, 3.3, 3.4. There is optional support for django1.6 (python2.6 does not support django1.7). diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index 05ff8c8..e4598b8 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -1,8 +1,7 @@ from __future__ import absolute_import from django.conf.urls import url -from django.http import HttpResponse -from django.views.decorators.http import require_http_methods +from django.http import HttpResponse, HttpResponseNotAllowed import json from ..exceptions import JSONRPCInvalidRequestException @@ -25,9 +24,11 @@ def urls(self): return urls - @require_http_methods(["POST"]) def jsonrpc(self, request): """ JSON-RPC 2.0 handler.""" + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + request_str = request.body.decode('utf8') try: jsonrpc_request = JSONRPCRequest.from_json(request_str) diff --git a/jsonrpc/tests/test_backend_django.py b/jsonrpc/tests/test_backend_django.py deleted file mode 100644 index 79b00f1..0000000 --- a/jsonrpc/tests/test_backend_django.py +++ /dev/null @@ -1,15 +0,0 @@ -""" Test Django Backend.""" -from __future__ import absolute_import -import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - - -from ..backend.django import JSONRPCAPI, api - - -class TestDjangoBackend(unittest.TestCase): - def test_urls(self): - self.assertTrue(isinstance(api.urls, list)) diff --git a/jsonrpc/tests/test_backend_django/__init__.py b/jsonrpc/tests/test_backend_django/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jsonrpc/tests/test_backend_django/settings.py b/jsonrpc/tests/test_backend_django/settings.py new file mode 100644 index 0000000..e573b4d --- /dev/null +++ b/jsonrpc/tests/test_backend_django/settings.py @@ -0,0 +1,10 @@ +SECRET_KEY = 'secret' +ROOT_URLCONF = 'jsonrpc.tests.test_backend_django.urls' +ALLOWED_HOSTS = ['testserver'] +DATABASE_ENGINE = 'django.db.backends.sqlite3' +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + } +} diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py new file mode 100644 index 0000000..d303ebf --- /dev/null +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -0,0 +1,46 @@ +""" Test Django Backend.""" +from __future__ import absolute_import +import sys +import os +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + + +from django.conf.urls import url +from django.core.urlresolvers import RegexURLPattern +from django.test import TestCase +from ...backend.django import api +import json + + +class TestDjangoBackend(TestCase): + @classmethod + def setUpClass(cls): + os.environ['DJANGO_SETTINGS_MODULE'] = 'jsonrpc.tests.test_backend_django.settings' + # django.setup() + + def test_urls(self): + self.assertTrue(isinstance(api.urls, list)) + for api_url in api.urls: + self.assertTrue(isinstance(api_url, RegexURLPattern)) + + def test_client_args(self): + @api.dispatcher.add_method + def dummy(request): + return "" + + json_data = { + "id": "0", + "jsonrpc": "2.0", + "method": "dummy", + } + response = self.client.post( + '', + json.dumps(json_data), + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.content.decode('utf8')) + self.assertEqual(data['result'], '') diff --git a/jsonrpc/tests/test_backend_django/urls.py b/jsonrpc/tests/test_backend_django/urls.py new file mode 100644 index 0000000..65c7e83 --- /dev/null +++ b/jsonrpc/tests/test_backend_django/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import patterns, url, include +from jsonrpc.backend.django import api + +urlpatterns = patterns( + '', + url(r'^', include(api.urls)) +) diff --git a/tox.ini b/tox.ini index 7dd0cc1..2058693 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = py26, py27, py32, py33, py34, pep8, cov [testenv] commands = nosetests +setenv = DJANGO_SETTINGS_MODULE=jsonrpc.tests.test_backend_django.settings deps = nose mock From 7f4d9d6f0f5cc4aaf73f63ec6fc83586d116d1d4 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 18:01:37 +0800 Subject: [PATCH 13/85] test type error coverage --- jsonrpc/tests/test_backend_django/tests.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index d303ebf..b4b2438 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -26,7 +26,7 @@ def test_urls(self): for api_url in api.urls: self.assertTrue(isinstance(api_url, RegexURLPattern)) - def test_client_args(self): + def test_client(self): @api.dispatcher.add_method def dummy(request): return "" @@ -44,3 +44,21 @@ def dummy(request): self.assertEqual(response.status_code, 200) data = json.loads(response.content.decode('utf8')) self.assertEqual(data['result'], '') + + def test_method_not_allowed(self): + response = self.client.get( + '', + content_type='application/json', + ) + self.assertEqual(response.status_code, 405, "Should allow only POST") + + def test_invalid_request(self): + response = self.client.post( + '', + '{', + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.content.decode('utf8')) + self.assertEqual(data['error']['code'], -32700) + self.assertEqual(data['error']['message'], 'Parse error') From cd6353c397b4a5a49b0a03d2235c6f1f75cdb2ef Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 21:22:36 +0800 Subject: [PATCH 14/85] test json rpc map --- jsonrpc/backend/django.py | 2 +- jsonrpc/tests/test_backend_django/tests.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index e4598b8..6e6eac6 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -19,7 +19,7 @@ def __init__(self, dispatcher=None): def urls(self): urls = [ url(r'^$', self.jsonrpc), - url(r'^/map$', self.jsonrpc_map), + url(r'^map$', self.jsonrpc_map), ] return urls diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index b4b2438..d5be7bd 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -62,3 +62,9 @@ def test_invalid_request(self): data = json.loads(response.content.decode('utf8')) self.assertEqual(data['error']['code'], -32700) self.assertEqual(data['error']['message'], 'Parse error') + + def test_resource_map(self): + response = self.client.get('/map') + self.assertEqual(response.status_code, 200) + data = response.content.decode('utf8') + self.assertIn("JSON-RPC map", data) From aa07dbeb8b545a67c75760dbfd827d3efc2e5b90 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 3 Mar 2015 21:23:31 +0800 Subject: [PATCH 15/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 2eb3313..e195b23 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 8, 1) +__version = (1, 8, 2) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 2b7510da9c9170637f29d10484b2cd6ae446b77d Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 5 Mar 2015 09:42:16 +0800 Subject: [PATCH 16/85] update code to match python2.6 format specification --- jsonrpc/backend/django.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index 6e6eac6..167fd9f 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -57,8 +57,8 @@ def jsonrpc_map(self, request): :return str: """ - result = "

JSON-RPC map

{}
".format("\n\n".join([ - "{}: {}".format(fname, f.__doc__) + result = "

JSON-RPC map

{0}
".format("\n\n".join([ + "{0}: {1}".format(fname, f.__doc__) for fname, f in self.dispatcher.items() ])) return HttpResponse(result) From 726a33edb9a5fb124bcab7bf5cdd8afa2d2413e0 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 5 Mar 2015 09:47:14 +0800 Subject: [PATCH 17/85] test package with pypy and jython --- .travis.yml | 2 ++ tox.ini | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a44ef91..c85fe01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,8 @@ env: - TOXENV=py32 - TOXENV=py33 - TOXENV=py34 + - TOXENV=pypy + - TOXENV=jython - TOXENV=cov branches: diff --git a/tox.ini b/tox.ini index 2058693..719d3e9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py32, py33, py34, pep8, cov +envlist = py26, py27, py32, py33, py34, pypy, jython, pep8, cov [testenv] commands = nosetests From 53112e1abffdd377647d11aa0c8e417945bb5740 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 5 Mar 2015 09:57:11 +0800 Subject: [PATCH 18/85] remove jython support --- .travis.yml | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c85fe01..81b558c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ env: - TOXENV=py33 - TOXENV=py34 - TOXENV=pypy - - TOXENV=jython - TOXENV=cov branches: diff --git a/tox.ini b/tox.ini index 719d3e9..355cfed 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py32, py33, py34, pypy, jython, pep8, cov +envlist = py26, py27, py32, py33, py34, pypy, pep8, cov [testenv] commands = nosetests From ca868084d2d513d99cf69526700e58ca1c406c97 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 5 Mar 2015 09:59:05 +0800 Subject: [PATCH 19/85] add pypy to description --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 738a67e..e72851f 100644 --- a/setup.py +++ b/setup.py @@ -47,6 +47,7 @@ def read(fname): "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Libraries :: Python Modules", ], license="MIT", From 04572da3eebc9e6907242bcef642e7dab8a9886e Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 5 Mar 2015 09:59:12 +0800 Subject: [PATCH 20/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index e195b23..0e35210 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 8, 2) +__version = (1, 8, 3) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From be941e85b8ff34c449e4b6e463d86c8b668069af Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:06:59 +0800 Subject: [PATCH 21/85] add more tests to django backend, check urs are included correctly --- .gitignore | 1 + jsonrpc/backend/django.py | 2 +- jsonrpc/tests/test_backend_django/tests.py | 22 +++++++++++++--------- jsonrpc/tests/test_backend_django/urls.py | 3 ++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 4b10aa6..0fae7d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.egg *.egg-info *.py[c|o] +.eggs/ .DS_Store .coverage .env diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index 167fd9f..ec02bb7 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -19,7 +19,7 @@ def __init__(self, dispatcher=None): def urls(self): urls = [ url(r'^$', self.jsonrpc), - url(r'^map$', self.jsonrpc_map), + url(r'map$', self.jsonrpc_map), ] return urls diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index d5be7bd..72dbe93 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -1,14 +1,7 @@ """ Test Django Backend.""" from __future__ import absolute_import -import sys import os -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest - -from django.conf.urls import url from django.core.urlresolvers import RegexURLPattern from django.test import TestCase from ...backend.django import api @@ -18,8 +11,8 @@ class TestDjangoBackend(TestCase): @classmethod def setUpClass(cls): - os.environ['DJANGO_SETTINGS_MODULE'] = 'jsonrpc.tests.test_backend_django.settings' - # django.setup() + os.environ['DJANGO_SETTINGS_MODULE'] = \ + 'jsonrpc.tests.test_backend_django.settings' def test_urls(self): self.assertTrue(isinstance(api.urls, list)) @@ -68,3 +61,14 @@ def test_resource_map(self): self.assertEqual(response.status_code, 200) data = response.content.decode('utf8') self.assertIn("JSON-RPC map", data) + + def test_method_not_allowed_prefix(self): + response = self.client.get( + '/prefix', + content_type='application/json', + ) + self.assertEqual(response.status_code, 405) + + def test_resource_map_prefix(self): + response = self.client.get('/prefix/map') + self.assertEqual(response.status_code, 200) diff --git a/jsonrpc/tests/test_backend_django/urls.py b/jsonrpc/tests/test_backend_django/urls.py index 65c7e83..d428427 100644 --- a/jsonrpc/tests/test_backend_django/urls.py +++ b/jsonrpc/tests/test_backend_django/urls.py @@ -3,5 +3,6 @@ urlpatterns = patterns( '', - url(r'^', include(api.urls)) + url(r'', include(api.urls)), + url(r'prefix', include(api.urls)), ) From 83adc114f0ac7f6a82b27b42822ee0e8b224b78b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:20:32 +0800 Subject: [PATCH 22/85] add explanation to InvalidParams response message, it might be raised because of invalid params or TypeError from inside the function --- jsonrpc/manager.py | 10 ++++++++-- jsonrpc/tests/test_manager.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index 06b601a..89b3ff8 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -102,8 +102,14 @@ def response(**kwargs): else: try: result = method(*request.args, **request.kwargs) - except TypeError: - output = response(error=JSONRPCInvalidParams()._data) + except TypeError as e: + data = { + "type": e.__class__.__name__, + "args": e.args, + "message": str(e), + } + output = response( + error=JSONRPCInvalidParams(data=data)._data) except JSONRPCDispatchException as e: output = response(error=e.error._data) except Exception as e: diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index a881c70..f206c03 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -27,6 +27,7 @@ def raise_(e): "list_len": len, "101_base": lambda **kwargs: int("101", **kwargs), "error": lambda: raise_(KeyError("error_explanation")), + "type_error": lambda: raise_(TypeError("TypeError inside method")), "long_time_method": self.long_time_method, "dispatch_error": lambda x: raise_( JSONRPCDispatchException(code=4000, message="error", @@ -84,6 +85,9 @@ def test_invalid_params(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Invalid params") self.assertEqual(response.error["code"], -32602) + self.assertEqual( + response.error["data"]["message"], + 'sum() takes no keyword arguments') def test_server_error(self): request = JSONRPC20Request("error", _id=0) @@ -117,3 +121,15 @@ def test_notification_does_not_return_error(self): request = JSONRPC20Request("error", is_notification=True) response = JSONRPCResponseManager.handle(request.json, self.dispatcher) self.assertEqual(response, None) + + def test_type_error_inside_method(self): + request = JSONRPC20Request("type_error", _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) + self.assertEqual(response.error["data"], { + "type": "TypeError", + "args": ('TypeError inside method',), + "message": 'TypeError inside method', + }) From d21c548525b5dc3123e1379d738e2914799d73a1 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:22:06 +0800 Subject: [PATCH 23/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 0e35210..0c71dfe 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 8, 3) +__version = (1, 8, 4) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 427e45a9f4efc0a92d924e6627ddc3f2bd9f7b81 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:23:14 +0800 Subject: [PATCH 24/85] add pypy to documentation --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 73ffbb8..58958ac 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ json-rpc :alt: License -`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.2+. +`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.2+, PyPy. Documentation: http://json-rpc.readthedocs.org From 2fed286e177500a72186fd20efa3c6156b8750ed Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:26:55 +0800 Subject: [PATCH 25/85] fix tests for PyPy --- jsonrpc/tests/test_manager.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index f206c03..2121663 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -85,9 +85,10 @@ def test_invalid_params(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Invalid params") self.assertEqual(response.error["code"], -32602) - self.assertEqual( - response.error["data"]["message"], - 'sum() takes no keyword arguments') + self.assertIn(response.error["data"]["message"], [ + 'sum() takes no keyword arguments', + "sum() got an unexpected keyword argument 'a'", + ]) def test_server_error(self): request = JSONRPC20Request("error", _id=0) From b9976402bced56daf33bc06eb39effafedc152f9 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 20 Mar 2015 11:29:28 +0800 Subject: [PATCH 26/85] update README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 58958ac..e10a9df 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ json-rpc :alt: License -`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.2+, PyPy. +`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.2+, PyPy. 200+ tests. Documentation: http://json-rpc.readthedocs.org From 30ab5f03852d61a8d9dc964b780526ca53a9c861 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 23 Mar 2015 23:08:18 +0800 Subject: [PATCH 27/85] distinguish Invalid Params and Type Error Exceptions in manager --- jsonrpc/manager.py | 20 +++++++++--------- jsonrpc/tests/test_manager.py | 38 +++++++++++++++++++++++++++++++++-- jsonrpc/tests/test_utils.py | 24 +++++++++++++++++++++- jsonrpc/utils.py | 29 ++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 14 deletions(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index 89b3ff8..a6ba7a1 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -1,5 +1,6 @@ import json import logging +from .utils import is_invalid_params from .exceptions import ( JSONRPCInvalidParams, JSONRPCInvalidRequest, @@ -102,14 +103,6 @@ def response(**kwargs): else: try: result = method(*request.args, **request.kwargs) - except TypeError as e: - data = { - "type": e.__class__.__name__, - "args": e.args, - "message": str(e), - } - output = response( - error=JSONRPCInvalidParams(data=data)._data) except JSONRPCDispatchException as e: output = response(error=e.error._data) except Exception as e: @@ -118,9 +111,14 @@ def response(**kwargs): "args": e.args, "message": str(e), } - logger.exception("API Exception: {0}".format(data)) - output = response( - error=JSONRPCServerError(data=data)._data) + if isinstance(e, TypeError) and is_invalid_params( + method, *request.args, **request.kwargs): + output = response( + error=JSONRPCInvalidParams(data=data)._data) + else: + logger.exception("API Exception: {0}".format(data)) + output = response( + error=JSONRPCServerError(data=data)._data) else: output = response(result=result) finally: diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 2121663..13a1287 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -24,6 +24,7 @@ def raise_(e): self.long_time_method = MagicMock() self.dispatcher = { "add": sum, + "multiply": lambda a, b: a * b, "list_len": len, "101_base": lambda **kwargs: int("101", **kwargs), "error": lambda: raise_(KeyError("error_explanation")), @@ -90,6 +91,31 @@ def test_invalid_params(self): "sum() got an unexpected keyword argument 'a'", ]) + def test_invalid_params_custom_function(self): + request = JSONRPC20Request("multiply", [0], _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) + + request = JSONRPC20Request("multiply", [0, 1, 2], _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) + + request = JSONRPC20Request("multiply", {"a": 1}, _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) + + request = JSONRPC20Request("multiply", {"a": 1, "b": 2, "c": 3}, _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) + def test_server_error(self): request = JSONRPC20Request("error", _id=0) response = JSONRPCResponseManager.handle(request.json, self.dispatcher) @@ -127,10 +153,18 @@ def test_type_error_inside_method(self): request = JSONRPC20Request("type_error", _id=0) response = JSONRPCResponseManager.handle(request.json, self.dispatcher) self.assertTrue(isinstance(response, JSONRPC20Response)) - self.assertEqual(response.error["message"], "Invalid params") - self.assertEqual(response.error["code"], -32602) + self.assertEqual(response.error["message"], "Server error") + self.assertEqual(response.error["code"], -32000) self.assertEqual(response.error["data"], { "type": "TypeError", "args": ('TypeError inside method',), "message": 'TypeError inside method', }) + + def test_invalid_params_before_dispatcher_error(self): + request = JSONRPC20Request( + "dispatch_error", ["invalid", "params"], _id=0) + response = JSONRPCResponseManager.handle(request.json, self.dispatcher) + self.assertTrue(isinstance(response, JSONRPC20Response)) + self.assertEqual(response.error["message"], "Invalid params") + self.assertEqual(response.error["code"], -32602) diff --git a/jsonrpc/tests/test_utils.py b/jsonrpc/tests/test_utils.py index 738d107..89a1ba6 100644 --- a/jsonrpc/tests/test_utils.py +++ b/jsonrpc/tests/test_utils.py @@ -10,7 +10,7 @@ from mock import patch -from ..utils import JSONSerializable, DatetimeDecimalEncoder +from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params class TestJSONSerializable(unittest.TestCase): @@ -86,3 +86,25 @@ def test_default(self): encoder.default("") self.assertEqual(json_default.call_count, 1) + + +class TestUtils(unittest.TestCase): + + """ Test utils functions.""" + + def test_is_invalid_params_builtin(self): + self.assertTrue(is_invalid_params(sum, 0, 0)) + # NOTE: Function generates TypeError already + # self.assertFalse(is_invalid_params(sum, [0, 0])) + + def test_is_invalid_params_args(self): + self.assertTrue(is_invalid_params(lambda a, b: None, 0)) + self.assertTrue(is_invalid_params(lambda a, b: None, 0, 1, 2)) + + def test_is_invalid_params_kwargs(self): + self.assertTrue(is_invalid_params(lambda x: None, **{})) + self.assertTrue(is_invalid_params(lambda x: None, **{"x": 0, "y": 1})) + + def test_invalid_params_correct(self): + # self.assertFalse(is_invalid_params(lambda: None)) + self.assertFalse(is_invalid_params(lambda a: None, 0)) diff --git a/jsonrpc/utils.py b/jsonrpc/utils.py index d91a8a6..85f2b10 100644 --- a/jsonrpc/utils.py +++ b/jsonrpc/utils.py @@ -2,6 +2,7 @@ from abc import ABCMeta, abstractmethod import datetime import decimal +import inspect import json from . import six @@ -50,3 +51,31 @@ def default(self, o): return o.isoformat() return json.JSONEncoder.default(self, o) + + +def is_invalid_params(func, *args, **kwargs): + """ Check, whether function 'func' accepts parameters 'args', 'kwargs'. + + NOTE: Method is called after funct(*args, **kwargs) generated TypeError, + it is aimed to destinguish TypeError because of invalid parameters from + TypeError from inside the function. + + """ + # For builtin functions inspect.getargspec(funct) return error. If builtin + # function generates TypeError, it is because of wrong parameters. + if not inspect.isfunction(func): + return True + + funcargs, varargs, varkwargs, defaults = inspect.getargspec(func) + if defaults: + funcargs = funcargs[:-len(defaults)] + + if args and len(args) != len(funcargs): + return True + if kwargs and set(kwargs.keys()) != set(funcargs): + return True + + if not args and not kwargs and funcargs: + return True + + return False From fbdfb6608e4cdbf1cf04c2f2f38175c8db234cca Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 24 Mar 2015 10:20:03 +0800 Subject: [PATCH 28/85] cover code --- jsonrpc/tests/test_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsonrpc/tests/test_utils.py b/jsonrpc/tests/test_utils.py index 89a1ba6..77cd0e3 100644 --- a/jsonrpc/tests/test_utils.py +++ b/jsonrpc/tests/test_utils.py @@ -108,3 +108,4 @@ def test_is_invalid_params_kwargs(self): def test_invalid_params_correct(self): # self.assertFalse(is_invalid_params(lambda: None)) self.assertFalse(is_invalid_params(lambda a: None, 0)) + self.assertFalse(is_invalid_params(lambda a, b=0: None, 0)) From 211f1d762d2fe53d55a519d219f1713add60db75 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 24 Mar 2015 10:23:35 +0800 Subject: [PATCH 29/85] add comments to code --- jsonrpc/manager.py | 5 ++++- jsonrpc/utils.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index a6ba7a1..0dac0b5 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -90,6 +90,9 @@ def _get_responses(cls, requests, dispatcher): :return iterator(JSONRPC20Response): + .. versionadded: 1.9.0 + TypeError inside the function is distinguished from Invalid Params. + """ for request in requests: def response(**kwargs): @@ -112,7 +115,7 @@ def response(**kwargs): "message": str(e), } if isinstance(e, TypeError) and is_invalid_params( - method, *request.args, **request.kwargs): + method, *request.args, **request.kwargs): output = response( error=JSONRPCInvalidParams(data=data)._data) else: diff --git a/jsonrpc/utils.py b/jsonrpc/utils.py index 85f2b10..906b5e0 100644 --- a/jsonrpc/utils.py +++ b/jsonrpc/utils.py @@ -60,6 +60,8 @@ def is_invalid_params(func, *args, **kwargs): it is aimed to destinguish TypeError because of invalid parameters from TypeError from inside the function. + .. versionadded: 1.9.0 + """ # For builtin functions inspect.getargspec(funct) return error. If builtin # function generates TypeError, it is because of wrong parameters. From b7466682fe72de35c8cf742ec743c0e6df9a4b80 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 24 Mar 2015 10:24:08 +0800 Subject: [PATCH 30/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 0c71dfe..f31de7e 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 8, 4) +__version = (1, 9, 0) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 3ac47949acee9990d82ff1d4f09ab28bc819f6a0 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sat, 28 Mar 2015 10:17:44 +0800 Subject: [PATCH 31/85] add serve documents shortcut --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 5b63802..7bf7007 100644 --- a/Makefile +++ b/Makefile @@ -46,3 +46,8 @@ test: clean $(ENV): virtualenv --no-site-packages .env $(ENV)/bin/pip install -r requirements.txt + +.PHONY: serve +# target: serve - server docs +serve: + python3 -mhttp.server From fbf0b9df38ff7f161eda99ce891bcab7b247571d Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sat, 28 Mar 2015 11:37:21 +0800 Subject: [PATCH 32/85] update documentation, move source files to docs/source --- docs/Makefile | 2 +- docs/index.rst | 35 -------------- docs/modules.rst | 7 --- docs/{ => source}/conf.py | 6 +-- docs/source/dispatcher.rst | 4 ++ docs/source/django_integration.rst | 4 ++ docs/source/exceptions.rst | 2 + docs/source/index.rst | 35 ++++++++++++++ docs/{ => source}/jsonrpc.rst | 0 docs/source/quickstart.rst | 78 ++++++++++++++++++++++++++++++ setup.cfg | 2 + 11 files changed, 129 insertions(+), 46 deletions(-) delete mode 100644 docs/index.rst delete mode 100644 docs/modules.rst rename docs/{ => source}/conf.py (98%) create mode 100644 docs/source/dispatcher.rst create mode 100644 docs/source/django_integration.rst create mode 100644 docs/source/exceptions.rst create mode 100644 docs/source/index.rst rename docs/{ => source}/jsonrpc.rst (100%) create mode 100644 docs/source/quickstart.rst create mode 100644 setup.cfg diff --git a/docs/Makefile b/docs/Makefile index 4930caf..394c1f4 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -10,7 +10,7 @@ BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index df7bff8..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. json-rpc documentation master file, created by - sphinx-quickstart on Sun Oct 13 15:41:10 2013. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to json-rpc's documentation! -==================================== - -Contents: - -.. toctree:: - :maxdepth: 3 - - modules.rst - -JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Primarily this specification defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments. It uses JSON (RFC 4627) as data format. - -Specification: -`JSON-PRC2.0 `_ -`JSON-RPC1.0 `_ - - -Install package:: - - pip install json-rpc - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/docs/modules.rst b/docs/modules.rst deleted file mode 100644 index acdec8c..0000000 --- a/docs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -jsonrpc -======= - -.. toctree:: - :maxdepth: 4 - - jsonrpc diff --git a/docs/conf.py b/docs/source/conf.py similarity index 98% rename from docs/conf.py rename to docs/source/conf.py index 737ff20..f3beac4 100644 --- a/docs/conf.py +++ b/docs/source/conf.py @@ -17,7 +17,7 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath('../..')) from jsonrpc import version, PROJECT @@ -44,7 +44,7 @@ # General information about the project. project = PROJECT -copyright = u'2013, Kirill Pavlov' +copyright = u'2013-2015, Kirill Pavlov' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -93,7 +93,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/source/dispatcher.rst b/docs/source/dispatcher.rst new file mode 100644 index 0000000..0bf679d --- /dev/null +++ b/docs/source/dispatcher.rst @@ -0,0 +1,4 @@ +Dispatcher +========== + +0 diff --git a/docs/source/django_integration.rst b/docs/source/django_integration.rst new file mode 100644 index 0000000..ae91979 --- /dev/null +++ b/docs/source/django_integration.rst @@ -0,0 +1,4 @@ +Integration with Django +======================= + +howto. diff --git a/docs/source/exceptions.rst b/docs/source/exceptions.rst new file mode 100644 index 0000000..afc8d87 --- /dev/null +++ b/docs/source/exceptions.rst @@ -0,0 +1,2 @@ +Exceptions +========== diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..02cbac5 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,35 @@ +.. json-rpc documentation master file, created by + sphinx-quickstart on Sun Oct 13 15:41:10 2013. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +JSON-RPC transport implementation +================================= + +:Source code: https://github.com/pavlov99/json-rpc +:Issue tracker: https://github.com/pavlov99/json-rpc/issues + + +JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Primarily this specification defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments. It uses JSON (RFC 4627) as data format. + +Features +-------- + +* Supports `JSON-PRC2.0 `_ and `JSON-RPC1.0 `_ +* Implementation is complete and 100% tested +* Does not depend on transport realisation, no external dependencies +* It comes with request manager and optional Django support +* Compatible with Python 2.6, 2.7, 3.x >= 3.2, PyPy + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + quickstart + dispatcher + exceptions + django_integration + + jsonrpc diff --git a/docs/jsonrpc.rst b/docs/source/jsonrpc.rst similarity index 100% rename from docs/jsonrpc.rst rename to docs/source/jsonrpc.rst diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst new file mode 100644 index 0000000..d912281 --- /dev/null +++ b/docs/source/quickstart.rst @@ -0,0 +1,78 @@ +Quickstart +========== + +Installation +------------ + +.. highlight:: bash + +:Requirements: **Python 2.6, 2.7**, **Python 3.x >= 3.2** or **PyPy** + +To install the latest released version of package:: + + pip install json-rpc + +Integration +----------- + +Package is transport agnostic, integration depends on you framework. As an example we have server with `Werkzeug `_ and client with `requests `_. + +Server + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + from werkzeug.serving import run_simple + + from jsonrpc import JSONRPCResponseManager, dispatcher + + + @dispatcher.add_method + def foobar(**kwargs): + return kwargs["foo"] + kwargs["bar"] + + + @Request.application + def application(request): + # Dispatcher is dictionary {: callable} + dispatcher["echo"] = lambda s: s + dispatcher["add"] = lambda a, b: a + b + + response = JSONRPCResponseManager.handle( + request.data, dispatcher) + return Response(response.json, mimetype='application/json') + + + if __name__ == '__main__': + run_simple('localhost', 4000, application) + +Client + +.. code-block:: python + + import requests + import json + + + def main(): + url = "http://localhost:4000/jsonrpc" + headers = {'content-type': 'application/json'} + + # Example echo method + payload = { + "method": "echo", + "params": ["echome!"], + "jsonrpc": "2.0", + "id": 0, + } + response = requests.post( + url, data=json.dumps(payload), headers=headers).json() + + assert response["result"] == "echome!" + assert response["jsonrpc"] + assert response["id"] == 0 + + if __name__ == "__main__": + main() + +q diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 From 3278df5242a8d7d1a5022f67fb337b3e79ba19ae Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sat, 28 Mar 2015 12:59:16 +0800 Subject: [PATCH 33/85] add documentation for dispatcher --- docs/source/conf.py | 7 +++- docs/source/dispatcher.rst | 8 +++-- docs/source/quickstart.rst | 4 ++- jsonrpc/dispatcher.py | 69 ++++++++++++++++++++++++++++---------- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index f3beac4..c4749f8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -28,7 +28,12 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/source/dispatcher.rst b/docs/source/dispatcher.rst index 0bf679d..3802acf 100644 --- a/docs/source/dispatcher.rst +++ b/docs/source/dispatcher.rst @@ -1,4 +1,6 @@ -Dispatcher -========== +Method dispatcher +================= -0 +.. automodule:: jsonrpc.dispatcher + :members: + :special-members: __init__ diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index d912281..202dfaf 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -75,4 +75,6 @@ Client if __name__ == "__main__": main() -q +Package ensures that request and response messages have correct format. +Besides that it provides :class:`jsonrpc.manager.JSONRPCResponseManager` which handles server common cases, such as incorrect message format or invalid method parameters. +Futher topics describe how to add methods to manager, how to handle custom exceptions and optional Django integration. diff --git a/jsonrpc/dispatcher.py b/jsonrpc/dispatcher.py index 315f86e..df1b3ae 100644 --- a/jsonrpc/dispatcher.py +++ b/jsonrpc/dispatcher.py @@ -1,19 +1,30 @@ -import collections +""" Dispatcher is used to add methods (functions) to the server. +For usage examples see :meth:`Dispatcher.add_method` -class Dispatcher(collections.MutableMapping): +""" +import collections - """ Method dispatcher. - Dictionary like object which holds map method_name to method. +class Dispatcher(collections.MutableMapping): - """ + """ Dictionary like object which maps method_name to method.""" def __init__(self, prototype=None): """ Build method dispatcher. - :param prototype: Initial method mapping. - :type prototype: None or object or dict + Parameters + ---------- + prototype : object or dict, optional + Initial method mapping. + + Examples + -------- + + Init object with method dictionary. + + >>> Dispatcher({"sum": lambda a, b: a + b}) + None """ self.method_map = dict() @@ -42,11 +53,33 @@ def __repr__(self): def add_method(self, f, name=None): """ Add a method to the dispatcher. - :param callable f: Callable to be added. - :param name: Name to register - :type name: None or str + Parameters + ---------- + f : callable + Callable to be added. + name : str, optional + Name to register (the default is function **f** name) + + Notes + ----- + When used as a decorator keeps callable object unmodified. + + Examples + -------- + + Use as method + + >>> d = Dispatcher() + >>> d.add_method(lambda a, b: a + b, name="sum") + > + + Or use as decorator + + >>> d = Dispatcher() + >>> @d.add_method + def mymethod(*args, **kwargs): + print(args, kwargs) - When used as a decorator keep callable object unmodified. """ self.method_map[name or f.__name__] = f return f @@ -54,12 +87,14 @@ def add_method(self, f, name=None): def build_method_map(self, prototype): """ Add prototype methods to the dispatcher. - :param prototype: Method mapping. - :type prototype: None or object or dict - - If given prototype is a dictionary then all callable objects - will be added to dispatcher. If given prototype is an object - then all public methods will be used. + Parameters + ---------- + prototype : object or dict + Initial method mapping. + If given prototype is a dictionary then all callable objects will + be added to dispatcher. + If given prototype is an object then all public methods will + be used. """ if not isinstance(prototype, dict): From 2f7f1f010034241278abd1252fa1fa9fc2f73ba9 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 29 Mar 2015 15:16:08 +0800 Subject: [PATCH 34/85] add napoleon sphinx extension to requirements --- docs/requirements.txt | 2 ++ docs/source/conf.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..7bf2a7a --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx_rtd_theme +sphinxcontrib-napoleon diff --git a/docs/source/conf.py b/docs/source/conf.py index c4749f8..5da81a9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,7 +30,7 @@ # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', + 'sphinxcontrib.napoleon', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', ] @@ -106,7 +106,8 @@ #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +import sphinx_rtd_theme +html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". From 5e777ede73d2651cd9527bfcae13d55567864a89 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 29 Mar 2015 20:55:56 +0800 Subject: [PATCH 35/85] add more exception documentation --- docs/source/exceptions.rst | 14 ++++++++++++++ docs/source/jsonrpc.rst | 12 ++++-------- jsonrpc/exceptions.py | 27 +++++++++++++++------------ 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/docs/source/exceptions.rst b/docs/source/exceptions.rst index afc8d87..e280791 100644 --- a/docs/source/exceptions.rst +++ b/docs/source/exceptions.rst @@ -1,2 +1,16 @@ Exceptions ========== + +According to specification, error code should be in response message. Http +server should respond with status code 200, even if there is an error. + +JSON-RPC Error messages +----------------------- + +Error messages are members of :class:`~jsonrpc.exceptions.JSONRPCError` class. Any custom error messages should be inherited from it. + +JSON-RPC has several predefined errors, each of them has reserver error code which should not be used for custom exceptions. + +JSON-RPC Custom exceptions +-------------------------- +a diff --git a/docs/source/jsonrpc.rst b/docs/source/jsonrpc.rst index bf09eff..b1adc43 100644 --- a/docs/source/jsonrpc.rst +++ b/docs/source/jsonrpc.rst @@ -9,14 +9,6 @@ jsonrpc Package :undoc-members: :show-inheritance: -:mod:`exceptions` Module ------------------------- - -.. automodule:: jsonrpc.exceptions - :members: - :undoc-members: - :show-inheritance: - :mod:`jsonrpc` Module --------------------- @@ -25,3 +17,7 @@ jsonrpc Package :undoc-members: :show-inheritance: +.. automodule:: jsonrpc.exceptions + :members: + :undoc-members: + :show-inheritance: diff --git a/jsonrpc/exceptions.py b/jsonrpc/exceptions.py index 7ef6261..b26e678 100644 --- a/jsonrpc/exceptions.py +++ b/jsonrpc/exceptions.py @@ -1,3 +1,4 @@ +""" JSON-RPC Exceptions.""" from . import six import json @@ -23,26 +24,28 @@ class JSONRPCError(object): When a rpc call encounters an error, the Response Object MUST contain the error member with a value that is a Object with the following members: - :param int code: A Number that indicates the error type that occurred. + Parameters + ---------- + code: int + A Number that indicates the error type that occurred. This MUST be an integer. - - :param str message: A String providing a short description of the error. + The error codes from and including -32768 to -32000 are reserved for + pre-defined errors. Any code within this range, but not defined + explicitly below is reserved for future use. The error codes are nearly + the same as those suggested for XML-RPC at the following + url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + + message: str + A String providing a short description of the error. The message SHOULD be limited to a concise single sentence. - :param data: A Primitive or Structured value that contains additional + data: int or str or dict or list, optional + A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). - :type data: None or int or str or dict or list - - The error codes from and including -32768 to -32000 are reserved for - pre-defined errors. Any code within this range, but not defined explicitly - below is reserved for future use. The error codes are nearly the same as - those suggested for XML-RPC at the following - url: http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php - """ serialize = staticmethod(json.dumps) From 633ce5df8d0cb237a40485d1aa7457bb8b39e4c1 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 29 Mar 2015 22:06:13 +0800 Subject: [PATCH 36/85] add exception documentation --- docs/source/exceptions.rst | 46 ++++++++++++++++++++++++++++++++------ docs/source/jsonrpc.rst | 25 +++++++++++++++------ jsonrpc/exceptions.py | 28 +++++++++++------------ 3 files changed, 71 insertions(+), 28 deletions(-) diff --git a/docs/source/exceptions.rst b/docs/source/exceptions.rst index e280791..5cebcc2 100644 --- a/docs/source/exceptions.rst +++ b/docs/source/exceptions.rst @@ -4,13 +4,45 @@ Exceptions According to specification, error code should be in response message. Http server should respond with status code 200, even if there is an error. -JSON-RPC Error messages ------------------------ +JSON-RPC Errors +--------------- -Error messages are members of :class:`~jsonrpc.exceptions.JSONRPCError` class. Any custom error messages should be inherited from it. +.. note:: Error is an object which represent any kind of erros in JSON-RPC specification. It is not python Exception and could not be raised. -JSON-RPC has several predefined errors, each of them has reserver error code which should not be used for custom exceptions. +Errors (Error messages) are members of :class:`~jsonrpc.exceptions.JSONRPCError` class. Any custom error messages should be inherited from it. +The class is responsible for specification following and creates response string based on error's attributes. -JSON-RPC Custom exceptions --------------------------- -a +JSON-RPC has several predefined errors, each of them has reserved code, which should not be used for custom errors. + ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| Code | Message | Meaning | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32700 | Parse error | Invalid JSON was received by the server.An error occurred on the server while parsing the JSON text. | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32600 | Invalid Request | The JSON sent is not a valid Request object. | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32601 | Method not found | The method does not exist / is not available. | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32602 | Invalid params | Invalid method parameter(s). | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32603 | Internal error | Internal JSON-RPC error. | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ +| -32000 to -32099 | Server error | Reserved for implementation-defined server-errors. | ++------------------+------------------+------------------------------------------------------------------------------------------------------+ + +:class:`~jsonrpc.manager.JSONRPCResponseManager` handles common errors. If you do not plan to implement own manager, you do not need to write custom errors. To controll error messages and codes, json-rpc has exceptions, covered in next paragraph. + +JSON-RPC Exceptions +------------------- + +.. note:: Exception here a json-rpc library object and not related to specification. They are inherited from python Exception and could be raised. + +JSON-RPC manager handles dispatcher method's exceptions, anything you raise would be catched. +There are two ways to generate error message in manager: + +First is to simply raise exception in your method. Manager will catch it and return :class:`~jsonrpc.exceptions.JSONRPCServerError` message with description. Advantage of this mehtod is that everything is already implemented, just add method to dispatcher and manager will do the job. + +If you need custom message code or error management, you might need to raise exception, inherited from :class:`~jsonrpc.exceptions.JSONRPCDispatchException`. Make sure, your exception class has error code. + +.. versionadded:: 1.9.0 + Fix `Invalid params` error false generated if method raises TypeError. Now in this case manager introspects the code and returns proper exception. diff --git a/docs/source/jsonrpc.rst b/docs/source/jsonrpc.rst index b1adc43..57d37b1 100644 --- a/docs/source/jsonrpc.rst +++ b/docs/source/jsonrpc.rst @@ -1,23 +1,34 @@ jsonrpc Package =============== -:mod:`jsonrpc` Package ----------------------- +JSONRPC +------- -.. automodule:: jsonrpc.__init__ +.. automodule:: jsonrpc.jsonrpc :members: :undoc-members: :show-inheritance: -:mod:`jsonrpc` Module ---------------------- +Exceptions +---------- -.. automodule:: jsonrpc.jsonrpc +.. automodule:: jsonrpc.exceptions :members: :undoc-members: :show-inheritance: -.. automodule:: jsonrpc.exceptions +Manager +------- + +.. automodule:: jsonrpc.manager + :members: + :undoc-members: + :show-inheritance: + +jsonrpc.backend.django module +----------------------------- + +.. automodule:: jsonrpc.backend.django :members: :undoc-members: :show-inheritance: diff --git a/jsonrpc/exceptions.py b/jsonrpc/exceptions.py index b26e678..175ba92 100644 --- a/jsonrpc/exceptions.py +++ b/jsonrpc/exceptions.py @@ -3,20 +3,6 @@ import json -class JSONRPCException(Exception): - - """ JSON-RPC Exception.""" - - pass - - -class JSONRPCInvalidRequestException(JSONRPCException): - - """ Request is not valid.""" - - pass - - class JSONRPCError(object): """ Error for JSON-RPC communication. @@ -172,6 +158,20 @@ class JSONRPCServerError(JSONRPCError): MESSAGE = "Server error" +class JSONRPCException(Exception): + + """ JSON-RPC Exception.""" + + pass + + +class JSONRPCInvalidRequestException(JSONRPCException): + + """ Request is not valid.""" + + pass + + class JSONRPCDispatchException(JSONRPCException): """ JSON-RPC Dispatch Exception. From 15ab7fa2dd05420a1eaf84e4cd607cc169d99b7c Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 29 Mar 2015 22:35:43 +0800 Subject: [PATCH 37/85] add django integration docs --- docs/source/django_integration.rst | 52 +++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/source/django_integration.rst b/docs/source/django_integration.rst index ae91979..6ce6b80 100644 --- a/docs/source/django_integration.rst +++ b/docs/source/django_integration.rst @@ -1,4 +1,54 @@ Integration with Django ======================= -howto. +.. note:: Django backend is optionaly supported. Library itself does not depend on Django. + +Django integration is similar project to project. Starting from version 1.8.4 json-rpc support it and provides convenient way of integration. To add json-rpc to Django project follow steps. + +Create api instance +------------------- + +If you want to use default (global) object, skip this step. In most cases it is enougth to stert with it, even if you plan to add another version later. Default api is located here: + +.. code-block:: python + + from jsonrpc.backend.django import api + + +If you would like to use different api versions (not, you could name methods differently) or use cudtom dispatcher, use + +.. code-block:: python + + from jsonrpc.backend.django import JSONRPCAPI + api = JSONRPCAPI(dispatcher=) + +Later on we assume that you use default api instance + +Add api urls to the project +--------------------------- + +In your urls.py file add + +.. code-block:: python + + urlpatterns = patterns( + ... + url(r'^api/jsonrpc$', include(api.urls)), + ) + +Add methods to api +------------------ + +.. code-block:: python + + @api.dispatcher.add_method + def my_method(request, *args, **kwargs): + return args, kwargs + +.. note:: first argument of each method should be request. In this case it is possible to get user and control access to data + +Make requests to api +-------------------- + +To use api, send `POST` request to api address. Make sure your message has correct format. +Also json-rpc generates method's map. It is available at `/map` url. From 3e7c099e3a7ddf2388a17c2cf076006f5c689138 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Thu, 16 Apr 2015 17:19:43 +0800 Subject: [PATCH 38/85] add stars to docs --- docs/source/index.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 02cbac5..6db15f9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -9,9 +9,12 @@ JSON-RPC transport implementation :Source code: https://github.com/pavlov99/json-rpc :Issue tracker: https://github.com/pavlov99/json-rpc/issues - JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Primarily this specification defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments. It uses JSON (RFC 4627) as data format. +.. raw:: html + + + Features -------- From e6129a6759b3ef5fc11459780b80ee5581a97aa5 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 5 May 2015 16:17:20 +0800 Subject: [PATCH 39/85] add logger to django handler --- jsonrpc/backend/django.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index ec02bb7..6344257 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -2,7 +2,10 @@ from django.conf.urls import url from django.http import HttpResponse, HttpResponseNotAllowed +import copy import json +import logging +import time from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc import JSONRPCRequest @@ -11,6 +14,9 @@ from ..dispatcher import Dispatcher +logger = logging.getLogger(__name__) + + class JSONRPCAPI(object): def __init__(self, dispatcher=None): self.dispatcher = dispatcher or Dispatcher() @@ -37,10 +43,16 @@ def jsonrpc(self, request): request_str, self.dispatcher) else: jsonrpc_request.params = jsonrpc_request.params or {} + jsonrpc_request_params = copy.copy(jsonrpc_request.params) if isinstance(jsonrpc_request.params, dict): jsonrpc_request.params.update(request=request) + + t1 = time.time() response = JSONRPCResponseManager.handle_request( jsonrpc_request, self.dispatcher) + t2 = time.time() + logger.info('{0}({1}) {2:.2f} sec'.format( + jsonrpc_request.method, jsonrpc_request_params, t2 - t1)) if response: def serialize(s): From 12064ad326cd932cace1c22ef05603b8e9f0a130 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Tue, 5 May 2015 16:18:33 +0800 Subject: [PATCH 40/85] update changelog --- changelog | 4 ++++ jsonrpc/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog b/changelog index 14390e7..20db18a 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,7 @@ +2015-05-05 kirill.pavlov@phystech.edu + + * Add logger to django api client + 2014-09-04 kirill.pavlov@phystech.edu * Add custom exception functionality by @julianhille diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index f31de7e..fcf7b16 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 9, 0) +__version = (1, 9, 1) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 257216d705db1ab6a756270fc2169160a7541634 Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Sat, 23 May 2015 14:14:00 +0000 Subject: [PATCH 41/85] Added Gitter badge --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index e10a9df..871d789 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,10 @@ json-rpc ======== +.. image:: https://badges.gitter.im/Join%20Chat.svg + :alt: Join the chat at https://gitter.im/pavlov99/json-rpc + :target: https://gitter.im/pavlov99/json-rpc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge + .. image:: https://travis-ci.org/pavlov99/json-rpc.png :target: https://travis-ci.org/pavlov99/json-rpc :alt: Build Status From 9da5417be7e85ab5d2e065dd70f42183e5e89c62 Mon Sep 17 00:00:00 2001 From: pavel Date: Tue, 2 Jun 2015 14:57:16 +0500 Subject: [PATCH 42/85] Added support of method prefixes --- jsonrpc/dispatcher.py | 19 ++++++++++++++++-- jsonrpc/tests/test_dispatcher.py | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/jsonrpc/dispatcher.py b/jsonrpc/dispatcher.py index df1b3ae..8505152 100644 --- a/jsonrpc/dispatcher.py +++ b/jsonrpc/dispatcher.py @@ -50,6 +50,19 @@ def __iter__(self): def __repr__(self): return repr(self.method_map) + def add_class(self, cls): + prefix = cls.__name__.lower()+'.' + self.build_method_map(cls(), prefix) + + def add_object(self, obj): + prefix = obj.__class__.__name__.lower()+'.' + self.build_method_map(obj, prefix) + + def add_dict(self, dict, prefix=''): + if prefix: + prefix += '.' + self.build_method_map(dict, prefix) + def add_method(self, f, name=None): """ Add a method to the dispatcher. @@ -84,7 +97,7 @@ def mymethod(*args, **kwargs): self.method_map[name or f.__name__] = f return f - def build_method_map(self, prototype): + def build_method_map(self, prototype, prefix=''): """ Add prototype methods to the dispatcher. Parameters @@ -95,6 +108,8 @@ def build_method_map(self, prototype): be added to dispatcher. If given prototype is an object then all public methods will be used. + prefix: string, optional + Prefix of methods """ if not isinstance(prototype, dict): @@ -104,4 +119,4 @@ def build_method_map(self, prototype): for attr, method in prototype.items(): if callable(method): - self[attr] = method + self[prefix+attr] = method diff --git a/jsonrpc/tests/test_dispatcher.py b/jsonrpc/tests/test_dispatcher.py index 4f76be7..568e90e 100644 --- a/jsonrpc/tests/test_dispatcher.py +++ b/jsonrpc/tests/test_dispatcher.py @@ -6,6 +6,15 @@ import unittest +class Math: + + def sum(self, a, b): + return a+b + + def diff(self, a, b): + return a-b + + class TestDispatcher(unittest.TestCase): """ Test Dispatcher functionality.""" @@ -34,6 +43,31 @@ def add(x, y): self.assertIn("add", d) self.assertEqual(d["add"](1, 1), 2) + def test_add_class(self): + d = Dispatcher() + d.add_class(Math) + + self.assertIn("math.sum", d) + self.assertIn("math.diff", d) + self.assertEqual(d["math.sum"](3, 8), 11) + self.assertEqual(d["math.diff"](6, 9), -3) + + def test_add_object(self): + d = Dispatcher() + d.add_object(Math()) + + self.assertIn("math.sum", d) + self.assertIn("math.diff", d) + self.assertEqual(d["math.sum"](5, 2), 7) + self.assertEqual(d["math.diff"](15, 9), 6) + + def test_add_dict(self): + d = Dispatcher() + d.add_dict({"sum": lambda *args: sum(args)}, "util") + + self.assertIn("util.sum", d) + self.assertEqual(d["util.sum"](13, -2), 11) + def test_add_method_keep_function_definitions(self): d = Dispatcher() From 32804802c79951c089957d69c465ad85b692bedf Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Jun 2015 00:51:44 +0800 Subject: [PATCH 43/85] add spaces around operators, pep8 --- jsonrpc/dispatcher.py | 6 +++--- jsonrpc/tests/test_dispatcher.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jsonrpc/dispatcher.py b/jsonrpc/dispatcher.py index 8505152..2bf5e43 100644 --- a/jsonrpc/dispatcher.py +++ b/jsonrpc/dispatcher.py @@ -51,11 +51,11 @@ def __repr__(self): return repr(self.method_map) def add_class(self, cls): - prefix = cls.__name__.lower()+'.' + prefix = cls.__name__.lower() + '.' self.build_method_map(cls(), prefix) def add_object(self, obj): - prefix = obj.__class__.__name__.lower()+'.' + prefix = obj.__class__.__name__.lower() + '.' self.build_method_map(obj, prefix) def add_dict(self, dict, prefix=''): @@ -119,4 +119,4 @@ def build_method_map(self, prototype, prefix=''): for attr, method in prototype.items(): if callable(method): - self[prefix+attr] = method + self[prefix + attr] = method diff --git a/jsonrpc/tests/test_dispatcher.py b/jsonrpc/tests/test_dispatcher.py index 568e90e..ae9d5cd 100644 --- a/jsonrpc/tests/test_dispatcher.py +++ b/jsonrpc/tests/test_dispatcher.py @@ -9,10 +9,10 @@ class Math: def sum(self, a, b): - return a+b + return a + b def diff(self, a, b): - return a-b + return a - b class TestDispatcher(unittest.TestCase): From aa52af99213db38ff0683dea68f655ecbf1ddc96 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Jun 2015 00:53:14 +0800 Subject: [PATCH 44/85] update changelog --- changelog | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog b/changelog index 20db18a..264c7b3 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,8 @@ +2015-06-03 kirill.pavlov@phystech.edu + + * Added support of method prefixes + https://github.com/pavlov99/json-rpc/pull/31 + 2015-05-05 kirill.pavlov@phystech.edu * Add logger to django api client From 7141b2b75a48810b5a57beb61e0db6d93fc4ed88 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Jun 2015 00:53:17 +0800 Subject: [PATCH 45/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index fcf7b16..db62cba 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 9, 1) +__version = (1, 9, 2) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 493f78c2115cee47f9c1b80ee7d4f7b69206158e Mon Sep 17 00:00:00 2001 From: lorekhov Date: Sun, 28 Jun 2015 17:27:15 +0500 Subject: [PATCH 46/85] Flask backend was added --- docs/source/flask_integration.rst | 65 +++++++++++ jsonrpc/backend/flask.py | 76 +++++++++++++ jsonrpc/tests/test_backend_flask/__init__.py | 0 jsonrpc/tests/test_backend_flask/tests.py | 112 +++++++++++++++++++ tox.ini | 1 + 5 files changed, 254 insertions(+) create mode 100644 docs/source/flask_integration.rst create mode 100644 jsonrpc/backend/flask.py create mode 100644 jsonrpc/tests/test_backend_flask/__init__.py create mode 100644 jsonrpc/tests/test_backend_flask/tests.py diff --git a/docs/source/flask_integration.rst b/docs/source/flask_integration.rst new file mode 100644 index 0000000..abca09f --- /dev/null +++ b/docs/source/flask_integration.rst @@ -0,0 +1,65 @@ +Integration with Flask +====================== + +.. note:: Flask backend is optionaly supported. Library itself does not depend on Flask. + +Create api instance +------------------- + +If you want to use default (global) object, skip this step. In most cases it is enough to start with it, even if you plan to add another version later. Default api is located here: + +.. code-block:: python + + from jsonrpc.backend.flask import api + + +If you would like to use different api versions (not, you could name methods differently) or use custom dispatcher, use + +.. code-block:: python + + from jsonrpc.backend.flask import JSONRPCAPI + api = JSONRPCAPI(dispatcher=) + +Later on we assume that you use default api instance. + +Add api endpoint to the project +------------------------------- + +You have to options to add new endpoint to your Flask application. + +First - register as a blueprint. In this case, as small bonus, you got a /map handler, which prints all registered methods. +.. code-block:: python + + from flask import Flask + + from jsonrpc.backend.flask import api + + app = Flask(__name__) + app.register_blueprint(api.as_blueprint()) + + +Second - register as a usual view. +.. code-block:: python + + from flask import Flask + + from jsonrpc.backend.flask import api + + app = Flask(__name__) + app.add_url_rule('/', 'api', api.as_view()) + + +Add methods to api +------------------ + +.. code-block:: python + + @api.dispatcher.add_method + def my_method(*args, **kwargs): + return args, kwargs + + +Make requests to api +-------------------- + +To use api, send `POST` request to api address. Make sure your message has correct format. diff --git a/jsonrpc/backend/flask.py b/jsonrpc/backend/flask.py new file mode 100644 index 0000000..fb5915d --- /dev/null +++ b/jsonrpc/backend/flask.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import + +import copy +import json +import logging +import time +from uuid import uuid4 + +from flask import Blueprint, request, Response + +from ..exceptions import JSONRPCInvalidRequestException +from ..jsonrpc import JSONRPCRequest +from ..manager import JSONRPCResponseManager +from ..utils import DatetimeDecimalEncoder +from ..dispatcher import Dispatcher + + +logger = logging.getLogger(__name__) + + +class JSONRPCAPI(object): + def __init__(self, dispatcher=None): + self.dispatcher = dispatcher or Dispatcher() + + def as_blueprint(self, name=None): + blueprint = Blueprint(name if name else str(uuid4()), __name__) + blueprint.add_url_rule( + '/', view_func=self.jsonrpc, methods=['POST']) + blueprint.add_url_rule( + '/map', view_func=self.jsonrpc_map, methods=['GET']) + return blueprint + + def as_view(self): + return self.jsonrpc + + def jsonrpc(self): + request_str = request.data + try: + jsonrpc_request = JSONRPCRequest.from_json(request_str) + except (TypeError, ValueError, JSONRPCInvalidRequestException): + response = JSONRPCResponseManager.handle( + request_str, self.dispatcher) + else: + jsonrpc_request.params = jsonrpc_request.params or {} + jsonrpc_request_params = copy.copy(jsonrpc_request.params) + t1 = time.time() + response = JSONRPCResponseManager.handle_request( + jsonrpc_request, self.dispatcher) + t2 = time.time() + logger.info('{0}({1}) {2:.2f} sec'.format( + jsonrpc_request.method, jsonrpc_request_params, t2 - t1)) + + if response: + response.serialize = self._serialize + response = response.json + + return Response(response, content_type="application/json") + + def jsonrpc_map(self): + """ Map of json-rpc available calls. + + :return str: + + """ + result = "

JSON-RPC map

{0}
".format("\n\n".join([ + "{0}: {1}".format(fname, f.__doc__) + for fname, f in self.dispatcher.items() + ])) + return Response(result) + + @staticmethod + def _serialize(s): + return json.dumps(s, cls=DatetimeDecimalEncoder) + + +api = JSONRPCAPI() diff --git a/jsonrpc/tests/test_backend_flask/__init__.py b/jsonrpc/tests/test_backend_flask/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py new file mode 100644 index 0000000..5282968 --- /dev/null +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -0,0 +1,112 @@ +import json +from unittest import TestCase + +from flask import Blueprint, Flask + +from ...backend.flask import api + + +@api.dispatcher.add_method +def dummy(): + return "" + + +class TestFlaskBackend(TestCase): + def setUp(self): + app = Flask(__name__) + app.config["TESTING"] = True + app.register_blueprint(api.as_blueprint()) + self.client = app.test_client() + + def test_client(self): + json_data = { + "id": "0", + "jsonrpc": "2.0", + "method": "dummy", + } + response = self.client.post( + '/', + data=json.dumps(json_data), + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['result'], '') + + def test_method_not_allowed(self): + response = self.client.get( + '/', + content_type='application/json', + ) + self.assertEqual(response.status_code, 405, "Should allow only POST") + + def test_parse_error(self): + response = self.client.post( + '/', + data='{', + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['error']['code'], -32700) + self.assertEqual(data['error']['message'], 'Parse error') + + def test_invalid_request(self): + response = self.client.post( + '/', + data='{"method": "dummy", "id": 1}', + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['error']['code'], -32600) + self.assertEqual(data['error']['message'], 'Invalid Request') + + def test_method_not_found(self): + data = { + "jsonrpc": "2.0", + "method": "dummy2", + "id": 1 + } + response = self.client.post( + '/', + data=json.dumps(data), + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['error']['code'], -32601) + self.assertEqual(data['error']['message'], 'Method not found') + + def test_invalid_parameters(self): + data = { + "jsonrpc": "2.0", + "method": "dummy", + "params": [42], + "id": 1 + } + response = self.client.post( + '/', + data=json.dumps(data), + content_type='application/json', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['error']['code'], -32602) + self.assertEqual(data['error']['message'], 'Invalid params') + + def test_resource_map(self): + response = self.client.get('/map') + self.assertEqual(response.status_code, 200) + self.assertIn("JSON-RPC map", response.data.decode('utf8')) + + def test_method_not_allowed_prefix(self): + response = self.client.get( + '/', + content_type='application/json', + ) + self.assertEqual(response.status_code, 405) + + def test_resource_map_prefix(self): + response = self.client.get('/map') + self.assertEqual(response.status_code, 200) diff --git a/tox.ini b/tox.ini index 355cfed..0d6773f 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ deps = nose mock django==1.6 + flask>=0.10.1 [testenv:py26] deps = From 6d76ddbf36f993f6e02f5a511b6db8cc42975759 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 16:39:16 +0800 Subject: [PATCH 47/85] fix tests for python2.6. Change assertIn to assertTrue( .. in ..) --- jsonrpc/tests/test_backend_flask/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index 5282968..ae1e176 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -98,7 +98,7 @@ def test_invalid_parameters(self): def test_resource_map(self): response = self.client.get('/map') self.assertEqual(response.status_code, 200) - self.assertIn("JSON-RPC map", response.data.decode('utf8')) + self.assertTrue("JSON-RPC map" in response.data.decode('utf8')) def test_method_not_allowed_prefix(self): response = self.client.get( From 8bafaee828e00c39265858b64ca6d264a89a9e1a Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 16:41:50 +0800 Subject: [PATCH 48/85] remove not used variables --- jsonrpc/tests/test_backend_flask/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index ae1e176..235b99e 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -1,7 +1,7 @@ import json from unittest import TestCase -from flask import Blueprint, Flask +from flask import Flask from ...backend.flask import api From 7415ae10b5265d4d9eb302be93ecbbc05e12dcfa Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 16:53:25 +0800 Subject: [PATCH 49/85] do not run Flask tests if python version is between 3.0 and 3.2 inclusive. --- jsonrpc/tests/test_backend_flask/tests.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index 235b99e..b6e9a5e 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -1,17 +1,23 @@ import json -from unittest import TestCase +import sys -from flask import Flask +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest -from ...backend.flask import api +if sys.version_info < (3, 0) or sys.version_info >= (3, 3): + from flask import Flask + from ...backend.flask import api + @api.dispatcher.add_method + def dummy(): + return "" -@api.dispatcher.add_method -def dummy(): - return "" - -class TestFlaskBackend(TestCase): +@unittest.skipIf((3, 0) <= sys.version_info < (3, 3), + 'Flask does not support python 3.0 - 3.2') +class TestFlaskBackend(unittest.TestCase): def setUp(self): app = Flask(__name__) app.config["TESTING"] = True From 2ef9052532ecf58e2ddafbd06ee006080f7b4822 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 17:05:43 +0800 Subject: [PATCH 50/85] cover code up to 100% --- jsonrpc/tests/test_backend_flask/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index b6e9a5e..dcd2589 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -1,5 +1,6 @@ import json import sys +from mock import patch if sys.version_info < (2, 7): import unittest2 as unittest @@ -116,3 +117,7 @@ def test_method_not_allowed_prefix(self): def test_resource_map_prefix(self): response = self.client.get('/map') self.assertEqual(response.status_code, 200) + + def test_as_view(self): + with patch.object(api, 'jsonrpc') as mock_jsonrpc: + self.assertIs(api.as_view(), mock_jsonrpc) From 88f9f8a39e76391f009f13267dfcd4834b20af9b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 17:12:00 +0800 Subject: [PATCH 51/85] update docs. --- LICENSE.txt | 2 +- README.rst | 3 ++- changelog | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/LICENSE.txt b/LICENSE.txt index 63354dc..e7f28b6 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013 Kirill Pavlov +Copyright (c) 2013-2015 Kirill Pavlov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index 871d789..f43835f 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,8 @@ json-rpc :alt: License -`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.2+, PyPy. 200+ tests. +`JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. +Supports python2.6+, python3.2+, PyPy. Has optional Django and Flask support. 200+ tests. Documentation: http://json-rpc.readthedocs.org diff --git a/changelog b/changelog index 264c7b3..84c83d8 100644 --- a/changelog +++ b/changelog @@ -1,24 +1,29 @@ -2015-06-03 kirill.pavlov@phystech.edu +2015-06-29 Kirill Pavlov + + * Flask backend support by Lev Orekhov + (https://github.com/lorehov) + +2015-06-03 Kirill Pavlov * Added support of method prefixes https://github.com/pavlov99/json-rpc/pull/31 -2015-05-05 kirill.pavlov@phystech.edu +2015-05-05 Kirill Pavlov * Add logger to django api client -2014-09-04 kirill.pavlov@phystech.edu +2014-09-04 Kirill Pavlov * Add custom exception functionality by @julianhille -2014-05-25 kirill.pavlov@phystech.edu +2014-05-25 Kirill Pavlov * Add python 2.6 support * Update server notification processing * Add functionality to dispatcher, it is possible to init it with class -2013-11-09 kirill.pavlov@phystech.edu +2013-11-09 Kirill Pavlov * Add JSON-RPC 1.0 support. * Add dispatcher for functions. @@ -30,12 +35,12 @@ * Add dispatcher and JSONPRCManager to jsonrpc/__init__, they are no longer in jsonrpc.jsonrpc module. -2013-10-13 kirill.pavlov@phystech.edu +2013-10-13 Kirill Pavlov * Add examples of usage. * Init documentation. * Remove six from dependencies. -2013-10-08 kirill.pavlov@phystech.edu +2013-10-08 Kirill Pavlov * Implement JSON-RPC 2.0 specification. From 005896db27b4b9a559548ab7d84b275f56ff4075 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 17:14:07 +0800 Subject: [PATCH 52/85] update version. Add Flask support. --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index db62cba..dff968f 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 9, 2) +__version = (1, 10, 1) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From e6687f71dbb2dd329d824f0a0b10092992f33fbd Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 17:18:40 +0800 Subject: [PATCH 53/85] add flask integration to index docs. --- docs/source/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/index.rst b/docs/source/index.rst index 6db15f9..b50d32b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -34,5 +34,6 @@ Contents dispatcher exceptions django_integration + flask_integration jsonrpc From 0682cf5aa4a7f7eb416066a074f9517b3f83eb35 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 29 Jun 2015 17:26:12 +0800 Subject: [PATCH 54/85] fix code blocks definition in documentation. --- docs/source/flask_integration.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/flask_integration.rst b/docs/source/flask_integration.rst index abca09f..491715f 100644 --- a/docs/source/flask_integration.rst +++ b/docs/source/flask_integration.rst @@ -28,6 +28,7 @@ Add api endpoint to the project You have to options to add new endpoint to your Flask application. First - register as a blueprint. In this case, as small bonus, you got a /map handler, which prints all registered methods. + .. code-block:: python from flask import Flask @@ -39,6 +40,7 @@ First - register as a blueprint. In this case, as small bonus, you got a /map ha Second - register as a usual view. + .. code-block:: python from flask import Flask From c90ed0d22b736567b378209cceb5a670b1864cfd Mon Sep 17 00:00:00 2001 From: lorekhov Date: Tue, 4 Aug 2015 01:14:10 +0500 Subject: [PATCH 55/85] content-type check now can be skipped --- jsonrpc/backend/flask.py | 17 +++++-- jsonrpc/tests/test_backend_flask/tests.py | 61 ++++++++++++++++++----- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/jsonrpc/backend/flask.py b/jsonrpc/backend/flask.py index fb5915d..c2bbb86 100644 --- a/jsonrpc/backend/flask.py +++ b/jsonrpc/backend/flask.py @@ -6,7 +6,7 @@ import time from uuid import uuid4 -from flask import Blueprint, request, Response +from flask import Blueprint, request, Response, g from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc import JSONRPCRequest @@ -19,8 +19,14 @@ class JSONRPCAPI(object): - def __init__(self, dispatcher=None): + def __init__(self, dispatcher=None, check_content_type=True): + """ + :param dispatcher: methods dispatcher + :param check_content_type: if True - content-type must be "application/json" + :return: + """ self.dispatcher = dispatcher or Dispatcher() + self.check_content_type = check_content_type def as_blueprint(self, name=None): blueprint = Blueprint(name if name else str(uuid4()), __name__) @@ -34,7 +40,7 @@ def as_view(self): return self.jsonrpc def jsonrpc(self): - request_str = request.data + request_str = self._get_request_str() try: jsonrpc_request = JSONRPCRequest.from_json(request_str) except (TypeError, ValueError, JSONRPCInvalidRequestException): @@ -68,6 +74,11 @@ def jsonrpc_map(self): ])) return Response(result) + def _get_request_str(self): + if self.check_content_type or request.data: + return request.data + return list(request.form.keys())[0] + @staticmethod def _serialize(s): return json.dumps(s, cls=DatetimeDecimalEncoder) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index dcd2589..3e5a789 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -9,31 +9,35 @@ if sys.version_info < (3, 0) or sys.version_info >= (3, 3): from flask import Flask - from ...backend.flask import api - - @api.dispatcher.add_method - def dummy(): - return "" + from ...backend.flask import JSONRPCAPI @unittest.skipIf((3, 0) <= sys.version_info < (3, 3), 'Flask does not support python 3.0 - 3.2') class TestFlaskBackend(unittest.TestCase): + REQUEST = json.dumps({ + "id": "0", + "jsonrpc": "2.0", + "method": "dummy", + }) + def setUp(self): + self.client = self._get_test_client(JSONRPCAPI()) + + def _get_test_client(self, api): + @api.dispatcher.add_method + def dummy(): + return "" + app = Flask(__name__) app.config["TESTING"] = True app.register_blueprint(api.as_blueprint()) - self.client = app.test_client() + return app.test_client() def test_client(self): - json_data = { - "id": "0", - "jsonrpc": "2.0", - "method": "dummy", - } response = self.client.post( '/', - data=json.dumps(json_data), + data=self.REQUEST, content_type='application/json', ) self.assertEqual(response.status_code, 200) @@ -58,6 +62,17 @@ def test_parse_error(self): self.assertEqual(data['error']['code'], -32700) self.assertEqual(data['error']['message'], 'Parse error') + def test_wrong_content_type(self): + response = self.client.post( + '/', + data=self.REQUEST, + content_type='application/x-www-form-urlencoded', + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['error']['code'], -32700) + self.assertEqual(data['error']['message'], 'Parse error') + def test_invalid_request(self): response = self.client.post( '/', @@ -119,5 +134,27 @@ def test_resource_map_prefix(self): self.assertEqual(response.status_code, 200) def test_as_view(self): + api = JSONRPCAPI() with patch.object(api, 'jsonrpc') as mock_jsonrpc: self.assertIs(api.as_view(), mock_jsonrpc) + + def test_not_check_content_type(self): + client = self._get_test_client(JSONRPCAPI(check_content_type=False)) + response = client.post( + '/', + data=self.REQUEST, + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['result'], '') + + def test_check_content_type(self): + client = self._get_test_client(JSONRPCAPI(check_content_type=False)) + response = client.post( + '/', + data=self.REQUEST, + content_type="application/x-www-form-urlencoded" + ) + self.assertEqual(response.status_code, 200) + data = json.loads(response.data.decode('utf8')) + self.assertEqual(data['result'], '') From cd5149eb003a801bc777b279a296f4ab88b1379f Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 7 Aug 2015 03:04:21 +0000 Subject: [PATCH 56/85] allow backend API instance init with custom empty dispatcher. Usecase: init with empty dispatcher subclass. --- jsonrpc/backend/django.py | 3 ++- jsonrpc/backend/flask.py | 3 ++- jsonrpc/tests/test_backend_django/tests.py | 11 ++++++++++- jsonrpc/tests/test_backend_flask/tests.py | 12 +++++++++++- 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index 6344257..cf7aae2 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -19,7 +19,8 @@ class JSONRPCAPI(object): def __init__(self, dispatcher=None): - self.dispatcher = dispatcher or Dispatcher() + self.dispatcher = dispatcher if dispatcher is not None \ + else Dispatcher() @property def urls(self): diff --git a/jsonrpc/backend/flask.py b/jsonrpc/backend/flask.py index fb5915d..20cf13d 100644 --- a/jsonrpc/backend/flask.py +++ b/jsonrpc/backend/flask.py @@ -20,7 +20,8 @@ class JSONRPCAPI(object): def __init__(self, dispatcher=None): - self.dispatcher = dispatcher or Dispatcher() + self.dispatcher = dispatcher if dispatcher is not None \ + else Dispatcher() def as_blueprint(self, name=None): blueprint = Blueprint(name if name else str(uuid4()), __name__) diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index 72dbe93..3f8ff0e 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -4,7 +4,7 @@ from django.core.urlresolvers import RegexURLPattern from django.test import TestCase -from ...backend.django import api +from ...backend.django import JSONRPCAPI, api import json @@ -72,3 +72,12 @@ def test_method_not_allowed_prefix(self): def test_resource_map_prefix(self): response = self.client.get('/prefix/map') self.assertEqual(response.status_code, 200) + + def test_empty_initial_dispatcher(self): + class SubDispatcher(type(api.dispatcher)): + pass + + custom_dispatcher = SubDispatcher() + custom_api = JSONRPCAPI(custom_dispatcher) + self.assertEqual(type(custom_api.dispatcher), SubDispatcher) + self.assertEqual(id(custom_api.dispatcher), id(custom_dispatcher)) diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index dcd2589..20ee9ed 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -7,9 +7,10 @@ else: import unittest +# Flask is supported only for python2 and pyton3.3+ if sys.version_info < (3, 0) or sys.version_info >= (3, 3): from flask import Flask - from ...backend.flask import api + from ...backend.flask import JSONRPCAPI, api @api.dispatcher.add_method def dummy(): @@ -121,3 +122,12 @@ def test_resource_map_prefix(self): def test_as_view(self): with patch.object(api, 'jsonrpc') as mock_jsonrpc: self.assertIs(api.as_view(), mock_jsonrpc) + + def test_empty_initial_dispatcher(self): + class SubDispatcher(type(api.dispatcher)): + pass + + custom_dispatcher = SubDispatcher() + custom_api = JSONRPCAPI(custom_dispatcher) + self.assertEqual(type(custom_api.dispatcher), SubDispatcher) + self.assertEqual(id(custom_api.dispatcher), id(custom_dispatcher)) From e8932292a2b5fba209bf441aadfa33f1e54a6f60 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 7 Aug 2015 03:25:35 +0000 Subject: [PATCH 57/85] add contributors to README --- README.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.rst b/README.rst index f43835f..53e5b43 100644 --- a/README.rst +++ b/README.rst @@ -53,6 +53,13 @@ Tests tox +Features +-------- + +- Vanilla python, no dependencies +- Optional backend support for Django, Flask +- json-rpc 1.1 and 2.0 support + Quickstart ---------- Server (uses `Werkzeug `_) @@ -121,3 +128,15 @@ There are `several libraries `_ +* Jan Willems `@jw `_ +* Robby Dermody (xnova) `@robby-dermody `_ +* matee911 `@matee911 `_ +* Malyshev Artem `@proofit404 `_ +* Julian Hille `@julianhille `_ +* Pavel Evdokimov `@Santinell `_ +* Lev Orekhov `@lorehov `_ From 215dda6847e4b36d28f6dba20c1d5a7be70edc0b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 7 Aug 2015 03:26:51 +0000 Subject: [PATCH 58/85] update changelog --- changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog b/changelog index 84c83d8..acf0084 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,7 @@ +2015-08-07 Kirill Pavlov + + * Allow custom empty dispatcher parameter for backend-specific api + 2015-06-29 Kirill Pavlov * Flask backend support by Lev Orekhov From 9699ca11779751c61284196510547607b4af4ad4 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Fri, 7 Aug 2015 03:35:08 +0000 Subject: [PATCH 59/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index dff968f..2bbf870 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 10, 1) +__version = (1, 10, 2) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From aee08cb6f544d83a71ddf6dcc9d6a34de75bd2f6 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Mon, 17 Aug 2015 11:44:34 +0800 Subject: [PATCH 60/85] Update README.rst --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 53e5b43..df2d906 100644 --- a/README.rst +++ b/README.rst @@ -12,6 +12,9 @@ json-rpc .. image:: https://coveralls.io/repos/pavlov99/json-rpc/badge.png :target: https://coveralls.io/r/pavlov99/json-rpc :alt: Coverage Status + +.. image:: https://www.codacy.com/project/badge/34e0c2c696214041ae4fd5cfcb4af401 + :target: https://www.codacy.com/app/pavlov99/json-rpc .. image:: https://pypip.in/v/json-rpc/badge.png :target: https://crate.io/packages/json-rpc From e20903153bccfc3ebbb799c2ce9d049775fafc71 Mon Sep 17 00:00:00 2001 From: sergey Date: Wed, 28 Oct 2015 08:26:07 +0300 Subject: [PATCH 61/85] FIX: #29 ADD: tests ADD: more obvious constructor for JSONRPCBaseResponse FIX: NotImplemented wrong using REMOVE: test that check none value --- jsonrpc/base.py | 18 +++++++++++++----- jsonrpc/jsonrpc1.py | 18 ++++++++---------- jsonrpc/jsonrpc2.py | 18 +++++++----------- jsonrpc/tests/test_bug29.py | 33 +++++++++++++++++++++++++++++++++ jsonrpc/tests/test_jsonrpc1.py | 5 +++-- jsonrpc/tests/test_jsonrpc2.py | 3 --- jsonrpc/utils.py | 2 +- 7 files changed, 65 insertions(+), 32 deletions(-) create mode 100644 jsonrpc/tests/test_bug29.py diff --git a/jsonrpc/base.py b/jsonrpc/base.py index 4f42f83..9359e85 100644 --- a/jsonrpc/base.py +++ b/jsonrpc/base.py @@ -51,14 +51,22 @@ class JSONRPCBaseResponse(JSONSerializable): """ Base class for JSON-RPC 1.0 and JSON-RPC 2.0 responses.""" - def __init__(self, result=None, error=None, _id=None): + def __init__(self, **kwargs): self.data = dict() - self.result = result - self.error = error - self._id = _id + try: + self.result = kwargs['result'] + except KeyError: + pass + + try: + self.error = kwargs['error'] + except KeyError: + pass + + self._id = kwargs.get('_id') - if self.result is None and self.error is None: + if 'result' not in kwargs and 'error' not in kwargs: raise ValueError("Either result or error should be used") @property diff --git a/jsonrpc/jsonrpc1.py b/jsonrpc/jsonrpc1.py index 475ca5e..c653fd3 100644 --- a/jsonrpc/jsonrpc1.py +++ b/jsonrpc/jsonrpc1.py @@ -1,7 +1,7 @@ from . import six from .base import JSONRPCBaseRequest, JSONRPCBaseResponse -from .exceptions import JSONRPCInvalidRequestException +from .exceptions import JSONRPCInvalidRequestException, JSONRPCError class JSONRPC10Request(JSONRPCBaseRequest): @@ -121,10 +121,8 @@ def result(self): @result.setter def result(self, value): - if value is not None: - if self.error is not None: - raise ValueError("Either result or error should be used") - + if self.error: + raise ValueError("Either result or error should be used") self._data["result"] = value @property @@ -133,11 +131,11 @@ def error(self): @error.setter def error(self, value): - if value is not None: - if self.result is not None: - raise ValueError("Either result or error should be used") - - self._data["error"] = value + self._data.pop('value', None) + if value: + self._data["error"] = value + # Test error + JSONRPCError(**value) @property def _id(self): diff --git a/jsonrpc/jsonrpc2.py b/jsonrpc/jsonrpc2.py index f394d65..0fc5693 100644 --- a/jsonrpc/jsonrpc2.py +++ b/jsonrpc/jsonrpc2.py @@ -207,7 +207,6 @@ def data(self): def data(self, value): if not isinstance(value, dict): raise ValueError("data should be dict") - self._data = value @property @@ -216,11 +215,9 @@ def result(self): @result.setter def result(self, value): - if value is not None: - if self.error is not None: - raise ValueError("Either result or error should be used") - - self._data["result"] = value + if self.error: + raise ValueError("Either result or error should be used") + self._data["result"] = value @property def error(self): @@ -228,12 +225,11 @@ def error(self): @error.setter def error(self, value): - if value is not None: - if self.result is not None: - raise ValueError("Either result or error should be used") - - JSONRPCError(**value) + self._data.pop('value', None) + if value: self._data["error"] = value + # Test error + JSONRPCError(**value) @property def _id(self): diff --git a/jsonrpc/tests/test_bug29.py b/jsonrpc/tests/test_bug29.py new file mode 100644 index 0000000..e3bb7d1 --- /dev/null +++ b/jsonrpc/tests/test_bug29.py @@ -0,0 +1,33 @@ +""" Exmples of usage with tests. + +Tests in this file represent examples taken from JSON-RPC specification. +http://www.jsonrpc.org/specification#examples + +""" +import sys +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest +import json +from ..manager import JSONRPCResponseManager + + +def isjsonequal(json1, json2): + return json.loads(json1) == json.loads(json2) + + +class TestJSONRPCExamples(unittest.TestCase): + def setUp(self): + self.dispatcher = { + "return_none": lambda: None, + } + + def test_none_as_result(self): + req = '{"jsonrpc": "2.0", "method": "return_none", "id": 0}' + response = JSONRPCResponseManager.handle(req, self.dispatcher) + self.assertTrue(isjsonequal( + response.json, + '{"jsonrpc": "2.0", "result": null, "id": 0}' + )) + diff --git a/jsonrpc/tests/test_jsonrpc1.py b/jsonrpc/tests/test_jsonrpc1.py index 147e188..73cdc1f 100644 --- a/jsonrpc/tests/test_jsonrpc1.py +++ b/jsonrpc/tests/test_jsonrpc1.py @@ -395,7 +395,9 @@ def test_validation_error_incorrect(self): with self.assertRaises(ValueError): JSONRPC10Response(**wrong_params) - def test_validation_incorrect_result_and_error(self): + def _test_validation_incorrect_result_and_error(self): + # @todo: remove + # It is OK because result is an mepty string, it is still result with self.assertRaises(ValueError): JSONRPC10Response(result="", error="", _id=0) @@ -408,7 +410,6 @@ def test_data(self): self.assertEqual(json.loads(r.json), r.data) self.assertEqual(r.data, { "result": "", - "error": None, "id": 0, }) diff --git a/jsonrpc/tests/test_jsonrpc2.py b/jsonrpc/tests/test_jsonrpc2.py index d65704f..a927206 100644 --- a/jsonrpc/tests/test_jsonrpc2.py +++ b/jsonrpc/tests/test_jsonrpc2.py @@ -573,9 +573,6 @@ def test_validation_incorrect_no_parameters(self): JSONRPC20Response() def test_validation_incorrect_result_and_error(self): - with self.assertRaises(ValueError): - JSONRPC20Response(result="", error={"code": 1, "message": ""}) - response = JSONRPC20Response(error={"code": 1, "message": ""}) with self.assertRaises(ValueError): response.result = "" diff --git a/jsonrpc/utils.py b/jsonrpc/utils.py index 906b5e0..d2e4038 100644 --- a/jsonrpc/utils.py +++ b/jsonrpc/utils.py @@ -17,7 +17,7 @@ class JSONSerializable(six.with_metaclass(ABCMeta, object)): @abstractmethod def json(self): - raise NotImplemented + raise NotImplementedError() @classmethod def from_json(cls, json_str): From 7516ee0388031ef49fb83161c987d5e05049a5ec Mon Sep 17 00:00:00 2001 From: Jean-Christophe Bohin Date: Wed, 28 Oct 2015 14:09:25 +0100 Subject: [PATCH 62/85] Fixed a typo in the doc --- docs/source/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index b50d32b..595068e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -18,7 +18,7 @@ JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. Prim Features -------- -* Supports `JSON-PRC2.0 `_ and `JSON-RPC1.0 `_ +* Supports `JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ * Implementation is complete and 100% tested * Does not depend on transport realisation, no external dependencies * It comes with request manager and optional Django support From d8f3ea279bafef427dd61cc32be1a4d842a3c78c Mon Sep 17 00:00:00 2001 From: Arne Brutschy Date: Sat, 30 Jan 2016 12:27:45 +0100 Subject: [PATCH 63/85] Makes RPC endpoint CSRF exempt --- jsonrpc/backend/django.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsonrpc/backend/django.py b/jsonrpc/backend/django.py index cf7aae2..9053887 100644 --- a/jsonrpc/backend/django.py +++ b/jsonrpc/backend/django.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +from django.views.decorators.csrf import csrf_exempt from django.conf.urls import url from django.http import HttpResponse, HttpResponseNotAllowed import copy @@ -31,6 +32,7 @@ def urls(self): return urls + @csrf_exempt def jsonrpc(self, request): """ JSON-RPC 2.0 handler.""" if request.method != "POST": From 3bb0fb2a384ea8c7e9fdb7249071375aa8449bc9 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 12:59:30 +0800 Subject: [PATCH 64/85] specify coverage version --- .coveragerc | 4 +--- tox.ini | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.coveragerc b/.coveragerc index 13a43d4..2109815 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,7 +2,7 @@ omit= jsonrpc/six.py jsonrpc/tests/* -source=jsonrpc +include=jsonrpc exclude_lines= raise NotImplemented @@ -11,5 +11,3 @@ omit= jsonrpc/six.py jsonrpc/tests/* source=jsonrpc -exclude_lines= - raise NotImplemented diff --git a/tox.ini b/tox.ini index 0d6773f..b13a769 100644 --- a/tox.ini +++ b/tox.ini @@ -25,7 +25,7 @@ commands = pylama --linters=pep8,pep257,mccabe,pyflakes,pylint jsonrpc [testenv:cov] deps = - coverage + coverage==4.0.3 {[testenv]deps} commands = From 029872b7bcd60a46d76556cc2f30e44757f1747b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:12:26 +0800 Subject: [PATCH 65/85] drop python 3.2 support, fix coverage report arguments --- .coveragerc | 11 +++++------ .travis.yml | 1 - changelog | 5 +++++ setup.py | 1 - tox.ini | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.coveragerc b/.coveragerc index 2109815..500dddb 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,13 +1,12 @@ -[report] +[run] omit= jsonrpc/six.py jsonrpc/tests/* -include=jsonrpc -exclude_lines= - raise NotImplemented +source=jsonrpc -[run] +[report] omit= jsonrpc/six.py jsonrpc/tests/* -source=jsonrpc +exclude_lines= + raise NotImplemented diff --git a/.travis.yml b/.travis.yml index 81b558c..6bf9588 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ python: "2.7" env: - TOXENV=py26 - TOXENV=py27 - - TOXENV=py32 - TOXENV=py33 - TOXENV=py34 - TOXENV=pypy diff --git a/changelog b/changelog index acf0084..08343a8 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,8 @@ +2016-01-31 Kirill Pavlov + + * Drop support of python 3.2. Pip does not support it, which leads to + tests fail + 2015-08-07 Kirill Pavlov * Allow custom empty dispatcher parameter for backend-specific api diff --git a/setup.py b/setup.py index e72851f..6b6c401 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,6 @@ def read(fname): "Operating System :: OS Independent", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: Implementation :: PyPy", diff --git a/tox.ini b/tox.ini index b13a769..b8818c0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py32, py33, py34, pypy, pep8, cov +envlist = py26, py27, py33, py34, pypy, pep8, cov [testenv] commands = nosetests From 1b3d93cc17a127e628f4e557805c8bd99572f556 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:14:50 +0800 Subject: [PATCH 66/85] use master branch to display badge. Otherwise any pull request could make badge red, indicating broken build. --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 53e5b43..15faa5c 100644 --- a/README.rst +++ b/README.rst @@ -5,12 +5,12 @@ json-rpc :alt: Join the chat at https://gitter.im/pavlov99/json-rpc :target: https://gitter.im/pavlov99/json-rpc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge -.. image:: https://travis-ci.org/pavlov99/json-rpc.png - :target: https://travis-ci.org/pavlov99/json-rpc +.. image:: https://travis-ci.org/pavlov99/json-rpc.png?branch=master + :target: https://travis-ci.org/pavlov99/json-rpc?branch=master :alt: Build Status -.. image:: https://coveralls.io/repos/pavlov99/json-rpc/badge.png - :target: https://coveralls.io/r/pavlov99/json-rpc +.. image:: https://coveralls.io/repos/pavlov99/json-rpc/badge.png?branch=master + :target: https://coveralls.io/r/pavlov99/json-rpc?branch=master :alt: Coverage Status .. image:: https://pypip.in/v/json-rpc/badge.png From 97a5ee857d1992d8600ae8e682c5389d6fca8aa7 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:18:45 +0800 Subject: [PATCH 67/85] fix minimal python3 version support in readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 15faa5c..ee965fd 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ json-rpc `JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. -Supports python2.6+, python3.2+, PyPy. Has optional Django and Flask support. 200+ tests. +Supports python2.6+, python3.3+, PyPy. Has optional Django and Flask support. 200+ tests. Documentation: http://json-rpc.readthedocs.org From 9f9f183ab7c545f3c7d3cab06d5cfe1674e82a45 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:25:01 +0800 Subject: [PATCH 68/85] add contributors to README --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9225cff..12b3aed 100644 --- a/README.rst +++ b/README.rst @@ -130,7 +130,7 @@ There are `several libraries `_ * Pavel Evdokimov `@Santinell `_ * Lev Orekhov `@lorehov `_ +* Sergey Nikitin `@nikitinsm `_ +* Jean-Christophe Bohin `@Jean-Christophe Bohin `_ From e6f6e8010ff69a08edbf4d49db516ba5f3a4346d Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:32:49 +0800 Subject: [PATCH 69/85] use NotImplementedError instead of NotImplemented in utils, so fix that in coverage report as well. --- .coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.coveragerc b/.coveragerc index 500dddb..40c3d1b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,4 +9,4 @@ omit= jsonrpc/six.py jsonrpc/tests/* exclude_lines= - raise NotImplemented + raise NotImplementedError() From f21c17ed80cb4beaf8dc730722cca943c45cefd7 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:34:03 +0800 Subject: [PATCH 70/85] remove not supported pypin badges. --- README.rst | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/README.rst b/README.rst index 12b3aed..46addf8 100644 --- a/README.rst +++ b/README.rst @@ -16,23 +16,6 @@ json-rpc .. image:: https://www.codacy.com/project/badge/34e0c2c696214041ae4fd5cfcb4af401 :target: https://www.codacy.com/app/pavlov99/json-rpc -.. image:: https://pypip.in/v/json-rpc/badge.png - :target: https://crate.io/packages/json-rpc - :alt: Version - -.. image:: https://pypip.in/d/json-rpc/badge.png - :target: https://crate.io/packages/json-rpc - :alt: Downloads - -.. image:: https://pypip.in/format/json-rpc/badge.png - :target: https://pypi.python.org/pypi/json-rpc/ - :alt: Download format - - -.. image:: https://pypip.in/license/json-rpc/badge.png - :target: https://pypi.python.org/pypi/json-rpc/ - :alt: License - `JSON-RPC2.0 `_ and `JSON-RPC1.0 `_ transport specification implementation. Supports python2.6+, python3.3+, PyPy. Has optional Django and Flask support. 200+ tests. From a01cf776301ab1420d0b7bd37fdd7c55748f57a8 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sun, 31 Jan 2016 13:34:11 +0800 Subject: [PATCH 71/85] fix typo --- changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog b/changelog index 08343a8..d110027 100644 --- a/changelog +++ b/changelog @@ -41,7 +41,7 @@ * Add custom json serializer (based on json) with datetime.Datetime and decimal.Decimal serialization support. * Move JSONRPC* classes to JSONRPC20*, as far as there is JSONRPC10*. - * Add dispatcher and JSONPRCManager to jsonrpc/__init__, they are no + * Add dispatcher and JSONRPCManager to jsonrpc/__init__, they are no longer in jsonrpc.jsonrpc module. 2013-10-13 Kirill Pavlov From 31bde89ae9f589c037ba85f4251b12b7f686ab7b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Feb 2016 00:33:20 +0800 Subject: [PATCH 72/85] init python3.5 tests --- tox.ini | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 0d6773f..a9b53a5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py32, py33, py34, pypy, pep8, cov +envlist = py26, py27, py33, py34, py35, pypy, pep8, cov [testenv] commands = nosetests @@ -15,6 +15,13 @@ deps = unittest2 {[testenv]deps} +[testenv:py35] +deps = + nose + mock + django==1.9 + flask>=0.10.1 + [testenv:pep8] deps = pep8 commands = pep8 setup.py jsonrpc --exclude=jsonrpc/six.py @@ -25,7 +32,7 @@ commands = pylama --linters=pep8,pep257,mccabe,pyflakes,pylint jsonrpc [testenv:cov] deps = - coverage + coverage==4.0.3 {[testenv]deps} commands = From b47b5c7233ad79ac9df734d7b51757342b83e405 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Feb 2016 00:35:33 +0800 Subject: [PATCH 73/85] remove extra code --- tox.ini | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tox.ini b/tox.ini index e43594e..b8818c0 100644 --- a/tox.ini +++ b/tox.ini @@ -15,13 +15,6 @@ deps = unittest2 {[testenv]deps} -[testenv:py35] -deps = - nose - mock - django==1.9 - flask>=0.10.1 - [testenv:pep8] deps = pep8 commands = pep8 setup.py jsonrpc --exclude=jsonrpc/six.py From 10aed0f435b0ba06b2c6a4a3a518be339c7af5ec Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Feb 2016 00:38:06 +0800 Subject: [PATCH 74/85] update contributors list --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 46addf8..e5f2b10 100644 --- a/README.rst +++ b/README.rst @@ -127,4 +127,5 @@ Contributors * Pavel Evdokimov `@Santinell `_ * Lev Orekhov `@lorehov `_ * Sergey Nikitin `@nikitinsm `_ -* Jean-Christophe Bohin `@Jean-Christophe Bohin `_ +* Jean-Christophe Bohin `@jcbohin `_ +* arnuschky `@arnuschky `_ From d94f95ba7a38d4c6a55e2c08fcb6f706bacfb1a8 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 3 Feb 2016 00:42:39 +0800 Subject: [PATCH 75/85] update version --- jsonrpc/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 2bbf870..06b43bd 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,4 +1,4 @@ -__version = (1, 10, 2) +__version = (1, 10, 3) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ From 7bb40c632bb5eca8d77609b4b13a71f39e4849f9 Mon Sep 17 00:00:00 2001 From: Luke Lee Date: Thu, 4 Aug 2016 11:18:30 +0200 Subject: [PATCH 76/85] Skip django/flask tests if not installed - Django and Flask are not firm requirements set out in the setup.py so makes sense to not mark the tests as failed if they aren't installed. --- jsonrpc/tests/test_backend_django/tests.py | 9 +++++++-- jsonrpc/tests/test_backend_flask/tests.py | 8 ++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index 3f8ff0e..664bfaa 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -2,8 +2,13 @@ from __future__ import absolute_import import os -from django.core.urlresolvers import RegexURLPattern -from django.test import TestCase +try: + from django.core.urlresolvers import RegexURLPattern + from django.test import TestCase +except ImportError: + import unittest + raise unittest.SkipTest('Django not found for testing') + from ...backend.django import JSONRPCAPI, api import json diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index f57a7a1..5dd48ad 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -7,9 +7,13 @@ else: import unittest -# Flask is supported only for python2 and pyton3.3+ +# Flask is supported only for python2 and python3.3+ if sys.version_info < (3, 0) or sys.version_info >= (3, 3): - from flask import Flask + try: + from flask import Flask + except ImportError: + raise unittest.SkipTest('Flask not found for testing') + from ...backend.flask import JSONRPCAPI, api @api.dispatcher.add_method From 11097585e4a2ae751a4d3763a7ef2dcad8489209 Mon Sep 17 00:00:00 2001 From: Piper Merriam Date: Fri, 2 Sep 2016 10:47:37 -0600 Subject: [PATCH 77/85] Add traceback data to error response when exceptions are raised --- jsonrpc/manager.py | 5 +++++ jsonrpc/tests/test_manager.py | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index 0dac0b5..7e83f4a 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -1,3 +1,5 @@ +import sys +import traceback import json import logging from .utils import is_invalid_params @@ -113,6 +115,9 @@ def response(**kwargs): "type": e.__class__.__name__, "args": e.args, "message": str(e), + "traceback": ''.join( + traceback.format_exception(*sys.exc_info()) + ), } if isinstance(e, TypeError) and is_invalid_params( method, *request.args, **request.kwargs): diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 13a1287..221e9b2 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -1,3 +1,4 @@ +import os import sys if sys.version_info < (2, 7): import unittest2 as unittest @@ -122,11 +123,11 @@ def test_server_error(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) - self.assertEqual(response.error["data"], { - "type": "KeyError", - "args": ('error_explanation',), - "message": "'error_explanation'", - }) + self.assertEqual(response.error["data"]['type'], "KeyError") + self.assertEqual(response.error["data"]['args'], ('error_explanation',)) + self.assertEqual(response.error["data"]['message'], "'error_explanation'") + self.assertIn('traceback', response.error["data"]) + self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) def test_notification_calls_method(self): request = JSONRPC20Request("long_time_method", is_notification=True) @@ -155,11 +156,11 @@ def test_type_error_inside_method(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) - self.assertEqual(response.error["data"], { - "type": "TypeError", - "args": ('TypeError inside method',), - "message": 'TypeError inside method', - }) + self.assertEqual(response.error["data"]['type'], "TypeError") + self.assertEqual(response.error["data"]['args'], ('TypeError inside method',)) + self.assertEqual(response.error["data"]['message'], 'TypeError inside method') + self.assertIn('traceback', response.error["data"]) + self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) def test_invalid_params_before_dispatcher_error(self): request = JSONRPC20Request( From 21d4ca753f80333d315870d613e959e318066f6d Mon Sep 17 00:00:00 2001 From: Piper Merriam Date: Fri, 2 Sep 2016 10:47:37 -0600 Subject: [PATCH 78/85] Add traceback data to error response when exceptions are raised --- jsonrpc/manager.py | 5 +++++ jsonrpc/tests/test_manager.py | 21 +++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/jsonrpc/manager.py b/jsonrpc/manager.py index 0dac0b5..7e83f4a 100644 --- a/jsonrpc/manager.py +++ b/jsonrpc/manager.py @@ -1,3 +1,5 @@ +import sys +import traceback import json import logging from .utils import is_invalid_params @@ -113,6 +115,9 @@ def response(**kwargs): "type": e.__class__.__name__, "args": e.args, "message": str(e), + "traceback": ''.join( + traceback.format_exception(*sys.exc_info()) + ), } if isinstance(e, TypeError) and is_invalid_params( method, *request.args, **request.kwargs): diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 13a1287..221e9b2 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -1,3 +1,4 @@ +import os import sys if sys.version_info < (2, 7): import unittest2 as unittest @@ -122,11 +123,11 @@ def test_server_error(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) - self.assertEqual(response.error["data"], { - "type": "KeyError", - "args": ('error_explanation',), - "message": "'error_explanation'", - }) + self.assertEqual(response.error["data"]['type'], "KeyError") + self.assertEqual(response.error["data"]['args'], ('error_explanation',)) + self.assertEqual(response.error["data"]['message'], "'error_explanation'") + self.assertIn('traceback', response.error["data"]) + self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) def test_notification_calls_method(self): request = JSONRPC20Request("long_time_method", is_notification=True) @@ -155,11 +156,11 @@ def test_type_error_inside_method(self): self.assertTrue(isinstance(response, JSONRPC20Response)) self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) - self.assertEqual(response.error["data"], { - "type": "TypeError", - "args": ('TypeError inside method',), - "message": 'TypeError inside method', - }) + self.assertEqual(response.error["data"]['type'], "TypeError") + self.assertEqual(response.error["data"]['args'], ('TypeError inside method',)) + self.assertEqual(response.error["data"]['message'], 'TypeError inside method') + self.assertIn('traceback', response.error["data"]) + self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) def test_invalid_params_before_dispatcher_error(self): request = JSONRPC20Request( From ae0a82bebea91863afa3f23b5f81597946aef8fa Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 5 Oct 2016 03:09:47 +0000 Subject: [PATCH 79/85] fix pep8 --- jsonrpc/__init__.py | 6 +++--- jsonrpc/tests/test_base.py | 5 +++-- jsonrpc/tests/test_bug29.py | 7 ++++--- jsonrpc/tests/test_dispatcher.py | 5 ++++- jsonrpc/tests/test_examples20.py | 6 ++++-- jsonrpc/tests/test_jsonrpc1.py | 10 +++++----- jsonrpc/tests/test_jsonrpc2.py | 9 +++++---- jsonrpc/tests/test_jsonrpc_errors.py | 9 +++++---- jsonrpc/tests/test_manager.py | 9 +++++---- jsonrpc/tests/test_utils.py | 8 ++++---- 10 files changed, 42 insertions(+), 32 deletions(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 06b43bd..5b96971 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,11 +1,11 @@ +from .manager import JSONRPCResponseManager +from .dispatcher import Dispatcher + __version = (1, 10, 3) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ -from .manager import JSONRPCResponseManager -from .dispatcher import Dispatcher - dispatcher = Dispatcher() # lint_ignore=W0611,W0401 diff --git a/jsonrpc/tests/test_base.py b/jsonrpc/tests/test_base.py index 8e8d718..6a0ff85 100644 --- a/jsonrpc/tests/test_base.py +++ b/jsonrpc/tests/test_base.py @@ -1,12 +1,13 @@ """ Test base JSON-RPC classes.""" import sys + +from ..base import JSONRPCBaseRequest, JSONRPCBaseResponse + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -from ..base import JSONRPCBaseRequest, JSONRPCBaseResponse - class TestJSONRPCBaseRequest(unittest.TestCase): diff --git a/jsonrpc/tests/test_bug29.py b/jsonrpc/tests/test_bug29.py index e3bb7d1..7019bce 100644 --- a/jsonrpc/tests/test_bug29.py +++ b/jsonrpc/tests/test_bug29.py @@ -5,12 +5,14 @@ """ import sys +import json + +from ..manager import JSONRPCResponseManager + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -import json -from ..manager import JSONRPCResponseManager def isjsonequal(json1, json2): @@ -30,4 +32,3 @@ def test_none_as_result(self): response.json, '{"jsonrpc": "2.0", "result": null, "id": 0}' )) - diff --git a/jsonrpc/tests/test_dispatcher.py b/jsonrpc/tests/test_dispatcher.py index ae9d5cd..56876b2 100644 --- a/jsonrpc/tests/test_dispatcher.py +++ b/jsonrpc/tests/test_dispatcher.py @@ -88,7 +88,10 @@ def test_del_method(self): def test_to_dict(self): d = Dispatcher() - func = lambda: "" + + def func(): + return "" + d["method"] = func self.assertEqual(dict(d), {"method": func}) diff --git a/jsonrpc/tests/test_examples20.py b/jsonrpc/tests/test_examples20.py index be4eef4..f127b09 100644 --- a/jsonrpc/tests/test_examples20.py +++ b/jsonrpc/tests/test_examples20.py @@ -5,12 +5,14 @@ """ import sys +import json + +from ..manager import JSONRPCResponseManager + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -import json -from ..manager import JSONRPCResponseManager def isjsonequal(json1, json2): diff --git a/jsonrpc/tests/test_jsonrpc1.py b/jsonrpc/tests/test_jsonrpc1.py index 73cdc1f..c66f045 100644 --- a/jsonrpc/tests/test_jsonrpc1.py +++ b/jsonrpc/tests/test_jsonrpc1.py @@ -1,10 +1,5 @@ import json - import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc1 import ( @@ -12,6 +7,11 @@ JSONRPC10Response, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPC10Request(unittest.TestCase): diff --git a/jsonrpc/tests/test_jsonrpc2.py b/jsonrpc/tests/test_jsonrpc2.py index a927206..32c1639 100644 --- a/jsonrpc/tests/test_jsonrpc2.py +++ b/jsonrpc/tests/test_jsonrpc2.py @@ -1,9 +1,5 @@ import json import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc2 import ( @@ -13,6 +9,11 @@ JSONRPC20BatchResponse, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPC20Request(unittest.TestCase): diff --git a/jsonrpc/tests/test_jsonrpc_errors.py b/jsonrpc/tests/test_jsonrpc_errors.py index d7e3c67..abc559f 100644 --- a/jsonrpc/tests/test_jsonrpc_errors.py +++ b/jsonrpc/tests/test_jsonrpc_errors.py @@ -1,9 +1,5 @@ import json import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import ( JSONRPCError, @@ -16,6 +12,11 @@ JSONRPCDispatchException, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPCError(unittest.TestCase): def setUp(self): diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 13a1287..a8d90a0 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -1,8 +1,4 @@ import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from mock import MagicMock from ..manager import JSONRPCResponseManager @@ -15,6 +11,11 @@ from ..jsonrpc1 import JSONRPC10Request, JSONRPC10Response from ..exceptions import JSONRPCDispatchException +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPCResponseManager(unittest.TestCase): def setUp(self): diff --git a/jsonrpc/tests/test_utils.py b/jsonrpc/tests/test_utils.py index 77cd0e3..cb8d94b 100644 --- a/jsonrpc/tests/test_utils.py +++ b/jsonrpc/tests/test_utils.py @@ -3,15 +3,15 @@ import decimal import json import sys +from mock import patch + +from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -from mock import patch - -from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params - class TestJSONSerializable(unittest.TestCase): From 43daaad40b5c37702676f108daae96f2b15c0403 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 5 Oct 2016 03:09:47 +0000 Subject: [PATCH 80/85] fix pep8 --- jsonrpc/__init__.py | 6 +++--- jsonrpc/tests/test_base.py | 5 +++-- jsonrpc/tests/test_bug29.py | 7 ++++--- jsonrpc/tests/test_dispatcher.py | 5 ++++- jsonrpc/tests/test_examples20.py | 6 ++++-- jsonrpc/tests/test_jsonrpc1.py | 10 +++++----- jsonrpc/tests/test_jsonrpc2.py | 9 +++++---- jsonrpc/tests/test_jsonrpc_errors.py | 9 +++++---- jsonrpc/tests/test_manager.py | 9 +++++---- jsonrpc/tests/test_utils.py | 8 ++++---- 10 files changed, 42 insertions(+), 32 deletions(-) diff --git a/jsonrpc/__init__.py b/jsonrpc/__init__.py index 06b43bd..5b96971 100644 --- a/jsonrpc/__init__.py +++ b/jsonrpc/__init__.py @@ -1,11 +1,11 @@ +from .manager import JSONRPCResponseManager +from .dispatcher import Dispatcher + __version = (1, 10, 3) __version__ = version = '.'.join(map(str, __version)) __project__ = PROJECT = __name__ -from .manager import JSONRPCResponseManager -from .dispatcher import Dispatcher - dispatcher = Dispatcher() # lint_ignore=W0611,W0401 diff --git a/jsonrpc/tests/test_base.py b/jsonrpc/tests/test_base.py index 8e8d718..6a0ff85 100644 --- a/jsonrpc/tests/test_base.py +++ b/jsonrpc/tests/test_base.py @@ -1,12 +1,13 @@ """ Test base JSON-RPC classes.""" import sys + +from ..base import JSONRPCBaseRequest, JSONRPCBaseResponse + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -from ..base import JSONRPCBaseRequest, JSONRPCBaseResponse - class TestJSONRPCBaseRequest(unittest.TestCase): diff --git a/jsonrpc/tests/test_bug29.py b/jsonrpc/tests/test_bug29.py index e3bb7d1..7019bce 100644 --- a/jsonrpc/tests/test_bug29.py +++ b/jsonrpc/tests/test_bug29.py @@ -5,12 +5,14 @@ """ import sys +import json + +from ..manager import JSONRPCResponseManager + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -import json -from ..manager import JSONRPCResponseManager def isjsonequal(json1, json2): @@ -30,4 +32,3 @@ def test_none_as_result(self): response.json, '{"jsonrpc": "2.0", "result": null, "id": 0}' )) - diff --git a/jsonrpc/tests/test_dispatcher.py b/jsonrpc/tests/test_dispatcher.py index ae9d5cd..56876b2 100644 --- a/jsonrpc/tests/test_dispatcher.py +++ b/jsonrpc/tests/test_dispatcher.py @@ -88,7 +88,10 @@ def test_del_method(self): def test_to_dict(self): d = Dispatcher() - func = lambda: "" + + def func(): + return "" + d["method"] = func self.assertEqual(dict(d), {"method": func}) diff --git a/jsonrpc/tests/test_examples20.py b/jsonrpc/tests/test_examples20.py index be4eef4..f127b09 100644 --- a/jsonrpc/tests/test_examples20.py +++ b/jsonrpc/tests/test_examples20.py @@ -5,12 +5,14 @@ """ import sys +import json + +from ..manager import JSONRPCResponseManager + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -import json -from ..manager import JSONRPCResponseManager def isjsonequal(json1, json2): diff --git a/jsonrpc/tests/test_jsonrpc1.py b/jsonrpc/tests/test_jsonrpc1.py index 73cdc1f..c66f045 100644 --- a/jsonrpc/tests/test_jsonrpc1.py +++ b/jsonrpc/tests/test_jsonrpc1.py @@ -1,10 +1,5 @@ import json - import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc1 import ( @@ -12,6 +7,11 @@ JSONRPC10Response, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPC10Request(unittest.TestCase): diff --git a/jsonrpc/tests/test_jsonrpc2.py b/jsonrpc/tests/test_jsonrpc2.py index a927206..32c1639 100644 --- a/jsonrpc/tests/test_jsonrpc2.py +++ b/jsonrpc/tests/test_jsonrpc2.py @@ -1,9 +1,5 @@ import json import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import JSONRPCInvalidRequestException from ..jsonrpc2 import ( @@ -13,6 +9,11 @@ JSONRPC20BatchResponse, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPC20Request(unittest.TestCase): diff --git a/jsonrpc/tests/test_jsonrpc_errors.py b/jsonrpc/tests/test_jsonrpc_errors.py index d7e3c67..abc559f 100644 --- a/jsonrpc/tests/test_jsonrpc_errors.py +++ b/jsonrpc/tests/test_jsonrpc_errors.py @@ -1,9 +1,5 @@ import json import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from ..exceptions import ( JSONRPCError, @@ -16,6 +12,11 @@ JSONRPCDispatchException, ) +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPCError(unittest.TestCase): def setUp(self): diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 13a1287..a8d90a0 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -1,8 +1,4 @@ import sys -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest from mock import MagicMock from ..manager import JSONRPCResponseManager @@ -15,6 +11,11 @@ from ..jsonrpc1 import JSONRPC10Request, JSONRPC10Response from ..exceptions import JSONRPCDispatchException +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest + class TestJSONRPCResponseManager(unittest.TestCase): def setUp(self): diff --git a/jsonrpc/tests/test_utils.py b/jsonrpc/tests/test_utils.py index 77cd0e3..cb8d94b 100644 --- a/jsonrpc/tests/test_utils.py +++ b/jsonrpc/tests/test_utils.py @@ -3,15 +3,15 @@ import decimal import json import sys +from mock import patch + +from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params + if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest -from mock import patch - -from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params - class TestJSONSerializable(unittest.TestCase): From 5d20ada9c4222920d164699c8ccfeaf9993191cf Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 5 Oct 2016 03:30:11 +0000 Subject: [PATCH 81/85] fix pep8 --- jsonrpc/tests/test_manager.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 0bc86a2..3a2be3c 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -125,10 +125,13 @@ def test_server_error(self): self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) self.assertEqual(response.error["data"]['type'], "KeyError") - self.assertEqual(response.error["data"]['args'], ('error_explanation',)) - self.assertEqual(response.error["data"]['message'], "'error_explanation'") + self.assertEqual( + response.error["data"]['args'], ('error_explanation',)) + self.assertEqual( + response.error["data"]['message'], "'error_explanation'") self.assertIn('traceback', response.error["data"]) - self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) + self.assertIn( + os.path.basename(__file__), response.error["data"]['traceback']) def test_notification_calls_method(self): request = JSONRPC20Request("long_time_method", is_notification=True) @@ -158,10 +161,13 @@ def test_type_error_inside_method(self): self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) self.assertEqual(response.error["data"]['type'], "TypeError") - self.assertEqual(response.error["data"]['args'], ('TypeError inside method',)) - self.assertEqual(response.error["data"]['message'], 'TypeError inside method') + self.assertEqual( + response.error["data"]['args'], ('TypeError inside method',)) + self.assertEqual( + response.error["data"]['message'], 'TypeError inside method') self.assertIn('traceback', response.error["data"]) - self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) + self.assertIn( + os.path.basename(__file__), response.error["data"]['traceback']) def test_invalid_params_before_dispatcher_error(self): request = JSONRPC20Request( From a76fa94d5b67990411453eac01e4fb92a3c9967e Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 5 Oct 2016 03:30:11 +0000 Subject: [PATCH 82/85] fix pep8 --- jsonrpc/tests/test_manager.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 0bc86a2..3a2be3c 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -125,10 +125,13 @@ def test_server_error(self): self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) self.assertEqual(response.error["data"]['type'], "KeyError") - self.assertEqual(response.error["data"]['args'], ('error_explanation',)) - self.assertEqual(response.error["data"]['message'], "'error_explanation'") + self.assertEqual( + response.error["data"]['args'], ('error_explanation',)) + self.assertEqual( + response.error["data"]['message'], "'error_explanation'") self.assertIn('traceback', response.error["data"]) - self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) + self.assertIn( + os.path.basename(__file__), response.error["data"]['traceback']) def test_notification_calls_method(self): request = JSONRPC20Request("long_time_method", is_notification=True) @@ -158,10 +161,13 @@ def test_type_error_inside_method(self): self.assertEqual(response.error["message"], "Server error") self.assertEqual(response.error["code"], -32000) self.assertEqual(response.error["data"]['type'], "TypeError") - self.assertEqual(response.error["data"]['args'], ('TypeError inside method',)) - self.assertEqual(response.error["data"]['message'], 'TypeError inside method') + self.assertEqual( + response.error["data"]['args'], ('TypeError inside method',)) + self.assertEqual( + response.error["data"]['message'], 'TypeError inside method') self.assertIn('traceback', response.error["data"]) - self.assertIn(os.path.basename(__file__), response.error["data"]['traceback']) + self.assertIn( + os.path.basename(__file__), response.error["data"]['traceback']) def test_invalid_params_before_dispatcher_error(self): request = JSONRPC20Request( From 0c5539afef13d1c2d23dc21e2afa8de5b7c45ee1 Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 21 Jun 2017 09:35:49 +0800 Subject: [PATCH 83/85] add python 3.5 and 3.6 support --- .travis.yml | 3 ++- jsonrpc/tests/test_backend_django/tests.py | 1 + jsonrpc/tests/test_backend_django/urls.py | 7 +++---- tox.ini | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bf9588..c582626 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,11 @@ language: python python: "2.7" env: - - TOXENV=py26 - TOXENV=py27 - TOXENV=py33 - TOXENV=py34 + - TOXENV=py35 + - TOXENV=py36 - TOXENV=pypy - TOXENV=cov diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index 664bfaa..c5e9eb2 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -18,6 +18,7 @@ class TestDjangoBackend(TestCase): def setUpClass(cls): os.environ['DJANGO_SETTINGS_MODULE'] = \ 'jsonrpc.tests.test_backend_django.settings' + super(TestDjangoBackend, cls).setUpClass() def test_urls(self): self.assertTrue(isinstance(api.urls, list)) diff --git a/jsonrpc/tests/test_backend_django/urls.py b/jsonrpc/tests/test_backend_django/urls.py index d428427..7aeefd9 100644 --- a/jsonrpc/tests/test_backend_django/urls.py +++ b/jsonrpc/tests/test_backend_django/urls.py @@ -1,8 +1,7 @@ -from django.conf.urls import patterns, url, include +from django.conf.urls import url, include from jsonrpc.backend.django import api -urlpatterns = patterns( - '', +urlpatterns = [ url(r'', include(api.urls)), url(r'prefix', include(api.urls)), -) +] diff --git a/tox.ini b/tox.ini index b8818c0..5dc282f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py33, py34, pypy, pep8, cov +envlist = py27, py33, py34, py35, py36, pypy, pep8, cov [testenv] commands = nosetests @@ -7,7 +7,7 @@ setenv = DJANGO_SETTINGS_MODULE=jsonrpc.tests.test_backend_django.settings deps = nose mock - django==1.6 + django==1.10 flask>=0.10.1 [testenv:py26] From 7292eb8f974955e83bc75160517a25ab0e4caf0b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Wed, 21 Jun 2017 09:35:49 +0800 Subject: [PATCH 84/85] add python 3.5 and 3.6 support --- .travis.yml | 3 ++- jsonrpc/tests/test_backend_django/tests.py | 1 + jsonrpc/tests/test_backend_django/urls.py | 7 +++---- tox.ini | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bf9588..c582626 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,11 @@ language: python python: "2.7" env: - - TOXENV=py26 - TOXENV=py27 - TOXENV=py33 - TOXENV=py34 + - TOXENV=py35 + - TOXENV=py36 - TOXENV=pypy - TOXENV=cov diff --git a/jsonrpc/tests/test_backend_django/tests.py b/jsonrpc/tests/test_backend_django/tests.py index 664bfaa..c5e9eb2 100644 --- a/jsonrpc/tests/test_backend_django/tests.py +++ b/jsonrpc/tests/test_backend_django/tests.py @@ -18,6 +18,7 @@ class TestDjangoBackend(TestCase): def setUpClass(cls): os.environ['DJANGO_SETTINGS_MODULE'] = \ 'jsonrpc.tests.test_backend_django.settings' + super(TestDjangoBackend, cls).setUpClass() def test_urls(self): self.assertTrue(isinstance(api.urls, list)) diff --git a/jsonrpc/tests/test_backend_django/urls.py b/jsonrpc/tests/test_backend_django/urls.py index d428427..7aeefd9 100644 --- a/jsonrpc/tests/test_backend_django/urls.py +++ b/jsonrpc/tests/test_backend_django/urls.py @@ -1,8 +1,7 @@ -from django.conf.urls import patterns, url, include +from django.conf.urls import url, include from jsonrpc.backend.django import api -urlpatterns = patterns( - '', +urlpatterns = [ url(r'', include(api.urls)), url(r'prefix', include(api.urls)), -) +] diff --git a/tox.ini b/tox.ini index b8818c0..5dc282f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26, py27, py33, py34, pypy, pep8, cov +envlist = py27, py33, py34, py35, py36, pypy, pep8, cov [testenv] commands = nosetests @@ -7,7 +7,7 @@ setenv = DJANGO_SETTINGS_MODULE=jsonrpc.tests.test_backend_django.settings deps = nose mock - django==1.6 + django==1.10 flask>=0.10.1 [testenv:py26] From 69186a16cd10d77a54c0243263e34067476b9e5b Mon Sep 17 00:00:00 2001 From: Kirill Pavlov Date: Sat, 28 Oct 2017 09:23:36 +0800 Subject: [PATCH 85/85] REF: update travis config: use py2.6+ and py3.3+ --- .travis.yml | 36 +++++++-------- jsonrpc/tests/test_backend_flask/tests.py | 6 ++- jsonrpc/tests/test_manager.py | 6 ++- jsonrpc/tests/test_utils.py | 8 +++- tox.ini | 54 ++++++++++++++++++++--- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/.travis.yml b/.travis.yml index c582626..2570bd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,27 @@ -language: python - -python: "2.7" +# Use containers instead of full VMs for faster startup. +sudo: false -env: - - TOXENV=py27 - - TOXENV=py33 - - TOXENV=py34 - - TOXENV=py35 - - TOXENV=py36 - - TOXENV=pypy - - TOXENV=cov +language: python +python: + - "2.6" + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + - "pypy" + - "pypy3" branches: only: - master - develop + - /.*/ install: pip install --quiet tox -script: tox - -after_script: - - if [ $TOXENV == "cov" ]; then - pip install --quiet coveralls; - coveralls; - fi +script: + # Remove version separator from python version and use it as a key for tox. + # Note: prefix it with 'py' for numbered python vestion. + - if [[ $TRAVIS_PYTHON_VERSION == 2* || $TRAVIS_PYTHON_VERSION == 3* ]]; then tox -e $(echo $TRAVIS_PYTHON_VERSION | sed 's/\.//g;s/^/py/'); fi + - if [[ $TRAVIS_PYTHON_VERSION == 'pypy'* ]]; then tox -e $TRAVIS_PYTHON_VERSION; fi diff --git a/jsonrpc/tests/test_backend_flask/tests.py b/jsonrpc/tests/test_backend_flask/tests.py index 5dd48ad..fb0f6b6 100644 --- a/jsonrpc/tests/test_backend_flask/tests.py +++ b/jsonrpc/tests/test_backend_flask/tests.py @@ -1,12 +1,16 @@ import json import sys -from mock import patch if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest +if sys.version_info < (3, 3): + from mock import patch +else: + from unittest.mock import patch + # Flask is supported only for python2 and python3.3+ if sys.version_info < (3, 0) or sys.version_info >= (3, 3): try: diff --git a/jsonrpc/tests/test_manager.py b/jsonrpc/tests/test_manager.py index 3a2be3c..c6a752a 100644 --- a/jsonrpc/tests/test_manager.py +++ b/jsonrpc/tests/test_manager.py @@ -1,6 +1,5 @@ import os import sys -from mock import MagicMock from ..manager import JSONRPCResponseManager from ..jsonrpc2 import ( @@ -17,6 +16,11 @@ else: import unittest +if sys.version_info < (3, 3): + from mock import MagicMock +else: + from unittest.mock import MagicMock + class TestJSONRPCResponseManager(unittest.TestCase): def setUp(self): diff --git a/jsonrpc/tests/test_utils.py b/jsonrpc/tests/test_utils.py index cb8d94b..520961c 100644 --- a/jsonrpc/tests/test_utils.py +++ b/jsonrpc/tests/test_utils.py @@ -1,11 +1,15 @@ """ Test utility functionality.""" +from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params + import datetime import decimal import json import sys -from mock import patch -from ..utils import JSONSerializable, DatetimeDecimalEncoder, is_invalid_params +if sys.version_info < (3, 3): + from mock import patch +else: + from unittest.mock import patch if sys.version_info < (2, 7): import unittest2 as unittest diff --git a/tox.ini b/tox.ini index 5dc282f..12557a4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,19 +1,61 @@ [tox] -envlist = py27, py33, py34, py35, py36, pypy, pep8, cov +envlist = py26, py27, py33, py34, py35, py36, pypy, pypy3, pep8, cov [testenv] commands = nosetests setenv = DJANGO_SETTINGS_MODULE=jsonrpc.tests.test_backend_django.settings deps = - nose - mock - django==1.10 - flask>=0.10.1 + nose==1.3.7 + Flask==0.12.2 +# Python 2.6 configuration. Latest Django support is 1.6 [testenv:py26] deps = - unittest2 + Django==1.6 + Flask==0.12.2 + mock==2.0.0 + nose==1.3.7 + unittest2==1.1.0 + +# Python 2.7 configuration. +# Django 1.11 is likely to be the last version to support Python 2.7 +# https://www.djangoproject.com/weblog/2015/jun/25/roadmap/ +[testenv:py27] +deps = + {[testenv]deps} + mock==2.0.0 + Django==1.11 + +[testenv:py33] +deps = + {[testenv]deps} + django==1.11 + +[testenv:py34] +deps = + {[testenv]deps} + django==1.11 + +[testenv:py35] +deps = + {[testenv]deps} + django==1.11 + +[testenv:py36] +deps = + {[testenv]deps} + django==1.11 + +[testenv:pypy] +deps = + {[testenv]deps} + mock==2.0.0 + django==1.11 + +[testenv:pypy3] +deps = {[testenv]deps} + django==1.11 [testenv:pep8] deps = pep8