>>> server = jsonrpclib.Server('http://localhost:8181')
>>> server.add(5, 6)
11
->>> jsonrpclib.__notify('add', (5, 6))
+>>> server._notify.add(5, 6)
+>>> batch = jsonrpclib.MultiCall(server)
+>>> batch.add(3, 50)
+>>> batch.add(2, 3)
+>>> batch._notify.add(3, 5)
+>>> batch()
+[53, 5]
See http://code.google.com/p/jsonrpclib/ for more info.
"""
response = file_h.read(1024)
if not response:
break
+ response_body += response
if self.verbose:
print 'body: %s' % response
- response_body += response
+ if response_body == '':
+ # Notification
+ return None
return_obj = loads(response_body)
return return_obj
self.__encoding = encoding
self.__verbose = verbose
- def __request(self, methodname, params, rpcid=None):
+ def _request(self, methodname, params, rpcid=None):
request = dumps(params, methodname, encoding=self.__encoding,
rpcid=rpcid, version=self.__version)
- response = self.__run_request(request)
+ response = self._run_request(request)
+ check_for_errors(response)
return response['result']
-
- def __notify(self, methodname, params, rpcid=None):
+
+ def _request_notify(self, methodname, params, rpcid=None):
request = dumps(params, methodname, encoding=self.__encoding,
rpcid=rpcid, version=self.__version, notify=True)
- response = self.__run_request(request, notify=True)
+ response = self._run_request(request, notify=True)
+ check_for_errors(response)
return
- def __run_request(self, request, notify=None):
+ def _run_request(self, request, notify=None):
global _last_request
global _last_response
_last_request = request
-
- if notify is True:
- _last_response = None
- return None
response = self.__transport.request(
self.__host,
return response
def __getattr__(self, name):
- # Same as original, just with new _Method and wrapper
- # for __notify
- if name in ('__notify', '__run_request'):
- wrapped_name = '_%s%s' % (self.__class__.__name__, name)
- return getattr(self, wrapped_name)
- return _Method(self.__request, name)
+ # Same as original, just with new _Method reference
+ return _Method(self._request, name)
+
+ @property
+ def _notify(self):
+ # Just like __getattr__, but with notify namespace.
+ return _Notify(self._request_notify)
+
class _Method(XML_Method):
+
def __call__(self, *args, **kwargs):
if len(args) > 0 and len(kwargs) > 0:
raise ProtocolError('Cannot use both positional ' +
else:
return self.__send(self.__name, kwargs)
+class _Notify(object):
+ def __init__(self, request):
+ self._request = request
+
+ def __getattr__(self, name):
+ return _Method(self._request, name)
+
# Batch implementation
-class Job(object):
+class MultiCallMethod(object):
def __init__(self, method, notify=False):
self.method = method
def __call__(self, *args, **kwargs):
if len(kwargs) > 0 and len(args) > 0:
- raise ProtocolError('A Job cannot have both positional ' +
- 'and keyword arguments.')
+ raise ProtocolError('JSON-RPC does not support both ' +
+ 'positional and keyword arguments.')
if len(kwargs) > 0:
self.params = kwargs
else:
def __repr__(self):
return '%s' % self.request()
-class BatchServerProxy(ServerProxy):
+class MultiCallNotify(object):
+ def __getattr__(self, name):
+ return MultiCallMethod(name, notify=True)
+
+class MultiCallIterator(object):
- def __init__(self, uri, *args, **kwargs):
- self.__job_list = []
- ServerProxy.__init__(self, uri, *args, **kwargs)
+ def __init__(self, results):
+ self.results = results
+
+ def __iter__(self):
+ for i in range(0, len(self.results)):
+ yield self[i]
+ raise StopIteration
+
+ def __getitem__(self, i):
+ item = self.results[i]
+ check_for_errors(item)
+ return item['result']
- def __run_request(self, request_body):
- run_request = getattr(ServerProxy, '_ServerProxy__run_request')
- return run_request(self, request_body)
+ def __len__(self):
+ return len(self.results)
- def __request(self):
+class MultiCall(object):
+
+ def __init__(self, server):
+ self.__server = server
+ self.__job_list = []
+
+ def _request(self):
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])
- responses = self.__run_request(request_body)
+ responses = self.__server._run_request(request_body)
del self.__job_list[:]
- return [ response['result'] for response in responses ]
+ return MultiCallIterator(responses)
- def __notify(self, method, params):
- new_job = Job(method, notify=True)
- self.__job_list.append(new_job)
+ @property
+ def _notify(self):
+ return MultiCallNotify()
def __getattr__(self, name):
- if name in ('__run', '__notify'):
- wrapped_name = '_%s%s' % (self.__class__.__name__, name)
- return getattr(self, wrapped_name)
- new_job = Job(name)
+ new_job = MultiCallMethod(name)
self.__job_list.append(new_job)
return new_job
- __run = __request
+ __call__ = _request
# These lines conform to xmlrpclib's "compatibility" line.
# Not really sure if we should include these, but oh well.
Server = ServerProxy
-BatchServer = BatchServerProxy
-
-def run(batch):
- """
- This method is just a caller for the __run() on the actual
- BatchServer itself. Useful only for those who don't like
- calling __ methods. :)
- """
- batch.__run()
-
-
-class Fault(dict):
+class Fault(object):
# JSON-RPC error class
def __init__(self, code=-32000, message='Server error'):
- self.code = code
- self.message = message
+ self.faultCode = code
+ self.faultString = message
def error(self):
- return {'code':self.code, 'message':self.message}
+ return {'code':self.faultCode, 'message':self.faultString}
def response(self, rpcid=None, version=None):
global _version
if not version:
version = _version
- return dumps(self, rpcid=None, methodresponse=True,
- version=version)
+ return dumps(self, rpcid=rpcid, version=version)
+
+ def __repr__(self):
+ return '<Fault %s: %s>' % (self.faultCode, self.faultString)
def random_id(length=8):
import string
"""
global _version
if not version:
- verion = _version
+ version = _version
valid_params = (types.TupleType, types.ListType, types.DictType)
if methodname in types.StringTypes and \
type(params) not in valid_params and \
"""
raise TypeError('Params must be a dict, list, tuple or Fault ' +
'instance.')
- 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 isinstance(params, Fault) and not methodresponse:
- raise TypeError('You can only use a Fault for responses.')
# Begin parsing object
payload = Payload(rpcid=rpcid, version=version)
if not encoding:
encoding = 'utf-8'
if type(params) is Fault:
- response = payload.error(params.code, params.message)
+ 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 methodresponse is True:
if rpcid is None:
raise ValueError('A method response must have an rpcid.')
the request structure in Dict format instead of the method, params.
It will return a list in the case of a batch request / response.
"""
+ if data == '':
+ # notification
+ return None
result = jloads(data)
# if the above raises an error, the implementing server code
# should return something like the following:
# { 'jsonrpc':'2.0', 'error': fault.error(), id: None }
-
- result_list = []
- if not isbatch(result):
- result_list.append(result)
- else:
- result_list = result
- for entry in result_list:
- if 'jsonrpc' in entry.keys() and float(entry['jsonrpc']) > 2.0:
- raise NotImplementedError('JSON-RPC version not yet supported.')
- if 'error' in entry.keys() and entry['error'] != None:
- code = entry['error']['code']
- message = entry['error']['message']
- raise ProtocolError('ERROR %s: %s' % (code, message))
- del result_list
+ return result
+
+def check_for_errors(result):
+ if not result:
+ # Notification
+ return result
+ if type(result) is not types.DictType:
+ 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:
+ code = result['error']['code']
+ message = result['error']['message']
+ raise ProtocolError('ERROR %s: %s' % (code, message))
return result
def isbatch(result):