jsonrpcclient¶
Send JSON-RPC requests in Python 2.7 and 3.3+.
Guides¶
jsonrpcclient over HTTP¶
Send JSON-RPC requests over HTTP.
Installation¶
$ pip install jsonrpcclient requests
Usage¶
Set the server details:
>>> from jsonrpcclient.http_server import HTTPServer
>>> server = HTTPServer('http://pets.com/api')
Send a request:
>>> response = server.request('cat', name='Mittens')
--> {"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 1}
<-- {"jsonrpc": "2.0", "result": "meow", "id": 1}
The first argument is the JSON-RPC method
, followed by arguments to the
method.
The return value is the payload, (the result
part of the response
message):
>>> response
'meow'
If you’re not interested in a response, use notify()
instead of
request()
.
Lower-Level¶
Send your own message with send()
:
>>> server.send({'jsonrpc': '2.0', 'method': 'cat', 'params': {'name': 'Mittens'}, 'id': 5})
A Request
class is provided to simplify making a JSON-RPC
message:
>>> Request('cat', name='Mittens', request_id=5)
{"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 5}
Send a Request
:
>>> server.send(Request('cat', name='Mittens', request_id=5))
--> {"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 5}
<-- {"jsonrpc": "2.0", "result": "meow", "id": 5}
'meow'
There’s also a Notification
class if you don’t need a response.
Batch requests¶
With batch requests you can send multiple requests in a single message:
>>> server.send([{'jsonrpc': '2.0', 'method': 'cat'}, {'jsonrpc': '2.0', 'method': 'dog'}])
Send multiple Request
objects:
>>> server.send([Request('cat'), Request('dog')])
Using list comprehension to get the cube of ten numbers:
>>> server.send([Request('cube', i) for i in range(10)])
Unlike single requests, batch requests return the whole JSON-RPC response
object, i.e. a list of responses for each request that had an id
member.
The server may not support batch requests.
Configuration¶
The Requests module’s Session is available so you can configure that before sending any requests.
For example, for SSL authentication:
>>> server.session.verify = '/path/to/cert'
Basic Auth:
>>> server.session.auth = ('user', 'pass')
Custom HTTP headers:
>>> server.session.headers.update({'Content-Type': 'application/json-rpc'})
You can also configure the Request options
when calling send
:
>>> server.send(req, auth=('user', 'pass'))
>>> server.send(req, headers={'Content-Type': 'application/json-rpc'})
As in the requests library, any dictionaries passed to send
in named
arguments will be merged with the session-level values that are set. The
method-level parameters override session parameters.
Exceptions¶
The Requests module raises a requests.exceptions.RequestException if there’s a problem transferring the message. Other exceptions raised are:
jsonrpcclient.ParseError: | |
---|---|
Raised if the response is not valid JSON. | |
jsonschema.ValidationError: | |
Raised if the response is valid JSON but not a valid JSON-RPC response. | |
jsonrpcclient.ReceivedErrorResponse: | |
Raised if the server responded with an error message. |
Logging¶
To see the JSON-RPC messages going back and forth, set the logging level to
INFO
:
import logging
logging.getLogger('jsonrpcclient').setLevel(logging.INFO)
Then add a basic handler:
logging.getLogger('jsonrpcclient').addHandler(logging.StreamHandler())
Or use custom handlers and formats:
request_format = '%(endpoint)s --> %(message)s'
response_format = '%(endpoint)s <-- %(message)s'
# Request log
request_handler = logging.StreamHandler()
request_handler.setFormatter(logging.Formatter(fmt=request_format))
logging.getLogger('jsonrpcclient.server.request').addHandler(
request_handler)
# Response log
response_handler = logging.StreamHandler()
response_handler.setFormatter(logging.Formatter(fmt=response_format))
logging.getLogger('jsonrpcclient.server.response').addHandler(
response_handler)
The request format has these fields:
endpoint: | The server endpoint, eg. http://example.com/api . |
---|---|
http_headers: | The full HTTP headers. |
message: | The JSON request (the body). |
The response format has these fields:
endpoint: | The server endpoint, eg. http://example.com/api . |
---|---|
http_code: | The HTTP status code received from the server, eg. 400 . |
http_reason: | The description of the status code, eg. BAD REQUEST . |
http_headers: | The full HTTP headers. |
message: | The JSON response (the body). |
jsonrpcclient over ZeroMQ¶
Send JSON-RPC requests over ZeroMQ.
Installation¶
$ pip install jsonrpcclient pyzmq
Usage¶
Set the server details:
from jsonrpcclient.zmq_server import ZMQServer
server = ZMQServer('tcp://localhost:5555')
Send a request:
>>> response = server.request('cat', name='Mittens')
--> {"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 1}
<-- {"jsonrpc": "2.0", "result": "meow", "id": 1}
The first argument is the JSON-RPC method
, followed by arguments to the
method.
The return value is the payload, (the result
part of the response
message):
>>> response
'meow'
If you’re not interested in a response, use notify()
instead of
request()
.
Lower-Level¶
Send your own message with send()
:
>>> server.send({'jsonrpc': '2.0', 'method': 'cat', 'params': {'name': 'Mittens'}, 'id': 5})
A Request
class is provided to simplify making a JSON-RPC
message:
>>> Request('cat', name='Mittens', request_id=5)
{"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 5}
Send a Request
:
>>> server.send(Request('cat', name='Mittens', request_id=5))
--> {"jsonrpc": "2.0", "method": "cat", "params": {"name": "Mittens"}, "id": 5}
<-- {"jsonrpc": "2.0", "result": "meow", "id": 5}
'meow'
There’s also a Notification
class if you don’t need a response.
Batch requests¶
With batch requests you can send multiple requests in a single message:
>>> server.send([{'jsonrpc': '2.0', 'method': 'cat'}, {'jsonrpc': '2.0', 'method': 'dog'}])
Send multiple Request
objects:
>>> server.send([Request('cat'), Request('dog')])
Using list comprehension to get the cube of ten numbers:
>>> server.send([Request('cube', i) for i in range(10)])
Unlike single requests, batch requests return the whole JSON-RPC response
object, i.e. a list of responses for each request that had an id
member.
The server may not support batch requests.
Exceptions¶
In the event of a communications problem, pyzmq raises zmq.ZMQError:
try:
server.request('go')
except zmq.ZMQError as e:
print(str(e))
jsonrpcclient.ParseError: | |
---|---|
Raised if the response is not valid JSON. | |
jsonschema.ValidationError: | |
Raised if the response is valid JSON but not a valid JSON-RPC response. | |
jsonrpcclient.ReceivedErrorResponse: | |
Raised if the server responded with an error message. |
Logging¶
To see the JSON-RPC messages going back and forth, set the logging level to
INFO
:
import logging
logging.getLogger('jsonrpcclient').setLevel(logging.INFO)
Then add a basic handler:
logging.getLogger('jsonrpcclient').addHandler(logging.StreamHandler())
Or use custom handlers and formats:
request_format = '%(endpoint)s --> %(message)s'
response_format = '%(endpoint)s <-- %(message)s'
# Request log
request_handler = logging.StreamHandler()
request_handler.setFormatter(logging.Formatter(fmt=request_format))
logging.getLogger('jsonrpcclient.server.request').addHandler(
request_handler)
# Response log
response_handler = logging.StreamHandler()
response_handler.setFormatter(logging.Formatter(fmt=response_format))
logging.getLogger('jsonrpcclient.server.response').addHandler(
response_handler)
The request format has these fields:
endpoint: | The server endpoint, eg. http://localhost:5555 . |
---|---|
message: | The JSON request (the body). |
The response format has these fields:
endpoint: | The server endpoint, eg. http://localhost:5555 . |
---|---|
message: | The JSON response (the body). |
API¶
HTTPServer¶
An HTTP server to communicate with, for example:
HTTPServer('http://example.com/api').request('go')
-
class
http_server.
HTTPServer
(endpoint)¶ Parameters: - endpoint – The server address.
- kwargs – HTTP headers and other options passed on to the requests module.
-
notify
(method_name, *args, **kwargs)¶ Send a JSON-RPC request, without expecting a response.
Parameters: - method_name – The remote procedure’s method name.
- args – Positional arguments passed to the remote procedure.
- kwargs – Keyword arguments passed to the remote procedure.
Returns: The payload (i.e. the
result
part of the response.)
-
request
(method_name, *args, **kwargs)¶ Send a JSON-RPC request, and get a response.
Parameters: - method_name – The remote procedure’s method name.
- args – Positional arguments passed to the remote procedure.
- kwargs – Keyword arguments passed to the remote procedure.
Returns: The payload (i.e. the
result
part of the response.)
-
send
(request, **kwargs)¶ Send a request or batch of requests.
Parameters: request – The request to send. If a string, must be valid JSON (double quotes around the identifiers!). Otherwise it must be a json serializable object (list or dict).
Raises:
-
validate
= True¶
ZMQServer¶
A ZeroMQ server to communicate with, for example:
ZMQServer('tcp://hostname:5555').request('go')
-
class
zmq_server.
ZMQServer
(endpoint, socket_type=3)¶ Parameters: - endpoint – The server address.
- socket_type – The zeromq socket type. Default is zmq.REQ.
-
notify
(method_name, *args, **kwargs)¶ Send a JSON-RPC request, without expecting a response.
Parameters: - method_name – The remote procedure’s method name.
- args – Positional arguments passed to the remote procedure.
- kwargs – Keyword arguments passed to the remote procedure.
Returns: The payload (i.e. the
result
part of the response.)
-
request
(method_name, *args, **kwargs)¶ Send a JSON-RPC request, and get a response.
Parameters: - method_name – The remote procedure’s method name.
- args – Positional arguments passed to the remote procedure.
- kwargs – Keyword arguments passed to the remote procedure.
Returns: The payload (i.e. the
result
part of the response.)
-
send
(request, **kwargs)¶ Send a request or batch of requests.
Parameters: request – The request to send. If a string, must be valid JSON (double quotes around the identifiers!). Otherwise it must be a json serializable object (list or dict).
Raises:
-
validate
= True¶
Requests¶
These classes make it easy to create JSON-RPC Request objects.
-
request.
Notification
¶ A JSON-RPC Request object, with no
id
member (meaning no payload data is wanted):>>> from jsonrpcclient import Notification >>> Notification('cat') {'jsonrpc': '2.0', 'method': 'cat'}
The first argument is the method; everything else is arguments to the method:
>>> Notification('cat', 'Mittens', 5) {'jsonrpc': '2.0', 'method': 'cat', params: ['Mittens', 5]}
Keyword arguments are also acceptable:
>>> Notification('cat', name='Mittens', age=5) {'jsonrpc': '2.0', 'method': 'cat', 'params': {'name': 'Mittens', 'age': 5}}
If you prefer, call the method as though it was a class attribute:
>>> Notification.cat(name='Mittens', age=5) {'jsonrpc': '2.0', 'method': 'cat', 'params': {'name': 'Mittens', 'age': 5}}
Parameters: - method – The method name.
- args – Positional arguments.
- kwargs – Keyword arguments.
Returns: The JSON-RPC request in dictionary form.
-
request.
Request
¶ A JSON-RPC Request object, with an
id
member (meaning payload data is wanted):>>> Request('cat') {'jsonrpc': '2.0', 'method': 'cat', 'id': 1}
An auto-incremented
id
is used, so each request has a uniqueid
:>>> Request('cat') {'jsonrpc': '2.0', 'method': 'cat', 'id': 2}
Use
request_id
to specify theid
to use:>>> Request('cat', request_id='Request #1') {'jsonrpc': '2.0', 'method': 'cat', 'id': 'Request #1'}
Parameters: - method – The method name.
- args – Positional arguments.
- kwargs – Keyword arguments.
Returns: The JSON-RPC request in dictionary form.
ID Iterators¶
By default the request id
is a decimal number which increments for each
request. Use a different format by setting Request.id_iterator
:
>>> from jsonrpcclient.request import Request
>>> from jsonrpcclient.id_iterators import random_iterator
>>> Request.id_iterator = random_iterator()
>>> Request('go')
{'jsonrpc': '2.0', 'method': 'go', 'id': 'fubui5e6'}
-
id_iterators.
hex_iterator
(start=1)¶ Incremental hexadecimal numbers.
e.g. ‘1’, ‘2’ .. ‘9’, ‘a’, ‘b’, etc.
Request.id_iterator = hex_iterator()
-
id_iterators.
uuid_iterator
()¶ Unique uuid ids.
e.g. ‘9bfe2c93-717e-4a45-b91b-55422c5af4ff’
Request.id_iterator = uuid_iterator()
-
id_iterators.
random_iterator
(length=8, chars='0123456789abcdefghijklmnopqrstuvwxyz')¶ A random string. Not unique, but has around 1 in a million chance of collision with default values.
e.g. ‘fubui5e6’
Request.id_iterator = random_iterator(16, 'abc123')
Parameters: - length – Length of the random string.
- chars – The characters to randomly choose from.
Exceptions¶
These exceptions are raised when processing responses from the server. For
example, if the response was garbage and could not be parsed,
ParseResponseError
is
raised.
To handle them, use a try-block when calling notify
or request
:
try:
server.notify('go')
except JsonRpcClientError as e:
print(str(e))
-
exception
jsonrpcclient.exceptions.
JsonRpcClientError
¶ Bases:
Exception
Base class for the other exceptions.
-
exception
jsonrpcclient.exceptions.
ParseResponseError
¶ Bases:
jsonrpcclient.exceptions.JsonRpcClientError
The response was not valid JSON.
-
exception
jsonrpcclient.exceptions.
ReceivedErrorResponse
(code, message, data)¶ Bases:
jsonrpcclient.exceptions.JsonRpcClientError
Raised if a single JSON-RPC error response object is received. This is not raised for batch requests.
This error means one of two things:
- There was a problem with the request.
- There was a problem with the application at the receiving end.
To see information about the error, catch the exception:
from jsonrpcclient.exceptions import JsonRpcClientError, \ ReceivedErrorResponse try: server.notify('go') except ReceivedErrorResponse as e: print(e.code, e.message, e.data) except JsonRpcClientError as e: print(str(e))