gruvi.jsonrpc – JSON-RPC Client and Server

The gruvi.jsonrpc module implements a JSON-RPC client and server.

There are two version of JSON-RPC: version 1.0 and version 2.0. While these versions are similar, they are not mutually compatible. The classes exposed by this module implement both versions. The default version to use is stored in JsonRpcProtocol.default_version, and most constructors take an argument that can be used to override this.

The “batch” feature of version 2.0 is not supported. It more relevant for JSON-RPC over HTTP rather for that clients and servers that operate directly on top of a connection.

The two main classes in this module are JsonRpcClient and JsonRpcServer. The difference is only who initiates the connection at the transport level. The JSON-RPC protocol itself does not distinguish between clients and servers.

Both the client and the server can receive and respond to incoming messages. These may be method calls (more common for servers), or notifications (common for both client and servers). These incoming messages are handled by a message handler, which is mandatory for a server and optional for a client. Note that for simple blocking method callls from client to a server no message handler is needed.

Message handlers run in a separate dispatcher fiber, one per connection. This means that a client will have at most one dispatcher fiber, while a server will have exactly one fiber per connection. Because they run in a separate fiber, message handlers can call into switchpoints themselves.

REQUEST

Constant indicating a JSON-RPC request message.

RESPONSE

Constant indicating a JSON-RPC response message.

errorcode {...}

Dictionary mapping error codes to error names.

strerror(code)

Return an error message for error code.

The error code is the error.code field of a JSON-RPC response message. The JSON-RPC version 2 spec contains a list of standard error codes.

exception JsonRpcError

Exception that is raised in case of JSON-RPC errors.

error

The error field from the JSON-RPC response, if available.

class JsonRpcVersion(name)

Class that encapsulates the differences between JSON-RPC vesion 1.0 and 2.0.

name

Return the JSON-RPC version as a string, e.g. ‘1.0’.

next_id()

Return a unique message ID.

static create(version)

Return a new instance for version, which can be either ‘1.0’ or ‘2.0’.

check_message(message)

Check message and return the message type. Raise a ValueError in case of an invalid message.

create_request(method, args=None, notification=False)

Create a new request message for method with arguments args.

The optional notification argument, if nonzero, creates a notification and no response to this message is expected.

create_response(request, result=None, error=None)

Create a new response message, as a response to request.

A successful response is created when the optional error argument is not provided. In this case result specifies the result, which may be any value including None. If error is provided, an error response is created, and error should be a dict as specified by the JSON-RPC specifications.

The request argument may be explicitly set to None, in which case this is an error response that is not specific to any one request.

class JsonRpcProtocol(handler=None, version=None, timeout=None)

A JSON-RPC client and server Protocol implementation.

default_timeout = 30

Class level attribute that specifies the default timeout.

default_version = '2.0'

Class level attribute that specifies the default JSON-RPC version.

version

Return the JsonRpcVersion instance for this protocol.

switchpoint send_message(message)

Send a raw JSON-RPC message.

The message argument must be a dictionary containing a valid JSON-RPC message according to the version passed into the constructor.

switchpoint call_method(method, *args)

Call a JSON-RPC method and wait for its result.

The method is called with positional arguments args.

On success, the result field from the JSON-RPC response is returned. On error, a JsonRpcError is raised, which you can use to access the error field of the JSON-RPC response.

switchpoint send_notification(method, *args)

Send a JSON-RPC notification.

The notification method is sent with positional arguments args.

switchpoint send_response(request, result=None, error=None)

Respond to a JSON-RPC method call.

This is a response to the message in request. If error is not provided, then this is a succesful response, and the value in result, which may be None, is passed back to the client. if error is provided and not None then an error is sent back. In this case error must be a dictionary as specified by the JSON-RPC spec.

class JsonRpcClient(handler=None, version=None, timeout=None)

A JSON-RPC Client.

The handler argument specifies an optional JSON-RPC message handler. You need to supply a message handler if you want to listen to notifications or you want to respond to server-to-client method calls. If provided, the message handler it must be a callable with signature handler(message, transport, protocol).

The version and timeout argument can be used to override the default protocol version and timeout, respectively.

protocol

Return the protocol, or None if not connected.

switchpoint call_method(method, *args)

A shorthand for self.protocol.call_method().

switchpoint send_message(message)

A shorthand for self.protocol.send_message().

switchpoint send_notification(method, *args)

A shorthand for self.protocol.send_notification().

class JsonRpcServer(handler, version=None, timeout=None)

A JSON-RPC Server.

The handler argument specifies the JSON-RPC message handler. It must be a callable with signature handler(message, transport, protocol). The message handler is called in a separate dispatcher fiber (one per connection).

The version and timeout argument can be used to override the default protocol version and timeout, respectively.

Example

# Ping-pong between JSON-RPC client and server.

from gruvi.jsonrpc import JsonRpcClient, JsonRpcServer

def handler(message, transport, protocol):
    method = message.get('method')
    if method == 'ping':
        protocol.send_response(message, 'pong')

server = JsonRpcServer(handler)
server.listen(('localhost', 0))
addr = server.addresses[0]

client = JsonRpcClient()
client.connect(addr)
result = client.call_method('ping')

print('result = {}'.format(result))