Errors¶
Base Classes¶
This library includes a flexible error-handling system (inherited from the underlying
sansios-jsonrpc
project). JSON-RPC exceptions can be thrown and caught like any
other Python exceptions. They all inherit from one base class:
-
class
trio_jsonrpc.
JsonRpcException
(error: sansio_jsonrpc.exc.JsonRpcError)¶ A base class for JSON-RPC exceptions.
This exception is never thrown by
sansio-jsonrpc
but since it is an ancestor for JSON-RPC exceptions, it is useful in try/except blocks as a catch-all. You generally should not instantiate this class yourself. Instead, use class:JsonRpcApplicationException, either directly or by creating your own subclass of it.-
code
¶ The JSON-RPC error code.
-
data
¶ Arbitrary data attached to the error.
-
staticmethod
exc_from_error
(error: sansio_jsonrpc.exc.JsonRpcError) → sansio_jsonrpc.exc.JsonRpcException¶ Create a new exception derived from the given error.
When you receive an error response, you may want to raise a corresponding exception. This method finds an appropriate exception subclass that matches the error’s numeric code and instantiates it. It uses some metaclass black magic so that it can even return custom subclasses you define in your own code!
-
get_error
() → sansio_jsonrpc.exc.JsonRpcError¶ Return the error underlying this exception.
-
message
¶ A JSON-RPC error message.
-
Each exception contains a numeric error code, a string message, and optional arbitrary
data. You generally should not instantiate JsonRpcException
, but it is useful as a
catch-all in try/except
blocks.
To communicate an error to the remote peer, a slightly different but related error object is used:
-
class
trio_jsonrpc.
JsonRpcError
(code: int, message: str, data: typing.Optional[JsonDict] = None)¶ Represents an error in the JSON RPC protocol.
-
classmethod
from_json_dict
(json_dict: Dict[str, Union[int, float, str, dict, list, None]]) → sansio_jsonrpc.exc.JsonRpcError¶ Return a new response from a JSON dictionary.
-
to_json_dict
() → Dict[str, Union[int, float, str, dict, list, None]]¶ Convert to a JSON dictionary.
-
classmethod
This error object can be passed into JsonRpcConnection.respond_with_error()
.
An exception object can be converted into an error by calling exc.get_error()
. An
error object can be converted into an exception by calling
JsonRpcException.exc_from_error(err)
. This class method uses some metaclass magic to
find the correct exception subclass and instantiate it. For example, if the error code
is -32601
, then the method will raise JsonRpcMethodNotFoundError
.
Built-in Exceptions¶
The library includes a hierarchy of built-in exceptions.
JsonRpcException
+-- JsonRpcReservedError
+-- JsonRpcInternalError
+-- JsonRpcInvalidRequestError
+-- JsonRpcInvalidParamsError
+-- JsonRpcMethodNotFoundError
+-- JsonRpcParseError
+-- JsonRpcApplicationError
The top-most class JsonRpcException
was discussed in the previous section. It has
two direct subclasses. JsonRpcReservedError
covers all of the error codes defined in
or reserved by the JSON-RPC 2.0 specification.
Custom Errors¶
The JSON-RPC specification allows implementers to specify their own JSON-RPC error codes
as long as they do not rely in the range of values reserved by the specification. This
capability is exposed in this library through the JsonRpcApplicationError
object.
There are two ways to use this class.
-
class
trio_jsonrpc.
JsonRpcApplicationError
(message: Optional[str] = None, *, data: Optional[Dict[str, Union[int, float, str, dict, list, None]]] = None, code: Optional[int] = None)¶ An exception corresponding to the unreserved range of error codes.
The error code must not be in the range [-32768, -32000].
The first way is to raise the exception directly and provide a custom error code.
if (some_error_condition):
raise JsonRpcApplicationError(code=1000, message='Foo error')
The second approach is to declare a custom subclass with the same error code.
class FooError(JsonRpcApplicationError):
ERROR_CODE = 1000
ERROR_MESSAGE = "Foo error"
...
if (some_error_condition):
raise FooError()
Both of these approaches will produce the same error signaling in the JSON-RPC protocol
data, i.e. the remote peer will see the same result. The latter approach is also
compatible with JsonRpcException.exc_from_error()
: if you receive a 1000
error
code from the peer, exc_from_error()
will create a FooError
!
To illustrate this point, consider the Client Example.
$ python -m example.client ws://localhost:8000 john 1234 transfer jane 10
INFO:client:Login success=True
INFO:client:Current balance: 90
INFO:client:New balance: 80
$ python -m example.client ws://localhost:8000 john 1234 transfer jane 100
INFO:client:Login success=True
INFO:client:Current balance: 80
Traceback (most recent call last):
File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/mhaase/code/hyperiongray/trio-jsonrpc/example/client.py", line 70, in <module>
trio.run(main, args)
File "/home/mhaase/.cache/pypoetry/virtualenvs/trio-jsonrpc-rezRQjTp-py3.7/lib/python3.7/site-packages/trio/_core/_run.py", line 1804, in run
raise runner.main_task_outcome.error
File "/home/mhaase/code/hyperiongray/trio-jsonrpc/example/client.py", line 34, in main
await args.func(client, args)
File "/home/mhaase/code/hyperiongray/trio-jsonrpc/example/client.py", line 21, in transfer
await client.request("transfer", [args.dest_account, args.amount])
File "/home/mhaase/code/hyperiongray/trio-jsonrpc/trio_jsonrpc/main.py", line 79, in request
raise JsonRpcException.exc_from_error(response.error)
example.shared.InsufficientFundsError: Insufficient funds
In the first command, we see that the user’s final balance is 80. In the second command,
they try to transfer 100. The server raises InsufficientFundsError
and then the
client catches the same type of exception. Under the hood, the server converts the
exception into a JSON-RPC error using the error code specified in
InsufficientFundsError
class. The client reads the error code from the JSON-RPC
response, looks up the error class with the same error code, and raises it.