The JSON-RPC protocol
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
Protocol implementation
API Reference
- class tinyrpc.protocols.jsonrpc.JSONRPCProtocol(id_generator: Generator[object, None, None] | None = None, *args, **kwargs)
Bases:
RPCBatchProtocolJSONRPC protocol implementation.
- JSON_RPC_VERSION = '2.0'
Currently, only version 2.0 is supported.
- request_factory() JSONRPCRequest
Factory for request objects.
Allows derived classes to use requests derived from
JSONRPCRequest.- Return type:
- create_batch_request(requests: JSONRPCRequest | List[JSONRPCRequest] = None) JSONRPCBatchRequest
Create a new
JSONRPCBatchRequestobject.Called by the client when constructing a request.
- Parameters:
requests (
listorJSONRPCRequest) – A list of requests.- Returns:
A new request instance.
- Return type:
- create_request(method: str, args: List[Any] = None, kwargs: Dict[str, Any] = None, one_way: bool = False) JSONRPCRequest
Creates a new
JSONRPCRequestobject.Called by the client when constructing a request. JSON RPC allows either the
argsorkwargsargument to be set.- 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:
- Raises:
InvalidRequestError – when
argsandkwargsare both defined.
- parse_reply(data: bytes) JSONRPCSuccessResponse | JSONRPCErrorResponse
De-serializes and validates a response.
Called by the client to reconstruct the serialized
JSONRPCResponse.- Parameters:
data (bytes) – The data stream received by the transport layer containing the serialized request.
- Returns:
A reconstructed response.
- Return type:
- Raises:
InvalidReplyError – if the response is not valid JSON or does not conform to the standard.
- parse_request(data: bytes) JSONRPCRequest | JSONRPCBatchRequest
De-serializes and validates a request.
Called by the server to reconstruct the serialized
JSONRPCRequest.- Parameters:
data (bytes) – The data stream received by the transport layer containing the serialized request.
- Returns:
A reconstructed request.
- Return type:
- Raises:
JSONRPCParseError – if the
datacannot be parsed as valid JSON.JSONRPCInvalidRequestError – if the request does not comply with the standard.
- raise_error(error: JSONRPCErrorResponse | Dict[str, Any]) JSONRPCError
Recreates the exception.
Creates a
JSONRPCErrorinstance and raises it. This allows the error, message and data attributes of the original exception to propagate into the client code.The
raises_errorflag controls if the exception object is raised or returned.- Returns:
the exception object if it is not allowed to raise it.
- Raises:
JSONRPCError – when the exception can be raised. The exception object will contain
message,codeand optionally adataproperty.
- class tinyrpc.protocols.jsonrpc.JSONRPCRequest
Bases:
RPCRequestDefines a JSON RPC request.
- one_way
Request or Notification.
- Type:
bool
This flag indicates if the client expects to receive a reply (request:
one_way = False) or not (notification:one_way = True).Note that according to the specification it is possible for the server to return an error response. For example if the request becomes unreadable and the server is not able to determine that it is in fact a notification an error should be returned. However, once the server had verified that the request is a notification no reply (not even an error) should be returned.
- unique_id
Correlation ID used to match request and response.
- Type:
int or str
Generated by the client, the server copies it from request to corresponding response.
- method
The name of the RPC function to be called.
- Type:
str
The
methodattribute uses the name of the function as it is known by the public. TheRPCDispatcherallows the use of public aliases in the@publicdecorators. These are the names used in themethodattribute.
- args
The positional arguments of the method call.
- Type:
list
The contents of this list are the positional parameters for the
methodcalled. It is eventually called asmethod(*args).
- kwargs
The keyword arguments of the method call.
- Type:
dict
The contents of this dict are the keyword parameters for the
methodcalled. It is eventually called asmethod(**kwargs).
- error_respond(error: Exception | str) JSONRPCErrorResponse | None
Create an error response to this request.
When processing the request produces an error condition this method can be used to create the error response object.
- Parameters:
error (Exception or str) – Specifies what error occurred.
- Returns:
An error response object that can be serialized and sent to the client.
- Return type:
;py:class:JSONRPCErrorResponse
- respond(result: Any) JSONRPCSuccessResponse | None
Create a response to this request.
When processing the request completed successfully this method can be used to create a response object.
- Parameters:
result (Anything that can be encoded by JSON.) – The result of the invoked method.
- Returns:
A response object that can be serialized and sent to the client.
- Return type:
- serialize() bytes
Returns a serialization of the request.
Converts the request into a bytes object that can be sent to the server.
- Returns:
The serialized encoded request object.
- Return type:
bytes
- class tinyrpc.protocols.jsonrpc.JSONRPCSuccessResponse
Bases:
RPCResponseCollects the attributes of a successful response message.
Contains the fields of a normal (i.e. a non-error) response message.
- unique_id
Correlation ID to match request and response. A JSON RPC response must have a defined matching id attribute.
Noneis not a valid value for a successful response.- Type:
str or int
- result
Contains the result of the RPC call.
- Type:
Any type that can be serialized by the protocol.
- 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.
- Returns:
The serialized encoded response object.
- Return type:
bytes
- class tinyrpc.protocols.jsonrpc.JSONRPCErrorResponse
Bases:
RPCErrorResponseCollects the attributes of an error response message.
Contains the fields of an error response message.
- unique_id
Correlation ID to match request and response.
Noneis a valid ID when the error cannot be matched to a particular request.- Type:
str or int or None
- error
The error message. A string describing the error condition.
- Type:
str
- data
This field may contain any JSON encodable datum that the server may want to return the client.
It may contain additional information about the error condition, a partial result or whatever. Its presence and value are entirely optional.
- Type:
Any type that can be serialized by the protocol.
- _jsonrpc_error_code
The numeric error code.
The value is usually predefined by one of the JSON protocol exceptions. It can be set by the developer when defining application specific exceptions. See
FixedErrorMessageMixinfor an example on how to do this.Note that the value of this field must comply with the defined values in the standard.
- serialize() bytes
Returns a serialization of the error.
Converts the response into a bytes object that can be passed to and by the transport layer.
- Returns:
The serialized encoded error object.
- Return type:
bytes
Batch protocol
API Reference
- class tinyrpc.protocols.jsonrpc.JSONRPCBatchRequest(iterable=(), /)
Bases:
RPCBatchRequestDefines a JSON RPC batch request.
- create_batch_response() JSONRPCBatchResponse | None
Produces a batch response object if a response is expected.
- Returns:
A batch response if needed
- Return type:
- 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.
- Returns:
A bytes object to be passed on to a transport.
- Return type:
bytes
- class tinyrpc.protocols.jsonrpc.JSONRPCBatchResponse(iterable=(), /)
Bases:
RPCBatchResponseMultiple responses from a batch request. See
JSONRPCBatchRequeston how to handle.Items in a batch response need to be
JSONRPCResponseinstances or None, meaning no reply should be 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.
- Returns:
A bytes object to be passed on to a transport.
- Return type:
bytes
Errors and error handling
API Reference
- class tinyrpc.protocols.jsonrpc.FixedErrorMessageMixin(*args, **kwargs)
Bases:
objectCombines JSON RPC exceptions with the generic RPC exceptions.
Constructs the exception using the provided parameters as well as properties of the JSON RPC Exception.
JSON RPC exceptions declare two attributes:
- jsonrpc_error_code
This is an error code conforming to the JSON RPC error codes convention.
- Type:
int
- message
This is a textual representation of the error code.
- Type:
str
- Parameters:
args (list) – Positional arguments for the constructor. When present it overrules the
messageattribute.kwargs (dict) – Keyword arguments for the constructor. If the
dataparameter is found inkwargsits contents are used as the data property of the JSON RPC Error object.
FixedErrorMessageMixinis the basis for adding your own exceptions to the predefined ones. Here is a version of the reverse string example that dislikes palindromes:class PalindromeError(FixedErrorMessageMixin, Exception) jsonrpc_error_code = 99 message = "Ah, that's cheating" @public def reverse_string(s): r = s[::-1] if r == s: raise PalindromeError(data=s) return r
>>> client.reverse('rotator')
Will return an error object to the client looking like:
{ "jsonrpc": "2.0", "id": 1, "error": { "code": 99, "message": "Ah, that's cheating", "data": "rotator" } }
- error_respond() JSONRPCErrorResponse
Converts the error to an error response object.
- Returns:
An error response object ready to be serialized and sent to the client.
- Return type:
- class tinyrpc.protocols.jsonrpc.JSONRPCParseError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,InvalidRequestErrorThe request cannot be decoded or is malformed.
- class tinyrpc.protocols.jsonrpc.JSONRPCInvalidRequestError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,InvalidRequestErrorThe request contents are not valid for JSON RPC 2.0
- class tinyrpc.protocols.jsonrpc.JSONRPCMethodNotFoundError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,MethodNotFoundErrorThe requested method name is not found in the registry.
- class tinyrpc.protocols.jsonrpc.JSONRPCInvalidParamsError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,InvalidRequestErrorThe provided parameters are not appropriate for the function called.
- class tinyrpc.protocols.jsonrpc.JSONRPCInternalError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,InvalidRequestErrorUnspecified error, not in the called function.
- class tinyrpc.protocols.jsonrpc.JSONRPCServerError(*args, **kwargs)
Bases:
FixedErrorMessageMixin,InvalidRequestErrorUnspecified error, this message originates from the called function.
- class tinyrpc.protocols.jsonrpc.JSONRPCError(error: JSONRPCErrorResponse | Dict[str, Any])
Bases:
FixedErrorMessageMixin,RPCErrorReconstructs (to some extend) the server-side exception.
The client creates this exception by providing it with the
errorattribute of the JSON error response object returned by the server.- Parameters:
error (dict) –
This dict contains the error specification:
code (int): the numeric error code.
message (str): the error description.
data (any): if present, the data attribute of the error
Adding custom exceptions
Note
As per the specification you should use error codes -32000 to -32099 when adding server specific error messages. Error codes outside the range -32768 to -32000 are available for application specific error codes.
To add custom errors you need to combine an Exception subclass
with the FixedErrorMessageMixin class to
create your exception object which you can raise.
So a version of the reverse string example that dislikes palindromes could look like:
from tinyrpc.protocols.jsonrpc import FixedErrorMessageMixin, JSONRPCProtocol
from tinyrpc.dispatch import RPCDispatcher
dispatcher = RPCDispatcher()
class PalindromeError(FixedErrorMessageMixin, Exception):
jsonrpc_error_code = 99
message = "Ah, that's cheating!"
@dispatcher.public
def reverse_string(s):
r = s[::-1]
if r == s:
raise PalindromeError()
return r
Error with data
The specification states that the error element of a reply may contain
an optional data property. This property is now available for your use.
There are two ways that you can use to pass additional data with an Exception.
It depends whether your application generates regular exceptions or exceptions derived
from FixedErrorMessageMixin.
When using ordinary exceptions you normally pass a single parameter (an error message)
to the Exception constructor.
By passing two parameters, the second parameter is assumed to be the data element.
@public
def fn():
raise Exception('error message', {'msg': 'structured data', 'lst': [1, 2, 3]})
This will produce the reply message:
{ "jsonrpc": "2.0",
"id": <some id>,
"error": {
"code": -32000,
"message": "error message",
"data": {"msg": "structured data", "lst": [1, 2, 3]}
}
}
When using FixedErrorMessageMixin based exceptions the data is passed using
a keyword parameter.
class MyException(FixedErrorMessageMixin, Exception):
jsonrcp_error_code = 99
message = 'standard message'
@public
def fn():
raise MyException(data={'msg': 'structured data', 'lst': [1, 2, 3]})
This will produce the reply message:
{ "jsonrpc": "2.0",
"id": <some id>,
"error": {
"code": 99,
"message": "standard message",
"data": {"msg": "structured data", "lst": [1, 2, 3]}
}
}