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.
- 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.
A new RPCRequest instance.
Parses a reply and returns an RPCResponse instance.
Returns: An instanced response.
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¶
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.
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.
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.
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.
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.
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.
Some protocols may support batch requests. In this case, they need to derive from RPCBatchProtocol.
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¶
- 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.
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.
The following example shows how to use the JSONRPCProtocol class in a custom application, without using any other components:
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)
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