Responses¶
Parsing a response¶
parse turns a deserialized response into an Ok or an Error. Both are
named tuples.
>>> from jsonrpcclient import parse
>>> parse({"jsonrpc": "2.0", "result": "pong", "id": 1})
Ok(result='pong', id=1)
>>> parse(
... {
... "jsonrpc": "2.0",
... "error": {"code": -32601, "message": "Method not found"},
... "id": 1,
... }
... )
Error(code=-32601, message='Method not found', data=None, id=1)
If you have a string or bytes, use parse_json. It calls json.loads first,
and passes any keyword arguments on to it:
>>> from decimal import Decimal
>>> from jsonrpcclient import parse_json
>>> parse_json('{"jsonrpc": "2.0", "result": "pong", "id": 1}')
Ok(result='pong', id=1)
>>> parse_json('{"jsonrpc": "2.0", "result": 1.1, "id": 1}', parse_float=Decimal)
Ok(result=Decimal('1.1'), id=1)
parse refuses a string or bytes with TypeError: Use parse_json on strings.
Ok and Error¶
Ok has two fields:
result: the server's result, as it sent itid: the id of the request it answers
Error has four:
code: the error code, such as-32601for "Method not found"message: a short descriptiondata: extra details from the server, orNoneif it sent noneid: the id of the request, orNoneif the server couldn't read it
Using the result¶
Check which one you got with isinstance:
import logging
from jsonrpcclient import Error, Ok, parse
parsed = parse({"jsonrpc": "2.0", "result": "pong", "id": 1})
if isinstance(parsed, Ok):
print(parsed.result)
elif isinstance(parsed, Error):
logging.error(parsed.message)
pong
On Python 3.10 and later you can use match:
import logging
from jsonrpcclient import Error, Ok, parse
match parse({"jsonrpc": "2.0", "result": "pong", "id": 1}):
case Ok(result=result):
print(result)
case Error(code=code, message=message):
logging.error("%s (code %s)", message, code)
pong
A type checker needs one of these checks before you use .result. See
Typing.
Batch responses¶
Parsing a list gives an iterator of responses, which you match to your requests by id:
>>> responses = parse(
... [
... {"jsonrpc": "2.0", "result": "pong", "id": 2},
... {
... "jsonrpc": "2.0",
... "error": {"code": -32601, "message": "Method not found"},
... "id": 1,
... },
... ]
... )
>>> by_id = {response.id: response for response in responses}
>>> by_id[1]
Error(code=-32601, message='Method not found', data=None, id=1)
>>> by_id[2]
Ok(result='pong', id=2)
Batches covers the cases where a batch reply isn't a list.
A response with both result and error¶
JSON-RPC 2.0 says a response has a result or an error, never both. Some
servers, mostly older JSON-RPC 1.0 style ones, send "result": null next to
an error. If error is present and not null, parse returns an Error:
>>> parse(
... {
... "jsonrpc": "2.0",
... "result": None,
... "error": {"code": -32601, "message": "Method not found"},
... "id": 1,
... }
... )
Error(code=-32601, message='Method not found', data=None, id=1)
This only works when error is an object with code and message.
JSON-RPC 1.0 didn't fix the shape of an error, so a server that sends, say, a
string as the error gets InvalidResponse instead (see below).
New in 4.1.0
Versions before 4.1.0 returned Ok(result=None, ...) here and dropped the
error.
Invalid responses¶
If a response is malformed, parse and parse_json raise InvalidResponse
with a message saying what's wrong:
>>> from jsonrpcclient import InvalidResponse
>>> parse({"jsonrpc": "2.0", "result": "pong"})
Traceback (most recent call last):
...
jsonrpcclient.responses.InvalidResponse: Invalid JSON-RPC response: missing 'id'
>>> parse({"jsonrpc": "2.0", "error": "Not found", "id": 1})
Traceback (most recent call last):
...
jsonrpcclient.responses.InvalidResponse: Invalid JSON-RPC response: 'error' must be an object, got str
>>> parse_json('"pong"')
Traceback (most recent call last):
...
jsonrpcclient.responses.InvalidResponse: Invalid JSON-RPC response: expected an object, got str
In a batch, an invalid item raises when the iterator reaches it.
New in 4.1.0
InvalidResponse was added in 4.1.0. Earlier versions raised a plain
KeyError or TypeError, so InvalidResponse subclasses both, and code
that catches either of those still works.
Error handling puts this together with transport errors and untrusted input.