jsonrpc — JSON RPC Client/server

Note

This is a Json RPC library by Roland Koebler and the documentation is included here only for developer reference.

JSON-RPC (remote procedure call).

It consists of 3 (independent) parts:
  • proxy/dispatcher
  • data structure / serializer
  • transport

It’s intended for JSON-RPC, but since the above 3 parts are independent, it could be used for other RPCs as well.

Currently, JSON-RPC 2.0 and JSON-RPC 1.0 are implemented

version:

2017-12-03-RELEASE

status:

experimental

example:

simple Client with JsonRPC 2.0 and TCP/IP:

>>> proxy = ServerProxy( JsonRpc20(), TransportTcpIp(addr=("127.0.0.1",31415)) )
>>> proxy.echo( "hello world" )
u'hello world'
>>> proxy.echo( "bye." )
u'bye.'

simple Server with JsonRPC2.0 and TCP/IP with logging to STDOUT:

>>> server = Server( JsonRpc20(), TransportTcpIp(addr=("127.0.0.1",31415), logfunc=log_stdout) )
>>> def echo( s ):
...   return s
>>> server.register_function( echo )
>>> server.serve( 2 )   # serve 2 requests          
listen ('127.0.0.1', 31415)
('127.0.0.1', ...) connected
('127.0.0.1', ...) <-- {"jsonrpc": "2.0", "method": "echo", "params": ["hello world"], "id": 0}
('127.0.0.1', ...) --> {"jsonrpc": "2.0", "result": "hello world", "id": 0}
('127.0.0.1', ...) close
('127.0.0.1', ...) connected
('127.0.0.1', ...) <-- {"jsonrpc": "2.0", "method": "echo", "params": ["bye."], "id": 0}
('127.0.0.1', ...) --> {"jsonrpc": "2.0", "result": "bye.", "id": 0}
('127.0.0.1', ...) close
close ('127.0.0.1', 31415)

Client with JsonRPC2.0 and an abstract Unix Domain Socket:

>>> proxy = ServerProxy( JsonRpc20(), TransportUnixSocket(addr="\x00.rpcsocket") )
>>> proxy.hi( message="hello" )         #named parameters
u'hi there'
>>> proxy.test()                        #fault
Traceback (most recent call last):
  ...
jsonrpc.RPCMethodNotFound: <RPCFault -32601: u'Method not found.' (None)>
>>> proxy.debug.echo( "hello world" )   #hierarchical procedures
u'hello world'

Server with JsonRPC2.0 and abstract Unix Domain Socket with a logfile:

>>> server = Server( JsonRpc20(), TransportUnixSocket(addr="\x00.rpcsocket", logfunc=log_file("mylog.txt")) )
>>> def echo( s ):
...   return s
>>> def hi( message ):
...   return "hi there"
>>> server.register_function( hi )
>>> server.register_function( echo, name="debug.echo" )
>>> server.serve( 3 )   # serve 3 requests

"mylog.txt" then contains:
listen '\x00.rpcsocket'
'' connected
'' --> '{"jsonrpc": "2.0", "method": "hi", "params": {"message": "hello"}, "id": 0}'
'' <-- '{"jsonrpc": "2.0", "result": "hi there", "id": 0}'
'' close
'' connected
'' --> '{"jsonrpc": "2.0", "method": "test", "id": 0}'
'' <-- '{"jsonrpc": "2.0", "error": {"code":-32601, "message": "Method not found."}, "id": 0}'
'' close
'' connected
'' --> '{"jsonrpc": "2.0", "method": "debug.echo", "params": ["hello world"], "id": 0}'
'' <-- '{"jsonrpc": "2.0", "result": "hello world", "id": 0}'
'' close
close '\x00.rpcsocket'
note:

all exceptions derived from RPCFault are propagated to the client. other exceptions are logged and result in a sent-back “empty” INTERNAL_ERROR.

uses:

logging, sys, json, codecs, time, socket, select

