summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorefokschaner <efokschaner@riotgames.com>2015-10-08 15:08:04 -0700
committerefokschaner <efokschaner@riotgames.com>2015-10-08 15:08:04 -0700
commitb6b93baaed2cd49eb3e850655e5cc1cf6764cf11 (patch)
treeeea385cc02ea5c9dd739dfc0a59b4d7b211b7e25
parent812a6834eec8b8286f4312b3fb8f748da714329d (diff)
parent3058181be3481ad7b3e4ae9646d357e57420f933 (diff)
downloadjsonrpclib-b6b93baaed2cd49eb3e850655e5cc1cf6764cf11.tar.gz
Merge upstream master into master
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml18
-rw-r--r--README.md5
-rw-r--r--dev-requirements.txt6
-rw-r--r--jsonrpclib/SimpleJSONRPCServer.py47
-rw-r--r--jsonrpclib/config.py8
-rw-r--r--jsonrpclib/history.py6
-rw-r--r--jsonrpclib/jsonclass.py31
-rw-r--r--jsonrpclib/jsonrpc.py155
-rwxr-xr-xsetup.py2
-rw-r--r--tests.py279
11 files changed, 321 insertions, 237 deletions
diff --git a/.gitignore b/.gitignore
index f7d6c1a..f51b1a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.pyc
build/*
dist/*
+.coverage
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c26fcd2
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+language: python
+sudo: false
+python:
+- '2.7'
+- '2.6'
+install:
+- pip install -r dev-requirements.txt
+- pip install twine
+script: nosetests tests.py --with-coverage --cover-package=jsonrpclib
+deploy:
+ provider: pypi
+ user: joshmarshall
+ password:
+ secure: RbRcj7YdDXE9U2/a9yg4DW9UXFfrGWrM+S8uE8RgYu1D9njDDzUyNcFygaBXd8rxd8GpwRXHzSAO31B/Rk4NVbbM7JtcIA/52jx5j+4DdmEhffnzvahDkCZT6EV5iS3IxlShbuxgbzp3Qz14jF7Kl9iBSCOlIFItLCDlK7rfyJU=
+ on:
+ tags: true
+ repo: joshmarshall/jsonrpclib
+ branch: master
diff --git a/README.md b/README.md
index 85a157e..9513c03 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[![Build Status](https://travis-ci.org/joshmarshall/jsonrpclib.svg)](https://travis-ci.org/joshmarshall/jsonrpclib)
+
JSONRPClib
==========
This library is an implementation of the JSON-RPC specification.
@@ -211,7 +213,8 @@ TESTS
I've dropped almost-verbatim tests from the JSON-RPC spec 2.0 page.
You can run it with:
- python tests.py
+ pip install -r dev-requirements.txt
+ nosetests tests.py
TODO
----
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..33be765
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,6 @@
+coverage==4.0
+linecache2==1.0.0
+nose==1.3.7
+six==1.9.0
+traceback2==1.4.0
+unittest2==1.1.0
diff --git a/jsonrpclib/SimpleJSONRPCServer.py b/jsonrpclib/SimpleJSONRPCServer.py
index d76da73..3a0a3bb 100644
--- a/jsonrpclib/SimpleJSONRPCServer.py
+++ b/jsonrpclib/SimpleJSONRPCServer.py
@@ -15,6 +15,7 @@ except ImportError:
# For Windows
fcntl = None
+
def get_version(request):
# must be a dict
if 'jsonrpc' in request.keys():
@@ -22,9 +23,10 @@ def get_version(request):
if 'id' in request.keys():
return 1.0
return None
-
+
+
def validate_request(request):
- if type(request) is not types.DictType:
+ if not isinstance(request, dict):
fault = Fault(
-32600, 'Request must be {}, not %s.' % type(request)
)
@@ -33,27 +35,27 @@ def validate_request(request):
version = get_version(request)
if not version:
fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid)
- return fault
+ return fault
request.setdefault('params', [])
method = request.get('method', None)
params = request.get('params')
param_types = (types.ListType, types.DictType, types.TupleType)
if not method or type(method) not in types.StringTypes or \
- type(params) not in param_types:
+ type(params) not in param_types:
fault = Fault(
-32600, 'Invalid request parameters or method.', rpcid=rpcid
)
return fault
return True
+
class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
def __init__(self, encoding=None):
- SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
- allow_none=True,
- encoding=encoding)
+ SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(
+ self, allow_none=True, encoding=encoding)
- def _marshaled_dispatch(self, data, dispatch_method = None):
+ def _marshaled_dispatch(self, data, dispatch_method=None):
response = None
try:
request = jsonrpclib.loads(data)
@@ -64,7 +66,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
if not request:
fault = Fault(-32600, 'Request invalid -- no request data.')
return fault.response()
- if type(request) is types.ListType:
+ if isinstance(request, list):
# This SHOULD be a batch, by spec
responses = []
for req_entry in request:
@@ -79,7 +81,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
response = '[%s]' % ','.join(responses)
else:
response = ''
- else:
+ else:
result = validate_request(request)
if type(result) is Fault:
return result.response()
@@ -99,7 +101,7 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
exc_type, exc_value, exc_tb = sys.exc_info()
fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
return fault.response()
- if 'id' not in request.keys() or request['id'] == None:
+ if 'id' not in request.keys() or request['id'] is None:
# It's a notification
return None
try:
@@ -132,25 +134,26 @@ class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
pass
if func is not None:
try:
- if type(params) is types.ListType:
+ if isinstance(params, types.ListType):
response = func(*params)
else:
response = func(**params)
return response
- except TypeError:
- return Fault(-32602, 'Invalid parameters.')
+ # except TypeError:
+ # return Fault(-32602, 'Invalid parameters.')
except:
err_lines = traceback.format_exc().splitlines()
trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
- fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
+ fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
trace_string)
return fault
else:
return Fault(-32601, 'Method %s not supported.' % method)
+
class SimpleJSONRPCRequestHandler(
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
-
+
def do_POST(self):
if not self.is_rpc_path_valid():
self.report_404()
@@ -166,13 +169,13 @@ class SimpleJSONRPCRequestHandler(
data = ''.join(L)
response = self.server._marshaled_dispatch(data)
self.send_response(200)
- except Exception, e:
+ except Exception:
self.send_response(500)
err_lines = traceback.format_exc().splitlines()
trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
response = fault.response()
- if response == None:
+ if response is None:
response = ''
self.send_header("Content-type", "application/json-rpc")
self.send_header("Content-length", str(len(response)))
@@ -181,6 +184,7 @@ class SimpleJSONRPCRequestHandler(
self.wfile.flush()
self.connection.shutdown(1)
+
class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher):
allow_reuse_address = True
@@ -198,7 +202,7 @@ class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher):
# Unix sockets can't be bound if they already exist in the
# filesystem. The convention of e.g. X11 is to unlink
# before binding again.
- if os.path.exists(addr):
+ if os.path.exists(addr):
try:
os.unlink(addr)
except OSError:
@@ -207,13 +211,14 @@ class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher):
if vi[0] < 3 and vi[1] < 6:
SocketServer.TCPServer.__init__(self, addr, requestHandler)
else:
- SocketServer.TCPServer.__init__(self, addr, requestHandler,
- bind_and_activate)
+ SocketServer.TCPServer.__init__(
+ self, addr, requestHandler, bind_and_activate)
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
+
class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher):
def __init__(self, encoding=None):
diff --git a/jsonrpclib/config.py b/jsonrpclib/config.py
index 4d28f1b..ca926ca 100644
--- a/jsonrpclib/config.py
+++ b/jsonrpclib/config.py
@@ -1,12 +1,14 @@
import sys
+
class LocalClasses(dict):
def add(self, cls):
self[cls.__name__] = cls
+
class Config(object):
"""
- This is pretty much used exclusively for the 'jsonclass'
+ This is pretty much used exclusively for the 'jsonclass'
functionality... set use_jsonclass to False to turn it off.
You can change serialize_method and ignore_attribute, or use
the local_classes.add(class) to include "local" classes.
@@ -15,7 +17,7 @@ class Config(object):
# Change to False to keep __jsonclass__ entries raw.
serialize_method = '_serialize'
# The serialize_method should be a string that references the
- # method on a custom class object which is responsible for
+ # method on a custom class object which is responsible for
# returning a tuple of the constructor arguments and a dict of
# attributes.
ignore_attribute = '_ignore'
@@ -30,7 +32,7 @@ class Config(object):
'.'.join([str(ver) for ver in sys.version_info[0:3]])
# User agent to use for calls.
_instance = None
-
+
@classmethod
def instance(cls):
if not cls._instance:
diff --git a/jsonrpclib/history.py b/jsonrpclib/history.py
index d11863d..f052baa 100644
--- a/jsonrpclib/history.py
+++ b/jsonrpclib/history.py
@@ -2,13 +2,13 @@ class History(object):
"""
This holds all the response and request objects for a
session. A server using this should call "clear" after
- each request cycle in order to keep it from clogging
+ each request cycle in order to keep it from clogging
memory.
"""
requests = []
responses = []
_instance = None
-
+
@classmethod
def instance(cls):
if not cls._instance:
@@ -17,7 +17,7 @@ class History(object):
def add_response(self, response_obj):
self.responses.append(response_obj)
-
+
def add_request(self, request_obj):
self.requests.append(request_obj)
diff --git a/jsonrpclib/jsonclass.py b/jsonrpclib/jsonclass.py
index 1d86d5f..4326f28 100644
--- a/jsonrpclib/jsonclass.py
+++ b/jsonrpclib/jsonclass.py
@@ -1,7 +1,6 @@
import types
import inspect
import re
-import traceback
from jsonrpclib import config
@@ -30,9 +29,11 @@ value_types = [
supported_types = iter_types+string_types+numeric_types+value_types
invalid_module_chars = r'[^a-zA-Z0-9\_\.]'
+
class TranslationError(Exception):
pass
+
def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]):
if not serialize_method:
serialize_method = config.serialize_method
@@ -46,17 +47,17 @@ def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]):
if obj_type in (types.ListType, types.TupleType):
new_obj = []
for item in obj:
- new_obj.append(dump(item, serialize_method,
- ignore_attribute, ignore))
- if obj_type is types.TupleType:
+ new_obj.append(
+ dump(item, serialize_method, ignore_attribute, ignore))
+ if isinstance(obj_type, types.TupleType):
new_obj = tuple(new_obj)
return new_obj
# It's a dict...
else:
new_obj = {}
for key, value in obj.iteritems():
- new_obj[key] = dump(value, serialize_method,
- ignore_attribute, ignore)
+ new_obj[key] = dump(
+ value, serialize_method, ignore_attribute, ignore)
return new_obj
# It's not a standard type, so it needs __jsonclass__
module_name = inspect.getmodule(obj).__name__
@@ -64,7 +65,7 @@ def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]):
json_class = class_name
if module_name not in ['', '__main__']:
json_class = '%s.%s' % (module_name, json_class)
- return_obj = {"__jsonclass__":[json_class,]}
+ return_obj = {"__jsonclass__": [json_class]}
# If a serialization method is defined..
if serialize_method in dir(obj):
# Params can be a dict (keyword) or list (positional)
@@ -84,21 +85,23 @@ def dump(obj, serialize_method=None, ignore_attribute=None, ignore=[]):
if type(attr_value) in supported_types and \
attr_name not in ignore_list and \
attr_value not in ignore_list:
- attrs[attr_name] = dump(attr_value, serialize_method,
- ignore_attribute, ignore)
+ attrs[attr_name] = dump(
+ attr_value, serialize_method, ignore_attribute, ignore)
return_obj.update(attrs)
return return_obj
+
def load(obj):
- if type(obj) in string_types+numeric_types+value_types:
+ if type(obj) in string_types + numeric_types + value_types:
return obj
- if type(obj) is types.ListType:
+
+ if isinstance(obj, list):
return_list = []
for entry in obj:
return_list.append(load(entry))
return return_list
# Othewise, it's a dict type
- if '__jsonclass__' not in obj.keys():
+ if '__jsonclass__' not in obj:
return_dict = {}
for key, value in obj.iteritems():
new_value = load(value)
@@ -139,9 +142,9 @@ def load(obj):
json_class = getattr(temp_module, json_class_name)
# Creating the object...
new_obj = None
- if type(params) is types.ListType:
+ if isinstance(params, list):
new_obj = json_class(*params)
- elif type(params) is types.DictType:
+ elif isinstance(params, dict):
new_obj = json_class(**params)
else:
raise TranslationError('Constructor args must be a dict or list.')
diff --git a/jsonrpclib/jsonrpc.py b/jsonrpclib/jsonrpc.py
index 3812b34..167bcd7 100644
--- a/jsonrpclib/jsonrpc.py
+++ b/jsonrpclib/jsonrpc.py
@@ -1,15 +1,15 @@
"""
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
============================
JSONRPC Library (jsonrpclib)
@@ -29,7 +29,7 @@ Eventually, I'll add a SimpleXMLRPCServer compatible library,
and other things to tie the thing off nicely. :)
For a quick-start, just open a console and type the following,
-replacing the server address, method, and parameters
+replacing the server address, method, and parameters
appropriately.
>>> import jsonrpclib
>>> server = jsonrpclib.Server('http://localhost:8181')
@@ -47,17 +47,14 @@ See http://code.google.com/p/jsonrpclib/ for more info.
"""
import types
-import sys
from xmlrpclib import Transport as XMLTransport
from xmlrpclib import SafeTransport as XMLSafeTransport
from xmlrpclib import ServerProxy as XMLServerProxy
from xmlrpclib import _Method as XML_Method
-import time
import string
import random
# Library includes
-import jsonrpclib
from jsonrpclib import config
from jsonrpclib import history
@@ -80,14 +77,17 @@ except ImportError:
IDCHARS = string.ascii_lowercase+string.digits
+
class UnixSocketMissing(Exception):
- """
- Just a properly named Exception if Unix Sockets usage is
+ """
+ Just a properly named Exception if Unix Sockets usage is
attempted on a platform that doesn't support them (Windows)
"""
pass
-#JSON Abstractions
+
+# JSON Abstractions
+
def jdumps(obj, encoding='utf-8'):
# Do 'serialize' test at some point for other classes
@@ -97,6 +97,7 @@ def jdumps(obj, encoding='utf-8'):
else:
return json.dumps(obj, encoding=encoding)
+
def jloads(json_string):
global cjson
if cjson:
@@ -107,14 +108,17 @@ def jloads(json_string):
# XMLRPClib re-implementations
+
class ProtocolError(Exception):
pass
+
class TransportMixIn(object):
""" Just extends the XMLRPC transport where necessary. """
user_agent = config.user_agent
# for Python 2.7 support
- _connection = None
+ _connection = (None, None)
+ _extra_headers = []
def send_content(self, connection, request_body):
connection.putheader("Content-Type", "application/json-rpc")
@@ -127,6 +131,7 @@ class TransportMixIn(object):
target = JSONTarget()
return JSONParser(target), target
+
class JSONParser(object):
def __init__(self, target):
self.target = target
@@ -137,6 +142,7 @@ class JSONParser(object):
def close(self):
pass
+
class JSONTarget(object):
def __init__(self):
self.data = []
@@ -147,24 +153,31 @@ class JSONTarget(object):
def close(self):
return ''.join(self.data)
+
class Transport(TransportMixIn, XMLTransport):
- pass
+ def __init__(self):
+ TransportMixIn.__init__(self)
+ XMLTransport.__init__(self)
+
class SafeTransport(TransportMixIn, XMLSafeTransport):
- pass
+ def __init__(self):
+ TransportMixIn.__init__(self)
+ XMLSafeTransport.__init__(self)
+
from httplib import HTTP, HTTPConnection
from socket import socket
USE_UNIX_SOCKETS = False
-try:
+try:
from socket import AF_UNIX, SOCK_STREAM
USE_UNIX_SOCKETS = True
except ImportError:
pass
-
+
if (USE_UNIX_SOCKETS):
-
+
class UnixHTTPConnection(HTTPConnection):
def connect(self):
self.sock = socket(AF_UNIX, SOCK_STREAM)
@@ -174,19 +187,19 @@ if (USE_UNIX_SOCKETS):
_connection_class = UnixHTTPConnection
class UnixTransport(TransportMixIn, XMLTransport):
+
def make_connection(self, host):
- import httplib
host, extra_headers, x509 = self.get_host_info(host)
return UnixHTTP(host)
-
+
class ServerProxy(XMLServerProxy):
"""
Unfortunately, much more of this class has to be copied since
so much of it does the serialization.
"""
- def __init__(self, uri, transport=None, encoding=None,
+ def __init__(self, uri, transport=None, encoding=None,
verbose=0, version=None):
import urllib
if not version:
@@ -205,7 +218,7 @@ class ServerProxy(XMLServerProxy):
self.__host, self.__handler = urllib.splithost(uri)
if not self.__handler:
# Not sure if this is in the JSON spec?
- #self.__handler = '/'
+ # self.__handler = '/'
self.__handler == '/'
if transport is None:
if schema == 'unix':
@@ -241,13 +254,13 @@ class ServerProxy(XMLServerProxy):
request,
verbose=self.__verbose
)
-
+
# Here, the XMLRPC library translates a single list
# response to the single value -- should we do the
# same, and require a tuple / list to be passed to
- # the response object, or expect the Server to be
+ # the response object, or expect the Server to be
# outputting the response appropriately?
-
+
history.add_response(response)
if not response:
return None
@@ -265,11 +278,12 @@ class ServerProxy(XMLServerProxy):
class _Method(XML_Method):
-
+
def __call__(self, *args, **kwargs):
if len(args) > 0 and len(kwargs) > 0:
- raise ProtocolError('Cannot use both positional ' +
- 'and keyword arguments (according to JSON-RPC spec.)')
+ raise ProtocolError(
+ 'Cannot use both positional and keyword arguments '
+ '(according to JSON-RPC spec.)')
if len(args) > 0:
return self.__send(self.__name, args)
else:
@@ -287,17 +301,20 @@ class _Method(XML_Method):
def __dir__(self):
return self.__dict__.keys()
+
class _Notify(object):
def __init__(self, request):
self._request = request
def __getattr__(self, name):
return _Method(self._request, name)
-
+
+
# Batch implementation
+
class MultiCallMethod(object):
-
+
def __init__(self, method, notify=False):
self.method = method
self.params = []
@@ -318,14 +335,15 @@ class MultiCallMethod(object):
def __repr__(self):
return '%s' % self.request()
-
+
def __getattr__(self, method):
new_method = '%s.%s' % (self.method, method)
self.method = new_method
return self
+
class MultiCallNotify(object):
-
+
def __init__(self, multicall):
self.multicall = multicall
@@ -334,8 +352,9 @@ class MultiCallNotify(object):
self.multicall._job_list.append(new_job)
return new_job
+
class MultiCallIterator(object):
-
+
def __init__(self, results):
self.results = results
@@ -352,8 +371,9 @@ class MultiCallIterator(object):
def __len__(self):
return len(self.results)
+
class MultiCall(object):
-
+
def __init__(self, server):
self._server = server
self._job_list = []
@@ -362,8 +382,8 @@ class MultiCall(object):
if len(self._job_list) < 1:
# Should we alert? This /is/ pretty obvious.
return
- request_body = '[ %s ]' % ','.join([job.request() for
- job in self._job_list])
+ request_body = '[ {0} ]'.format(
+ ','.join([job.request() for job in self._job_list]))
responses = self._server._run_request(request_body)
del self._job_list[:]
if not responses:
@@ -381,19 +401,21 @@ class MultiCall(object):
__call__ = _request
-# These lines conform to xmlrpclib's "compatibility" line.
+# These lines conform to xmlrpclib's "compatibility" line.
# Not really sure if we should include these, but oh well.
Server = ServerProxy
+
class Fault(object):
# JSON-RPC error class
+
def __init__(self, code=-32000, message='Server error', rpcid=None):
self.faultCode = code
self.faultString = message
self.rpcid = rpcid
def error(self):
- return {'code':self.faultCode, 'message':self.faultString}
+ return {'code': self.faultCode, 'message': self.faultString}
def response(self, rpcid=None, version=None):
if not version:
@@ -407,25 +429,27 @@ class Fault(object):
def __repr__(self):
return '<Fault %s: %s>' % (self.faultCode, self.faultString)
+
def random_id(length=8):
return_id = ''
for i in range(length):
return_id += random.choice(IDCHARS)
return return_id
+
class Payload(dict):
def __init__(self, rpcid=None, version=None):
if not version:
version = config.version
self.id = rpcid
self.version = float(version)
-
+
def request(self, method, params=[]):
if type(method) not in types.StringTypes:
raise ValueError('Method name must be a string.')
if not self.id:
self.id = random_id()
- request = { 'id':self.id, 'method':method }
+ request = {'id': self.id, 'method': method}
if params:
request['params'] = params
if self.version >= 2:
@@ -441,7 +465,7 @@ class Payload(dict):
return request
def response(self, result=None):
- response = {'result':result, 'id':self.id}
+ response = {'result': result, 'id': self.id}
if self.version >= 2:
response['jsonrpc'] = str(self.version)
else:
@@ -454,13 +478,15 @@ class Payload(dict):
del error['result']
else:
error['result'] = None
- error['error'] = {'code':code, 'message':message}
+ error['error'] = {'code': code, 'message': message}
return error
-def dumps(params=[], methodname=None, methodresponse=None,
+
+def dumps(
+ params=[], methodname=None, methodresponse=None,
encoding=None, rpcid=None, version=None, notify=None):
"""
- This differs from the Python implementation in that it implements
+ This differs from the Python implementation in that it implements
the rpcid argument since the 2.0 spec requires it for responses.
"""
if not version:
@@ -469,7 +495,7 @@ def dumps(params=[], methodname=None, methodresponse=None,
if methodname in types.StringTypes and \
type(params) not in valid_params and \
not isinstance(params, Fault):
- """
+ """
If a method, and params are not in a listish or a Fault,
error out.
"""
@@ -482,10 +508,14 @@ def dumps(params=[], methodname=None, methodresponse=None,
if type(params) is Fault:
response = payload.error(params.faultCode, params.faultString)
return jdumps(response, encoding=encoding)
- if type(methodname) not in types.StringTypes and methodresponse != True:
- raise ValueError('Method name must be a string, or methodresponse '+
- 'must be set to True.')
- if config.use_jsonclass == True:
+
+ if type(methodname) not in types.StringTypes and \
+ methodresponse is not True:
+ raise ValueError(
+ 'Method name must be a string, or methodresponse must '
+ 'be set to True.')
+
+ if config.use_jsonclass is True:
from jsonrpclib import jsonclass
params = jsonclass.dump(params)
if methodresponse is True:
@@ -494,12 +524,13 @@ def dumps(params=[], methodname=None, methodresponse=None,
response = payload.response(params)
return jdumps(response, encoding=encoding)
request = None
- if notify == True:
+ if notify is True:
request = payload.notify(methodname, params)
else:
request = payload.request(methodname, params)
return jdumps(request, encoding=encoding)
+
def loads(data):
"""
This differs from the Python implementation, in that it returns
@@ -510,36 +541,39 @@ def loads(data):
# notification
return None
result = jloads(data)
- # if the above raises an error, the implementing server code
+ # if the above raises an error, the implementing server code
# should return something like the following:
# { 'jsonrpc':'2.0', 'error': fault.error(), id: None }
- if config.use_jsonclass == True:
+ if config.use_jsonclass is True:
from jsonrpclib import jsonclass
result = jsonclass.load(result)
return result
+
def check_for_errors(result):
if not result:
# Notification
return result
- if type(result) is not types.DictType:
+
+ if not isinstance(result, dict):
raise TypeError('Response is not a dict.')
if 'jsonrpc' in result.keys() and float(result['jsonrpc']) > 2.0:
raise NotImplementedError('JSON-RPC version not yet supported.')
if 'result' not in result.keys() and 'error' not in result.keys():
raise ValueError('Response does not have a result or error key.')
- if 'error' in result.keys() and result['error'] != None:
+ if 'error' in result.keys() and result['error'] is not None:
code = result['error']['code']
message = result['error']['message']
raise ProtocolError((code, message))
return result
+
def isbatch(result):
if type(result) not in (types.ListType, types.TupleType):
return False
if len(result) < 1:
return False
- if type(result[0]) is not types.DictType:
+ if not isinstance(result[0], dict):
return False
if 'jsonrpc' not in result[0].keys():
return False
@@ -551,11 +585,12 @@ def isbatch(result):
return False
return True
+
def isnotification(request):
if 'id' not in request.keys():
# 2.0 notification
return True
- if request['id'] == None:
+ if request['id'] is None:
# 1.0 notification
return True
return False
diff --git a/setup.py b/setup.py
index 72f8b35..e318f44 100755
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,7 @@ import distutils.core
distutils.core.setup(
name = "jsonrpclib",
- version = "0.1.3",
+ version = "0.1.4",
packages = ["jsonrpclib"],
author = "Josh Marshall",
author_email = "catchjosh@gmail.com",
diff --git a/tests.py b/tests.py
index 3ce1009..f64efc7 100644
--- a/tests.py
+++ b/tests.py
@@ -1,16 +1,11 @@
"""
The tests in this file compare the request and response objects
to the JSON-RPC 2.0 specification document, as well as testing
-several internal components of the jsonrpclib library. Run this
+several internal components of the jsonrpclib library. Run this
module without any parameters to run the tests.
-Currently, this is not easily tested with a framework like
-nosetests because we spin up a daemon thread running the
-the Server, and nosetests (at least in my tests) does not
-ever "kill" the thread.
-
If you are testing jsonrpclib and the module doesn't return to
-the command prompt after running the tests, you can hit
+the command prompt after running the tests, you can hit
"Ctrl-C" (or "Ctrl-Break" on Windows) and that should kill it.
TODO:
@@ -19,36 +14,47 @@ TODO:
* Implement JSONClass, History, Config tests
"""
-from jsonrpclib import Server, MultiCall, history, config, ProtocolError
-from jsonrpclib import jsonrpc
-from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
-from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCRequestHandler
-import socket
-import tempfile
-import unittest
-import os
-import time
+
try:
import json
except ImportError:
import simplejson as json
+import os
+import socket
+import sys
+import tempfile
from threading import Thread
-PORTS = range(8000, 8999)
+if sys.version_info < (2, 7):
+ import unittest2 as unittest
+else:
+ import unittest
+
+from jsonrpclib import Server, MultiCall, history, ProtocolError
+from jsonrpclib import jsonrpc
+from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer
+from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCRequestHandler
+
+
+def get_port(family=socket.AF_INET):
+ sock = socket.socket(family, socket.SOCK_STREAM)
+ sock.bind(("localhost", 0))
+ return sock.getsockname()[1]
+
class TestCompatibility(unittest.TestCase):
-
+
client = None
port = None
server = None
-
+
def setUp(self):
- self.port = PORTS.pop()
+ self.port = get_port()
self.server = server_set_up(addr=('', self.port))
self.client = Server('http://localhost:%d' % self.port)
-
+
# v1 tests forthcoming
-
+
# Version 2.0 Tests
def test_positional(self):
""" Positional arguments in a single call """
@@ -59,7 +65,7 @@ class TestCompatibility(unittest.TestCase):
request = json.loads(history.request)
response = json.loads(history.response)
verify_request = {
- "jsonrpc": "2.0", "method": "subtract",
+ "jsonrpc": "2.0", "method": "subtract",
"params": [42, 23], "id": request['id']
}
verify_response = {
@@ -67,7 +73,7 @@ class TestCompatibility(unittest.TestCase):
}
self.assertTrue(request == verify_request)
self.assertTrue(response == verify_response)
-
+
def test_named(self):
""" Named arguments in a single call """
result = self.client.subtract(subtrahend=23, minuend=42)
@@ -77,8 +83,8 @@ class TestCompatibility(unittest.TestCase):
request = json.loads(history.request)
response = json.loads(history.response)
verify_request = {
- "jsonrpc": "2.0", "method": "subtract",
- "params": {"subtrahend": 23, "minuend": 42},
+ "jsonrpc": "2.0", "method": "subtract",
+ "params": {"subtrahend": 23, "minuend": 42},
"id": request['id']
}
verify_response = {
@@ -86,101 +92,103 @@ class TestCompatibility(unittest.TestCase):
}
self.assertTrue(request == verify_request)
self.assertTrue(response == verify_response)
-
+
def test_notification(self):
""" Testing a notification (response should be null) """
result = self.client._notify.update(1, 2, 3, 4, 5)
- self.assertTrue(result == None)
+ self.assertTrue(result is None)
request = json.loads(history.request)
response = history.response
verify_request = {
- "jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]
+ "jsonrpc": "2.0", "method": "update", "params": [1, 2, 3, 4, 5]
}
verify_response = ''
self.assertTrue(request == verify_request)
self.assertTrue(response == verify_response)
-
+
def test_non_existent_method(self):
- self.assertRaises(ProtocolError, self.client.foobar)
+ with self.assertRaises(ProtocolError):
+ self.client.foobar()
+
request = json.loads(history.request)
response = json.loads(history.response)
verify_request = {
"jsonrpc": "2.0", "method": "foobar", "id": request['id']
}
verify_response = {
- "jsonrpc": "2.0",
- "error":
- {"code": -32601, "message": response['error']['message']},
+ "jsonrpc": "2.0",
+ "error":
+ {"code": -32601, "message": response['error']['message']},
"id": request['id']
}
self.assertTrue(request == verify_request)
self.assertTrue(response == verify_response)
-
+
def test_invalid_json(self):
- invalid_json = '{"jsonrpc": "2.0", "method": "foobar, '+ \
+ invalid_json = '{"jsonrpc": "2.0", "method": "foobar, ' + \
'"params": "bar", "baz]'
response = self.client._run_request(invalid_json)
response = json.loads(history.response)
verify_response = json.loads(
- '{"jsonrpc": "2.0", "error": {"code": -32700,'+
+ '{"jsonrpc": "2.0", "error": {"code": -32700,' +
' "message": "Parse error."}, "id": null}'
)
verify_response['error']['message'] = response['error']['message']
self.assertTrue(response == verify_response)
-
+
def test_invalid_request(self):
invalid_request = '{"jsonrpc": "2.0", "method": 1, "params": "bar"}'
response = self.client._run_request(invalid_request)
response = json.loads(history.response)
verify_response = json.loads(
- '{"jsonrpc": "2.0", "error": {"code": -32600, '+
+ '{"jsonrpc": "2.0", "error": {"code": -32600, ' +
'"message": "Invalid Request."}, "id": null}'
)
verify_response['error']['message'] = response['error']['message']
self.assertTrue(response == verify_response)
-
+
def test_batch_invalid_json(self):
- invalid_request = '[ {"jsonrpc": "2.0", "method": "sum", '+ \
- '"params": [1,2,4], "id": "1"},{"jsonrpc": "2.0", "method" ]'
+ invalid_request = '[ {"jsonrpc": "2.0", "method": "sum", ' + \
+ '"params": [1, 2, 4], "id": "1"},{"jsonrpc": "2.0", "method" ]'
response = self.client._run_request(invalid_request)
response = json.loads(history.response)
verify_response = json.loads(
- '{"jsonrpc": "2.0", "error": {"code": -32700,'+
+ '{"jsonrpc": "2.0", "error": {"code": -32700,' +
'"message": "Parse error."}, "id": null}'
)
verify_response['error']['message'] = response['error']['message']
self.assertTrue(response == verify_response)
-
+
def test_empty_array(self):
invalid_request = '[]'
response = self.client._run_request(invalid_request)
response = json.loads(history.response)
verify_response = json.loads(
- '{"jsonrpc": "2.0", "error": {"code": -32600, '+
+ '{"jsonrpc": "2.0", "error": {"code": -32600, ' +
'"message": "Invalid Request."}, "id": null}'
)
verify_response['error']['message'] = response['error']['message']
self.assertTrue(response == verify_response)
-
+
def test_nonempty_array(self):
- invalid_request = '[1,2]'
+ invalid_request = '[1, 2]'
request_obj = json.loads(invalid_request)
response = self.client._run_request(invalid_request)
response = json.loads(history.response)
self.assertTrue(len(response) == len(request_obj))
for resp in response:
verify_resp = json.loads(
- '{"jsonrpc": "2.0", "error": {"code": -32600, '+
+ '{"jsonrpc": "2.0", "error": {"code": -32600, ' +
'"message": "Invalid Request."}, "id": null}'
)
verify_resp['error']['message'] = resp['error']['message']
self.assertTrue(resp == verify_resp)
-
+
def test_batch(self):
multicall = MultiCall(self.client)
- multicall.sum(1,2,4)
+ multicall.sum(1, 2, 4)
multicall._notify.notify_hello(7)
- multicall.subtract(42,23)
+ multicall.subtract(42, 23)
multicall.foo.get(name='myself')
multicall.get_data()
job_requests = [j.request() for j in multicall._job_list]
@@ -188,38 +196,44 @@ class TestCompatibility(unittest.TestCase):
json_requests = '[%s]' % ','.join(job_requests)
requests = json.loads(json_requests)
responses = self.client._run_request(json_requests)
-
+
verify_requests = json.loads("""[
- {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
+ {"jsonrpc": "2.0", "method": "sum",
+ "params": [1, 2, 4], "id": "1"},
{"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
- {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
- {"foo": "boo"},
- {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
- {"jsonrpc": "2.0", "method": "get_data", "id": "9"}
+ {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23],
+ "id": "2"}, {"foo": "boo"},
+ {"jsonrpc": "2.0", "method": "foo.get",
+ "params": {"name": "myself"}, "id": "5"},
+ {"jsonrpc": "2.0", "method": "get_data", "id": "9"}
]""")
-
+
# Thankfully, these are in order so testing is pretty simple.
verify_responses = json.loads("""[
{"jsonrpc": "2.0", "result": 7, "id": "1"},
{"jsonrpc": "2.0", "result": 19, "id": "2"},
- {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request."}, "id": null},
- {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found."}, "id": "5"},
+ {"jsonrpc": "2.0",
+ "error": {"code": -32600, "message": "Invalid Request."},
+ "id": null},
+ {"jsonrpc": "2.0",
+ "error": {"code": -32601, "message": "Method not found."},
+ "id": "5"},
{"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]""")
-
+
self.assertTrue(len(requests) == len(verify_requests))
self.assertTrue(len(responses) == len(verify_responses))
-
+
responses_by_id = {}
response_i = 0
-
+
for i in range(len(requests)):
verify_request = verify_requests[i]
request = requests[i]
response = None
if request.get('method') != 'notify_hello':
req_id = request.get('id')
- if verify_request.has_key('id'):
+ if "id" in verify_request:
verify_request['id'] = req_id
verify_response = verify_responses[response_i]
verify_response['id'] = req_id
@@ -227,23 +241,23 @@ class TestCompatibility(unittest.TestCase):
response_i += 1
response = verify_response
self.assertTrue(request == verify_request)
-
+
for response in responses:
verify_response = responses_by_id.get(response.get('id'))
- if verify_response.has_key('error'):
+ if "error" in verify_response:
verify_response['error']['message'] = \
response['error']['message']
self.assertTrue(response == verify_response)
-
- def test_batch_notifications(self):
+
+ def test_batch_notifications(self):
multicall = MultiCall(self.client)
multicall._notify.notify_sum(1, 2, 4)
multicall._notify.notify_hello(7)
result = multicall()
self.assertTrue(len(result) == 0)
valid_request = json.loads(
- '[{"jsonrpc": "2.0", "method": "notify_sum", '+
- '"params": [1,2,4]},{"jsonrpc": "2.0", '+
+ '[{"jsonrpc": "2.0", "method": "notify_sum", ' +
+ '"params": [1, 2, 4]},{"jsonrpc": "2.0", ' +
'"method": "notify_hello", "params": [7]}]'
)
request = json.loads(history.request)
@@ -253,23 +267,24 @@ class TestCompatibility(unittest.TestCase):
valid_req = valid_request[i]
self.assertTrue(req == valid_req)
self.assertTrue(history.response == '')
-
+
+
class InternalTests(unittest.TestCase):
- """
- These tests verify that the client and server portions of
+ """
+ These tests verify that the client and server portions of
jsonrpclib talk to each other properly.
- """
+ """
client = None
server = None
port = None
-
+
def setUp(self):
- self.port = PORTS.pop()
+ self.port = get_port()
self.server = server_set_up(addr=('', self.port))
-
+
def get_client(self):
return Server('http://localhost:%d' % self.port)
-
+
def get_multicall_client(self):
server = self.get_client()
return MultiCall(server)
@@ -278,33 +293,34 @@ class InternalTests(unittest.TestCase):
client = self.get_client()
result = client.ping()
self.assertTrue(result)
-
+
def test_single_args(self):
client = self.get_client()
result = client.add(5, 10)
self.assertTrue(result == 15)
-
+
def test_single_kwargs(self):
client = self.get_client()
result = client.add(x=5, y=10)
self.assertTrue(result == 15)
-
+
def test_single_kwargs_and_args(self):
client = self.get_client()
- self.assertRaises(ProtocolError, client.add, (5,), {'y':10})
-
+ with self.assertRaises(ProtocolError):
+ client.add(5, y=10)
+
def test_single_notify(self):
client = self.get_client()
result = client._notify.add(5, 10)
- self.assertTrue(result == None)
-
+ self.assertTrue(result is None)
+
def test_single_namespace(self):
client = self.get_client()
- response = client.namespace.sum(1,2,4)
+ response = client.namespace.sum(1, 2, 4)
request = json.loads(history.request)
response = json.loads(history.response)
verify_request = {
- "jsonrpc": "2.0", "params": [1, 2, 4],
+ "jsonrpc": "2.0", "params": [1, 2, 4],
"id": "5", "method": "namespace.sum"
}
verify_response = {
@@ -314,25 +330,18 @@ class InternalTests(unittest.TestCase):
verify_response['id'] = request['id']
self.assertTrue(verify_request == request)
self.assertTrue(verify_response == response)
-
+
def test_multicall_success(self):
multicall = self.get_multicall_client()
multicall.ping()
multicall.add(5, 10)
- multicall.namespace.sum([5, 10, 15])
+ multicall.namespace.sum(5, 10, 15)
correct = [True, 15, 30]
i = 0
for result in multicall():
self.assertTrue(result == correct[i])
i += 1
-
- def test_multicall_success(self):
- multicall = self.get_multicall_client()
- for i in range(3):
- multicall.add(5, i)
- result = multicall()
- self.assertTrue(result[2] == 7)
-
+
def test_multicall_failure(self):
multicall = self.get_multicall_client()
multicall.ping()
@@ -345,87 +354,96 @@ class InternalTests(unittest.TestCase):
else:
def func():
return result[i]
- self.assertRaises(raises[i], func)
-
-
+ with self.assertRaises(raises[i]):
+ func()
+
+
if jsonrpc.USE_UNIX_SOCKETS:
# We won't do these tests unless Unix Sockets are supported
-
+
+ @unittest.skip("Skipping Unix socket tests right now.")
class UnixSocketInternalTests(InternalTests):
"""
- These tests run the same internal communication tests,
+ These tests run the same internal communication tests,
but over a Unix socket instead of a TCP socket.
"""
def setUp(self):
- suffix = "%d.sock" % PORTS.pop()
-
- # Open to safer, alternative processes
+ suffix = "%d.sock" % get_port()
+
+ # Open to safer, alternative processes
# for getting a temp file name...
temp = tempfile.NamedTemporaryFile(
suffix=suffix
)
self.port = temp.name
temp.close()
-
+
self.server = server_set_up(
- addr=self.port,
+ addr=self.port,
address_family=socket.AF_UNIX
)
def get_client(self):
return Server('unix:/%s' % self.port)
-
+
def tearDown(self):
""" Removes the tempory socket file """
os.unlink(self.port)
-
+
+
class UnixSocketErrorTests(unittest.TestCase):
- """
- Simply tests that the proper exceptions fire if
+ """
+ Simply tests that the proper exceptions fire if
Unix sockets are attempted to be used on a platform
that doesn't support them.
"""
-
+
def setUp(self):
self.original_value = jsonrpc.USE_UNIX_SOCKETS
if (jsonrpc.USE_UNIX_SOCKETS):
jsonrpc.USE_UNIX_SOCKETS = False
-
+
def test_client(self):
address = "unix://shouldnt/work.sock"
- self.assertRaises(
- jsonrpc.UnixSocketMissing,
- Server,
- address
- )
-
+ with self.assertRaises(jsonrpc.UnixSocketMissing):
+ Server(address)
+
def tearDown(self):
jsonrpc.USE_UNIX_SOCKETS = self.original_value
-
+
""" Test Methods """
+
+
def subtract(minuend, subtrahend):
""" Using the keywords from the JSON-RPC v2 doc """
return minuend-subtrahend
-
+
+
def add(x, y):
return x + y
-
+
+
def update(*args):
return args
-
+
+
def summation(*args):
return sum(args)
-
+
+
def notify_hello(*args):
return args
-
+
+
def get_data():
return ['hello', 5]
-
+
+
def ping():
return True
-
+
+
def server_set_up(addr, address_family=socket.AF_INET):
# Not sure this is a good idea to spin up a new server thread
# for each test... but it seems to work fine.
@@ -447,10 +465,3 @@ def server_set_up(addr, address_family=socket.AF_INET):
server_proc.daemon = True
server_proc.start()
return server_proc
-
-if __name__ == '__main__':
- print "==============================================================="
- print " NOTE: There may be threading exceptions after tests finish. "
- print "==============================================================="
- time.sleep(2)
- unittest.main()