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.
-
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 once instance of RPCProtocol should be used per client.
-
These require implementations of the following classes as well:
-
class
tinyrpc.
RPCRequest
¶ -
args
= []¶ The positional arguments of the method call.
-
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.
-
kwargs
= {}¶ The keyword arguments of the method call.
-
method
= None¶ The name of the method to be called.
-
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.
-
unique_id
= None¶ A unique ID to remember the request by. Protocol specific, may or may not be set. This value should only be set by
create_request()
.The ID allows client to receive responses out-of-order and still allocate them to the correct request.
Only supported if the parent protocol has
supports_out_of_order
set toTrue
.
-
-
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 attributeerror
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
orNone
, 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