The protocol layer

Any protocol is implemented by deriving from RPCProtocol and implementing all of its members:

class tinyrpc.RPCProtocol

Base class for all protocol implementations.

create_request(method, args=None, kwargs=None, one_way=False)

Creates a new RPCRequest object.

It is up to the implementing protocol whether or not args, kwargs, one of these, both at once or none of them are supported.

Parameters:
  • method – The method name to invoke.
  • args – The positional arguments to call the method with.
  • kwargs – The keyword arguments to call the method with.
  • one_way – The request is an update, i.e. it does not expect a reply.
Returns:

A new RPCRequest instance.

parse_reply(data)

Parses a reply and returns an RPCResponse instance.

Returns:An instanced response.
parse_request(data)

Parses a request given as a string and returns an RPCRequest instance.

Returns:An instanced request.

These require implementations of the following classes as well:

class tinyrpc.RPCRequest
error_respond(error)

Creates an error response.

Create a response indicating that the request was parsed correctly, but an error has occured trying to fulfill it.

Parameters:error – An exception or a string describing the error.
Returns:A response or None to indicate that no error should be sent out.
respond(result)

Create a response.

Call this to return the result of a successful method invocation.

This creates and returns an instance of a protocol-specific subclass of RPCResponse.

Parameters:result – Passed on to new response instance.
Returns:A response or None to indicate this request does not expect a response.
serialize()

Returns a serialization of the request.

Returns:A string to be passed on to a transport.
class tinyrpc.RPCResponse

RPC call response class.

Base class for all deriving responses.

Has an attribute result containing the result of the RPC call, unless an error occured, in which case an attribute error will contain the error message.

serialize()

Returns a serialization of the response.

Returns:A reply to be passed on to a transport.
class tinyrpc.BadRequestError

Base class for all errors that caused the processing of a request to abort before a request object could be instantiated.

error_respond()

Create RPCErrorResponse to respond the error.

Returns:A error responce instance or None, if the protocol decides to drop the error silently.

Every protocol deals with multiple kinds of structures: data arguments are always byte strings, either messages or replies, that are sent via or received from a transport.

There are two protocol-specific subclasses of RPCRequest and RPCResponse, these represent well-formed requests and responses.

Finally, if an error occurs during parsing of a request, a BadRequestError instance must be thrown. These need to be subclassed for each protocol as well, since they generate error replies.

Batch protocols

Some protocols may support batch requests. In this case, they need to derive from RPCBatchProtocol.

Batch protocols differ in that their parse_request() method may return an instance of RPCBatchRequest. They also possess an addional method in create_batch_request().

Handling a batch request is slightly different, while it supports error_respond(), to make actual responses, create_batch_response() needs to be used.

No assumptions are made whether or not it is okay for batch requests to be handled in parallel. This is up to the server/dispatch implementation, which must be chosen appropriately.

class tinyrpc.RPCBatchProtocol
create_batch_request(requests=None)

Create a new tinyrpc.RPCBatchRequest object.

Parameters:requests – A list of requests.
class tinyrpc.RPCBatchRequest

Multiple requests batched together.

A batch request is a subclass of list. Protocols that support multiple requests in a single message use this to group them together.

Handling a batch requests is done in any order, responses must be gathered in a batch response and be in the same order as their respective requests.

Any item of a batch request is either a request or a subclass of BadRequestError, which indicates that there has been an error in parsing the request.

create_batch_response()

Creates a response suitable for responding to this request.

Returns:An RPCBatchResponse or None, if no response is expected.
class tinyrpc.RPCBatchResponse

Multiple response from a batch request. See RPCBatchRequest on how to handle.

Items in a batch response need to be RPCResponse instances or None, meaning no reply should generated for the request.

serialize()

Returns a serialization of the batch response.

Supported protocols

Any supported protocol is used by instantiating its class and calling the interface of RPCProtocol. Note that constructors are not part of the interface, any protocol may have specific arguments for its instances.

Protocols usually live in their own module because they may need to import optional modules that needn’t be a dependency for all of tinyrpc.

Example

The following example shows how to use the JSONRPCProtocol class in a custom application, without using any other components:

Server

from tinyrpc.protocols.jsonrpc import JSONRPCProtocol
from tinyrpc import BadRequestError, RPCBatchRequest

rpc = JSONRPCProtocol()

# the code below is valid for all protocols, not just JSONRPC:

def handle_incoming_message(self, data):
    try:
        request = rpc.parse_request(data)
    except BadRequestError as e:
        # request was invalid, directly create response
        response = e.error_respond(e)
    else:
        # we got a valid request
        # the handle_request function is user-defined
        # and returns some form of response
        if hasattr(request, create_batch_response):
            response = request.create_batch_response(
                handle_request(req) for req in request
            )
        else:
            response = handle_request(request)

    # now send the response to the client
    if response != None:
         send_to_client(response.serialize())


def handle_request(request):
    try:
        # do magic with method, args, kwargs...
        return request.respond(result)
    except Exception as e:
        # for example, a method wasn't found
        return request.error_respond(e)

Client

from tinyrpc.protocols.jsonrpc import JSONRPCProtocol

rpc = JSONRPCProtocol()

# again, code below is protocol-independent

# assuming you want to call method(*args, **kwargs)

request = rpc.create_request(method, args, kwargs)
reply = send_to_server_and_get_reply(request)

response = rpc.parse_reply(reply)

if hasattr(response, 'error'):
    # error handling...
else:
    # the return value is found in response.result
    do_something_with(response.result)

Another example, this time using batch requests:

# or using batch requests:

requests = rpc.create_batch_request([
    rpc.create_request(method_1, args_1, kwargs_1)
    rpc.create_request(method_2, args_2, kwargs_2)
    # ...
])

reply = send_to_server_and_get_reply(request)

responses = rpc.parse_reply(reply)

for responses in response:
    if hasattr(reponse, 'error'):
        # ...

Finally, one-way requests are requests where the client does not expect an answer:

request = rpc.create_request(method, args, kwargs, one_way=True)
send_to_server(request)

# done

JSON-RPC

class tinyrpc.protocols.jsonrpc.JSONRPCProtocol(*args, **kwargs)

JSONRPC protocol implementation.

Currently, only version 2.0 is supported.