The protocol layer

Interface definition

All protocols are implemented by deriving from RPCProtocol and implementing all of its members.

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.

Protocol-specific subclasses of RPCRequest and RPCResponse represent well-formed requests and responses.

Protocol specific subclasses of RPCErrorResponse represent errors and error 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.

API Reference

class tinyrpc.protocols.RPCProtocol

Bases: abc.ABC

Abstract base class for all protocol implementations.

supports_out_of_order = False

If true, this protocol can receive responses out of order correctly.

Note that this usually depends on the generation of unique_ids, the generation of these may or may not be thread safe, depending on the protocol. Ideally, only one instance of RPCProtocol should be used per client.

Type:bool
raises_errors = True

If True, this protocol instance will raise an RPCError exception.

On receipt of an RPCErrorResponse instance an RPCError exception is raised. When this flag is False the RPCErrorResponse object is returned to the caller which is then responsible for handling the error.

Type:bool
create_request(method: str, args: List[Any] = None, kwargs: Dict[str, Any] = None, one_way: bool = False) → tinyrpc.protocols.RPCRequest

Creates a new RPCRequest object.

Called by the client when constructing a request. 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 (str) – The method name to invoke.
  • args (list) – The positional arguments to call the method with.
  • kwargs (dict) – The keyword arguments to call the method with.
  • one_way (bool) – The request is an update, i.e. it does not expect a reply.
Returns:

A new request instance

Return type:

RPCRequest

parse_request(data: bytes) → tinyrpc.protocols.RPCRequest

De-serializes and validates a request.

Called by the server to reconstruct the serialized RPCRequest.

Parameters:data (bytes) – The data stream received by the transport layer containing the serialized request.
Returns:A reconstructed request.
Return type:RPCRequest
parse_reply(data: bytes) → tinyrpc.protocols.RPCResponse

De-serializes and validates a response.

Called by the client to reconstruct the serialized RPCResponse.

Parameters:data (bytes) – The data stream received by the transport layer containing the serialized response.
Returns:A reconstructed response.
Return type:RPCResponse
raise_error(error: tinyrpc.protocols.RPCErrorResponse) → tinyrpc.exc.RPCError

Raises the exception in the client.

Called by the client to convert the RPCErrorResponse into an Exception and raise or return it depending on the raises_errors attribute.

Parameters:error (RPCResponse) – The error response received from the server.
Return type:RPCError when raises_errors is False.
Raises:RPCError when raises_errors is True.
class tinyrpc.protocols.RPCRequest

Bases: object

Defines a generic RPC request.

unique_id = None

Correlation ID used to match request and response.

Type:int or str or None

Protocol specific, may or may not be set. This value should only be set by create_request().

When the protocol permits it this ID allows servers to respond to requests out of order and allows clients to relate a response to the corresponding request.

Only supported if the protocol has its supports_out_of_order set to True.

Generated by the client, the server copies it from request to corresponding response.

method = None

The name of the RPC function to be called.

Type:str

The method attribute uses the name of the function as it is known by the public. The RPCDispatcher allows the use of public aliases in the @public decorators. These are the names used in the method attribute.

args = None

The positional arguments of the method call.

Type:list

The contents of this list are the positional parameters for the method called. It is eventually called as method(*args).

kwargs = None

The keyword arguments of the method call.

Type:dict

The contents of this dict are the keyword parameters for the method called. It is eventually called as method(**kwargs).

error_respond(error: Union[Exception, str]) → Optional[tinyrpc.protocols.RPCErrorResponse]

Creates an error response.

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

This is an abstract method that must be overridden in a derived class.

Parameters:error (Exception or str) – An exception or a string describing the error.
Returns:A response or None to indicate that no error should be sent out.
Return type:RPCErrorResponse
respond(result: Any) → Optional[tinyrpc.protocols.RPCResponse]

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.

This is an abstract method that must be overridden in a derived class.

Parameters:result (Any type that can be serialized by the protocol.) – Passed on to new response instance.
Returns:A response or None to indicate this request does not expect a response.
Return type:RPCResponse
serialize() → bytes

Returns a serialization of the request.

Converts the request into a bytes object that can be passed to and by the transport layer.

This is an abstract method that must be overridden in a derived class.

Returns:A bytes object to be passed on to a transport.
Return type:bytes
class tinyrpc.protocols.RPCResponse

Bases: abc.ABC

Defines a generic RPC response.

Base class for all responses.

id

Correlation ID to match request and response

Type:str or int
result

When present this attribute contains the result of the RPC call. Otherwise the error attribute must be defined.

Type:Any type that can be serialized by the protocol.
error

When present the result attribute must be absent. Presence of this attribute indicates an error condition.

Type:RPCError
unique_id = None

Correlation ID used to match request and response.

Type:int or str or None
serialize() → bytes

Returns a serialization of the response.

Converts the response into a bytes object that can be passed to and by the transport layer.

This is an abstract method that must be overridden in a derived class.

Returns:The serialized encoded response object.
Return type:bytes
class tinyrpc.protocols.RPCErrorResponse

Bases: tinyrpc.protocols.RPCResponse, abc.ABC

RPC error response class.

Base class for all deriving responses.

error

This attribute contains the fields message (str) and code (int) where at least message is required to contain a value.

Type:dict
class tinyrpc.exc.BadRequestError

Bases: tinyrpc.exc.RPCError, abc.ABC

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

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.

API Reference

class tinyrpc.protocols.RPCBatchProtocol

Bases: tinyrpc.protocols.RPCProtocol, abc.ABC

Abstract base class for all batch protocol implementations.

create_batch_request(requests: List[RPCRequest] = None) → tinyrpc.protocols.RPCBatchRequest

Create a new RPCBatchRequest object.

Called by the client when constructing a request.

Parameters:requests (list or RPCRequest) – A list of requests.
Returns:A new request instance.
Return type:RPCBatchRequest
class tinyrpc.protocols.RPCBatchRequest

Bases: list

Multiple requests batched together.

Protocols that support multiple requests in a single message use this to group them together. Note that not all protocols may support batch requests.

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 an RPCRequest or an BadRequestError, which indicates that there has been an error in parsing the request.

create_batch_response() → Optional[tinyrpc.protocols.RPCBatchResponse]

Creates a response suitable for responding to this request.

This is an abstract method that must be overridden in a derived class.

Returns:An RPCBatchResponse or None if no response is expected.
Return type:RPCBatchResponse
serialize() → bytes

Returns a serialization of the request.

Converts the request into a bytes object that can be passed to and by the transport layer.

This is an abstract method that must be overridden in a derived class.

Returns:A bytes object to be passed on to a transport.
Return type:bytes
class tinyrpc.protocols.RPCBatchResponse

Bases: list

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() → bytes

Returns a serialization of the batch response.

Converts the response into a bytes object that can be passed to and by the transport layer.

This is an abstract method that must be overridden in a derived class.

Returns:A bytes object to be passed on to a transport.
Return type:bytes

ID Generators

By default, the JSONRPCProtocol and MSGPACKRPCProtocol classes generates ids as sequential integers starting at 1. If alternative id generation is needed, you may supply your own generator.

Example

The following example shows how to use alternative id generators in a protocol that supports them.

from tinyrpc.protocols.jsonrpc import JSONRPCProtocol

def collatz_generator():
    """A sample generator for demonstration purposes ONLY."""
    n = 27
    while True:
        if n % 2 != 0:
            n = 3*n + 1
        else:
            n = n / 2
        yield n

rpc = JSONRPCProtocol(id_generator=collatz_generator())

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.