seealso:

JSON-RPC 2.0 proposal, 1.0 specification

warning:

Warning

This is experimental code!

author:

Dave Pedu (dave(at)davepedu.com)

changelog:
  • 2008-08-31: 1st release
  • 2017-12-03-RELEASE Modern python 3.0 rewrite
todo:
  • server: multithreading rpc-server
  • client: multicall (send several requests)
  • transport: SSL sockets, maybe HTTP, HTTPS
  • types: support for date/time (ISO 8601)
  • errors: maybe customizable error-codes/exceptions
  • mixed 1.0/2.0 server ?
  • system description etc. ?
  • maybe test other json-serializers, like cjson?
class pyircbot.jsonrpc.JsonRpc10(dumps=<function dumps>, loads=<function loads>)[source]

Bases: object

JSON-RPC V1.0 data-structure / serializer

This implementation is quite liberal in what it accepts: It treats missing “params” and “id” in Requests and missing “result”/”error” in Responses as empty/null.

Seealso:JSON-RPC 1.0 specification
Todo:catch json.dumps not-serializable-exceptions
dumps_error(error, id=None)[source]

serialize a JSON-RPC-Response-error

Since JSON-RPC 1.0 does not define an error-object, this uses the JSON-RPC 2.0 error-object.

Parameters:error (RPCFault) – an RPCFault instance
Returns:str like {“result”: null, “error”: {“code”: code, “message”: message, “data”: data}, “id”: …}. “result”, “error” and “id” are always in this order, data is omitted if None.
Raises:ValueError if error is not a RPCFault instance, TypeError if not JSON-serializable
dumps_notification(method, params=())[source]

serialize a JSON-RPC-Notification

Parameters:
  • method (str) – the method-name
  • params (list, tuple) – the parameters
Returns:

str like {“method”: “…”, “params”: …, “id”: null}. “method”, “params” and “id” are always in this order.

Raises:

TypeError if method/params is of wrong type or not JSON-serializable

dumps_request(method, params=(), id=0)[source]

serialize JSON-RPC-Request

Parameters:
  • method (str) – the method-name
  • params (list, tuple) – the parameters
  • id – if id isNone, this results in a Notification
Returns:

str like`{“method”: “…”, “params”: …, “id”: …}`. “method”, “params” and “id” are always in this order.

Raises:

TypeError if method/params is of wrong type or not JSON-serializable

dumps_response(result, id=None)[source]

serialize a JSON-RPC-Response (without error)

Returns:str like {“result”: …, “error”: null, “id”: …}. “result”, “error” and “id” are always in this order.
Raises:TypeError if not JSON-serializable
loads_request(string)[source]

de-serialize a JSON-RPC Request/Notification

Returns:list like [method_name, params, id] or [method_name, params]. params is a tuple/list. if id is missing, this is a Notification
Raises:RPCParseError, RPCInvalidRPC, RPCInvalidMethodParams
loads_response(string)[source]

de-serialize a JSON-RPC Response/error

Returns:list like [result, id] for Responses
Raises:
RPCFault+derivates for error-packages/faults, RPCParseError, RPCInvalidRPC
Note:error-packages which do not match the V2.0-definition, RPCFault(-1, “Error”, RECEIVED_ERROR_OBJ) is instead raised.
class pyircbot.jsonrpc.JsonRpc20(dumps=<function dumps>, loads=<function loads>)[source]

Bases: object

JSON-RPC V2.0 data-structure / serializer

See:JSON-RPC 2.0 specification
Todo:catch simplejson.dumps not-serializable-exceptions
Todo:rewrite serializer as modern java encoder subclass? support for more types this way?
dumps_error(error, id=None)[source]

serialize a JSON-RPC-Response-error

Parameters:error (RPCFault) – error to serialize
Returns:str like {“jsonrpc”: “2.0”, “error”: {“code”: code, “message”: message, “data”: data}, “id”: …}. “jsonrpc”, “result”, “error” and “id” are always in this order, data is omitted if None.
Raises:ValueError if error is not a RPCFault instance, TypeError if not JSON-serializable
dumps_notification(method, params=())[source]

