Skip to content

Commit

Permalink
Simpler configuration of the id iterator + docs
Browse files Browse the repository at this point in the history
  • Loading branch information
bcb committed Aug 10, 2016
1 parent 8d1320a commit 5ed2237
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 61 deletions.
45 changes: 20 additions & 25 deletions doc/guide.rst → doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@
jsonrpcclient API
*****************

Some of the more advanced features of the library, including usage and
:mod:`configuration <config>`.

Request
=======

Expand All @@ -22,7 +19,9 @@ Send
Request class
=============

>>> from jsonrpcclient import Request
::

from jsonrpcclient import Request

.. autoclass:: request.Request

Expand All @@ -36,26 +35,26 @@ Send a ``Request`` object::
The :func:`~server.Server.request` method is a wrapper around
``send(Request())``.

If you're not interested in a response, use ``Notification`` instead of
``Request``.
If you're not interested in a response, use the ``Notification`` class instead
of ``Request``.

Batch requests
==============

This JSON-RPC feature allows you to send multiple requests in a single
message::

>>> server.send([
... {'jsonrpc': '2.0', 'method': 'cat', 'id': 1}, \
... {'jsonrpc': '2.0', 'method': 'dog', 'id': 2}])
server.send([
{'jsonrpc': '2.0', 'method': 'cat', 'id': 1}, \
{'jsonrpc': '2.0', 'method': 'dog', 'id': 2}])

Send multiple ``Request`` objects::
Send multiple :class:`~request.Request` objects::

>>> server.send([Request('cat'), Request('dog')])
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)])
server.send([Request('cube', i) for i in range(10)])

Unlike single requests, batch requests return the whole JSON-RPC response
object - a list of responses for each request that had an ``id`` member.
Expand All @@ -67,34 +66,30 @@ Configuration

.. automodule:: config

Request IDs
-----------

.. automodule:: ids

Configuring the Requests library
--------------------------------

The HTTPServer class makes use of python's Requests library. The `Session
HTTPServer makes use of Kenneth Reitz's Requests library. The `Session
<http://docs.python-requests.org/en/master/api/#requests.Session>`_ is
available so you can configure that before sending any requests.
available so you can configure it before sending any requests.

For example, for SSL authentication::
For example, Basic Auth::

>>> server.session.verify = '/path/to/certificate'
server.session.auth = ('user', 'pass')

Basic Auth::
SSL authentication::

>>> server.session.auth = ('user', 'pass')
server.session.verify = '/path/to/certificate'

Custom HTTP headers::

>>> server.session.headers.update({'Content-Type': 'application/json-rpc'})
server.session.headers.update({'Content-Type': 'application/json-rpc'})

You can also configure some Requests options when calling
:func:`~server.Server.send`::