serialize a JSON-RPC-Notification

Parameters:
  • method – name of the method to call
  • params (dict,list,tuple) – data structure of args
Returns:

String like {“jsonrpc”: “2.0”, “method”: “…”, “params”: …}. “jsonrpc”, “method” and “params” are always in this order.

Raises:

see dumps_request

dumps_request(method, params=(), id=0)[source]

serialize a JSON-RPC-Request to string

Parameters:
  • method – name of the method to call
  • params (dict,list,tuple) – data structure of args

: type id: request id (should not be None) :returns: string like: {“jsonrpc”: “2.0”, “method”: “…”, “params”: …, “id”: …}. “jsonrpc”, “method”,

“params” and “id” are always in this order. “params” is omitted if empty
Raises:TypeError if method/params is of wrong type or not JSON-serializable
dumps_response(result, id=None)[source]

serialize a JSON-RPC-Response (without error)

Returns:str like {“jsonrpc”: “2.0”, “result”: …, “id”: …}.”jsonrpc”, “result”, and “id” are always in this order.
Raises:TypeError if not JSON-serializable
loads_request(string)[source]

de-serialize a JSON-RPC Request or Notification

Returns:[method_name, params, id] or [method_name, params]. params is a tuple/list or dict (with only str-keys). if id is missing, this is a Notification
Raises:RPCParseError, RPCInvalidRPC, RPCInvalidMethodParams
loads_response(string)[source]

de-serialize a JSON-RPC Response/error

Returns:[result, id] for Responses
Raises:RPCFault+derivates for error-packages/faults, RPCParseError, RPCInvalidRPC
exception pyircbot.jsonrpc.RPCAuthentificationError(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

AUTHENTIFICATION_ERROR

exception pyircbot.jsonrpc.RPCError[source]

Bases: Exception

Base class for rpc-errors.

exception pyircbot.jsonrpc.RPCFault(error_code, error_message, error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCError

RPC error/fault package received.

This exception can also be used as a class, to generate a RPC-error/fault message.

Variables:
  • error_code: the RPC error-code
  • error_string: description of the error
  • error_data: optional additional information
    (must be json-serializable)
TODO:

improve __str__

exception pyircbot.jsonrpc.RPCInternalError(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Internal error. (INTERNAL_ERROR)

exception pyircbot.jsonrpc.RPCInvalidMethodParams(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Invalid method-parameters. (INVALID_METHOD_PARAMS)

exception pyircbot.jsonrpc.RPCInvalidParamValues(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

INVALID_PARAM_VALUES

exception pyircbot.jsonrpc.RPCInvalidRPC(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Invalid rpc-package. (INVALID_REQUEST)

exception pyircbot.jsonrpc.RPCMethodNotFound(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Method not found. (METHOD_NOT_FOUND)

exception pyircbot.jsonrpc.RPCParseError(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Broken rpc-package. (PARSE_ERROR)

exception pyircbot.jsonrpc.RPCPermissionDenied(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

PERMISSION_DENIED

exception pyircbot.jsonrpc.RPCProcedureException(error_data=None)[source]

Bases: pyircbot.jsonrpc.RPCFault

Procedure exception. (PROCEDURE_EXCEPTION)

exception pyircbot.jsonrpc.RPCTimeoutError[source]

Bases: pyircbot.jsonrpc.RPCTransportError

Transport/reply timeout.

exception pyircbot.jsonrpc.RPCTransportError[source]

Bases: pyircbot.jsonrpc.RPCError

Transport error.

class pyircbot.jsonrpc.Server(data_serializer, transport, logfile=None)[source]

Bases: object

RPC server.

It works with different data/serializers and with different transports.

Example:

see module-docstring

TODO:
  • mixed JSON-RPC 1.0/2.0 server?
  • logging/loglevels?
handle(rpcstr)[source]

Handle a RPC Request.

Parameters:rpcstr (str) – the received rpc message
Returns:the data to send back or None if nothing should be sent back
Raises:RPCFault (and maybe others)
log(message)[source]

write a message to the logfile :param message: log message to write :type message: str

register_function(function, name=None)[source]

Add a function to the RPC-services.

Parameters:
  • function – callable to add
  • name (str) – RPC-name for the function. If omitted/None, the original name of the function is used.
register_instance(myinst, name=None)[source]

Add all functions of a class-instance to the RPC-services.

All entries of the instance which do not begin with ‘_’ are added.

Parameters:
  • myinst – class-instance containing the functions
  • name – hierarchical prefix. If omitted, the functions are added directly. If given, the functions are added as “name.function”.
Todo:
  • only add functions and omit attributes?
  • improve hierarchy?
serve(n=None)[source]

Run the server (forever or for n communicaions).

See:Transport
class pyircbot.jsonrpc.ServerProxy(data_serializer, transport)[source]

Bases: object

RPC-client: server proxy

A client-side logical connection to a RPC server.

It works with different data/serializers and different transports.

Notifications and id-handling/multicall are not yet implemented.

Example:see module-docstring
Todo:verbose/logging?
class pyircbot.jsonrpc.Transport[source]

Bases: object

generic Transport-interface.

This class, and especially its methods and docstrings, define the Transport-Interface.

recv()[source]

receive data. must be implemented by derived classes. :return str:

send(data)[source]

send all data. must be implemented by derived classes. :param data: data to send :type data: str

sendrecv(string)[source]

send + receive data :param string: message to send :type string: str

serve(handler, n=None)[source]

serve (forever or for n communicaions).

  • receive data
  • call result = handler(data)
  • send back result if not None

The serving can be stopped by SIGINT.

TODO:
  • how to stop? maybe use a .run-file, and stop server if file removed?
  • maybe make n_current accessible? (e.g. for logging)
class pyircbot.jsonrpc.TransportSTDINOUT[source]

Bases: pyircbot.jsonrpc.Transport

receive from STDIN, send to STDOUT. Useful e.g. for debugging.

recv()[source]

read data from STDIN

send(string)[source]

write data to STDOUT with ‘***SEND:’ prefix

class pyircbot.jsonrpc.TransportSocket(addr, limit=4096, sock_type=<AddressFamily.AF_INET: 2>, sock_prot=<SocketKind.SOCK_STREAM: 1>, timeout=1.0, logfunc=<function log_dummy>)[source]

Bases: pyircbot.jsonrpc.Transport

Transport via socket.

TODO:
  • documentation
  • improve this (e.g. make sure that connections are closed, socket-files are deleted etc.)
  • exception-handling? (socket.error)
close()[source]
connect()[source]
recv()[source]
send(string)[source]
sendrecv(string)[source]

send data + receive data + close

serve(handler, n=None)[source]

open socket, wait for incoming connections and handle them.

Parameters:
  • n: serve n requests, None=forever
class pyircbot.jsonrpc.TransportTcpIp(addr=None, limit=4096, timeout=1.0, logfunc=<function log_dummy>)[source]

Bases: pyircbot.jsonrpc.TransportSocket

Transport via TCP/IP.

class pyircbot.jsonrpc.TransportUnixSocket(addr=None, limit=4096, timeout=1.0, logfunc=<function log_dummy>)[source]

Bases: pyircbot.jsonrpc.TransportSocket

Transport via Unix Domain Socket.

pyircbot.jsonrpc.log_dummy(message)[source]
pyircbot.jsonrpc.log_file(filename)[source]

return a logfunc which logs to a file (in utf-8)

pyircbot.jsonrpc.log_filedate(filename)[source]

return a logfunc which logs date+message to a file (in utf-8)

pyircbot.jsonrpc.log_stdout(message)[source]

print message to STDOUT