>>> server.send(req, verify=True, cert='/path/to/certificate')
server.send(req, verify=True, cert='/path/to/certificate', \
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
Expand Down
8 changes: 6 additions & 2 deletions jsonrpcclient/config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"""Some options are configured in the ``config`` module. Import it and modify
the attributes. For example::
"""Some options can be configured in the ``config`` module. Import it and modify
the attributes, for example::
from jsonrpcclient import config
config.validate = False
"""
#: Validate responses against the JSON-RPC schema. Disable to speed up
#: processing.
validate = True

#: Configure the ``id`` part of requests. Can be "decimal", "hex", "random" or
#: "uuid".
ids = 'decimal'
34 changes: 27 additions & 7 deletions jsonrpcclient/ids.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"""By default the request ``id`` is a decimal number which increments with each
request. Use a different format by patching ``Request.ids``::
>>> from jsonrpcclient import Request, ids
>>> Request.ids = ids.random()
>>> Request('go')
{'jsonrpc': '2.0', 'method': 'go', 'id': 'fubui5e6'}
request. See the :mod:`config` module.
"""
from uuid import uuid4
import itertools
from string import digits, ascii_lowercase
from random import choice
from uuid import uuid4


def decimal(start=1):
"""Increments from ``start``, e.g. 1, 2, 3, .. 9, 10, 11, etc.
:param start: The first value to start with.
"""
return itertools.count(start)

def hex(start=1):
"""Incremental hexadecimal numbers. e.g. 1, 2, 3, .. 9, a, b, etc.
Expand All @@ -36,3 +39,20 @@ def uuid():
"""Unique uuid ids. e.g. '9bfe2c93-717e-4a45-b91b-55422c5af4ff'"""
while True:
yield str(uuid4())


def from_config(setting):
"""Returns an iterator, based on a configuration setting, such as
'decimal'
"""
# Create iterator based on config setting
if setting == 'decimal':
return decimal()
elif setting == 'hex':
return hex()
elif setting == 'random':
return random()
elif setting == 'uuid':
return uuid()
else:
raise ValueError('Unknown ids config setting')
30 changes: 15 additions & 15 deletions jsonrpcclient/request.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
"""These classes make it easy to create JSON-RPC Request objects."""

import itertools
import sys
import json
from collections import OrderedDict
from future.utils import with_metaclass

from jsonrpcclient import config, ids


def _sort_request(req):
"""Sorts a JSON-RPC request dict returning a sorted OrderedDict, having no
Expand Down Expand Up @@ -111,22 +113,20 @@ class Request(Notification):
:param method: The ``method`` name.
:param args: Positional arguments added to ``params``.
:param kwargs: Keyword arguments added to ``params``.
:param kwargs: Keyword arguments added to ``params``. Use ``request_id=x``
to force the ``id`` to use.
:returns: The JSON-RPC request in dictionary form.
"""
#: The ids are auto-incremented from 1, unless an id is specified in a
#: ``request_id`` keyword argument.
#: The ids attribute can be patched to give different id formats, (see
#: :mod:`config`).
ids = itertools.count(1)
id_iterator = None

def __init__(self, method, *args, **kwargs):
# 'response' means use an auto-iterated id
#kwargs.pop('response', None)
# 'request_id' means use the specified id
if kwargs.get('request_id'):
self['id'] = kwargs['request_id']
else:
self['id'] = next(self.ids)
kwargs.pop('request_id', None)
# If 'request_id' is passed, use the specified id
if 'request_id' in kwargs:
self['id'] = kwargs.pop('request_id', None)
else: # Get the next id from the iterator
# Create the iterator if not yet created
if Request.id_iterator is None:
Request.id_iterator = ids.from_config(config.ids)
self['id'] = next(self.id_iterator)
# We call super last, after popping the request_id
super(Request, self).__init__(method, *args, **kwargs)
5 changes: 2 additions & 3 deletions test/unit/test_http_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from unittest import TestCase, main
import itertools

try:
from urllib.parse import urlencode
except ImportError:
Expand All @@ -20,8 +19,8 @@
class TestHTTPServer(TestCase):

def setUp(self):
# Patch Request.ids to ensure the id is always 1
Request.id = itertools.count(1)
# Patch Request.id_iterator to ensure the id is always 1
Request.id_iterator = itertools.count(1)

@staticmethod
def test_init_endpoint_only():
Expand Down
13 changes: 6 additions & 7 deletions test/unit/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ class TestRequest(TestCase):

def setUp(self):
# Start each test with id 1
Request.ids = itertools.count(1)
Request.id_iterator = itertools.count(1)

def tearDown(self):
# Restore default iterator
Request.ids = itertools.count(1)
Request.id_iterator = itertools.count(1)

def test(self):
self.assertEqual(
Expand Down Expand Up @@ -94,19 +94,18 @@ def test_keyword(self):

def test_auto_iterating_id(self):
self.assertEqual(
{'jsonrpc': '2.0', 'method': 'go', 'id': 1},
Request('go'))
{'jsonrpc': '2.0', 'method': 'go', 'id': 1}, Request('go'))
print(Request.id_iterator)
self.assertEqual(
{'jsonrpc': '2.0', 'method': 'go', 'id': 2},
Request('go'))
{'jsonrpc': '2.0', 'method': 'go', 'id': 2}, Request('go'))

def test_specified_id(self):
self.assertEqual(
{'jsonrpc': '2.0', 'method': 'get', 'id': 'Request #1'},
Request('get', request_id='Request #1'))

def test_custom_iterator(self):
Request.ids = ids.hex(10)
Request.id_iterator = ids.hex(10)
self.assertEqual(
{'jsonrpc': '2.0', 'method': 'go', 'id': 'a'},
Request('go'))
Expand Down
4 changes: 2 additions & 2 deletions test/unit/test_zmq_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
class TestZMQServer(TestCase):

def setUp(self):
# Patch Request.ids to ensure the request id is always 1
Request.ids = itertools.count(1)
# Patch Request.id_iterator to ensure the request id is always 1
Request.id_iterator = itertools.count(1)

@staticmethod
def test_instantiate():
Expand Down

0 comments on commit 5ed2237

Please sign in to comment.