From 066715c5cebc4862e3facc23ddbd400c8e7650a1 Mon Sep 17 00:00:00 2001
From: = <=>
Date: Thu, 26 Nov 2020 10:40:20 +0000
Subject: [PATCH 1/8] Release 2 Update
This library now supports Release 2 of the RANDOM.ORG JSON-RPC API.
Updates:
- Error codes (400: RandomOrgKeyNonExistentError, 404: RandomOrgKeyInvalidAccessError, 405: RandomOrgKeyInvalidVersionError)
- New methods (generate_integer_sequences, generate_signed_integer_sequences, get_result)
- new optional parameter 'user_data' added to signed methods returning random values
- new test cases in test_rdoclient.py to handle the new methods and error codes
Changes were also made to support newer versions of Python - this library can now be used for Python 2.7 & Python >= 3.5.
---
README.rst | 17 +-
rdoclient/__init__.py | 14 +-
rdoclient/rdoclient.py | 856 ++++++++++++++++++++++++++++++++++-------
setup.py | 22 +-
test_rdoclient.py | 196 ++++++++--
5 files changed, 914 insertions(+), 191 deletions(-)
diff --git a/README.rst b/README.rst
index e8148f7..890dd92 100644
--- a/README.rst
+++ b/README.rst
@@ -1,9 +1,9 @@
JSON-RPC-Python
===============
-RANDOM.ORG JSON-RPC API (Release 1) implementation.
+RANDOM.ORG JSON-RPC API (Release 2) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R1). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R2). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
Installation
------------
@@ -20,6 +20,15 @@ Requires the `requests `_ lib:
$ pip install requests
+Requires the `six `_ lib:
+
+.. code-block:: bash
+
+ $ pip install six
+
+
+Note that the required dependencies 'requests' and 'six' are installed automatically, when using pip install for 'rdoclient' version >= 1.2 (Update: November 2020).
+
Usage
-----
@@ -67,9 +76,9 @@ Finally, it is possible to request live results as-soon-as-possible and without
Documentation
-------------
-For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/1/
+For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/2
Tests
-----
-Note that to run the accompanying tests the API_KEY fields must be given authentic values.
+Note that to run the accompanying tests the _API_KEY_1 field in test_rdoclient.py must be given an authentic value, i.e. a valid API key. The _API_KEY_2 field does not need to be changed.
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index f81412b..dd18a7d 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 1) implementation.
+RANDOM.ORG JSON-RPC API (Release 2) implementation.
Usage:
@@ -44,7 +44,7 @@
[3, 5, 2, 4, 8]
For a full list of available randomness generation functions see
-rdoclient.py documentation and https://api.random.org/json-rpc/1/
+rdoclient.py documentation and https://api.random.org/json-rpc/2
"""
__title__ = 'rdoclient'
@@ -54,9 +54,15 @@
__license__ = 'MIT'
__copyright__ = 'Copyright 2014 RANDOM.ORG'
-from .rdoclient import RandomOrgClient, RandomOrgCache, RandomOrgSendTimeoutError, RandomOrgKeyNotRunningError, RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError
+from .rdoclient import (RandomOrgClient, RandomOrgCache, RandomOrgSendTimeoutError,
+ RandomOrgKeyNonExistentError, RandomOrgKeyNotRunningError,
+ RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError,
+ RandomOrgKeyInvalidAccessError, RandomOrgKeyInvalidVersionError)
-__all__ = [ 'RandomOrgClient', 'RandomOrgCache', 'RandomOrgSendTimeoutError', 'RandomOrgKeyNotRunningError', 'RandomOrgInsufficientRequestsError', 'RandomOrgInsufficientBitsError' ]
+__all__ = [ 'RandomOrgClient', 'RandomOrgCache', 'RandomOrgSendTimeoutError',
+ 'RandomOrgKeyNonExistentError', 'RandomOrgKeyNotRunningError',
+ 'RandomOrgInsufficientRequestsError', 'RandomOrgInsufficientBitsError',
+ 'RandomOrgKeyInvalidAccessError', 'RandomOrgKeyInvalidVersionError' ]
import logging
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index f84d2e6..ebe4d1a 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -1,7 +1,7 @@
"""
-RANDOM.ORG JSON-RPC API (Release 1) implementation.
+RANDOM.ORG JSON-RPC API (Release 2) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R1).
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R2).
It provides either serialized or unserialized access to both the signed
and unsigned methods of the API through the RandomOrgClient class. It
also provides a convenience class through the RandomOrgClient class,
@@ -15,27 +15,41 @@
RandomOrgSendTimeoutError -- when request can't be sent in a set time.
+RandomOrgKeyNonExistentError -- key does not exist.
+
RandomOrgKeyNotRunningError -- key stopped exception.
RandomOrgInsufficientRequestsError -- requests allowance exceeded.
RandomOrgInsufficientBitsError -- bits allowance exceeded.
+RandomOrgKeyInvalidAccessError -- key is not valid for the requested method
+
+RandomOrgKeyInvalidVersionError -- key is not valid for the version of the API
+
"""
+from collections import OrderedDict
import json
import logging
import threading
import time
+import sys
import uuid
from datetime import datetime
-from Queue import Queue, Empty
+try:
+ # Python 2.7
+ from Queue import Queue, Empty
+except ImportError:
+ # Python 3+
+ from queue import Queue, Empty
import requests
-# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/1/
+# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/2/basic
_INTEGER_METHOD = 'generateIntegers'
+_INTEGER_SEQUENCES_METHOD = 'generateIntegerSequences'
_DECIMAL_FRACTION_METHOD = 'generateDecimalFractions'
_GAUSSIAN_METHOD = 'generateGaussians'
_STRING_METHOD = 'generateStrings'
@@ -43,13 +57,15 @@
_BLOB_METHOD = 'generateBlobs'
_GET_USAGE_METHOD = 'getUsage'
-# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/1/signing
+# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/2/signed
_SIGNED_INTEGER_METHOD = 'generateSignedIntegers'
+_SIGNED_INTEGER_SEQUENCES_METHOD = 'generateSignedIntegerSequences'
_SIGNED_DECIMAL_FRACTION_METHOD = 'generateSignedDecimalFractions'
_SIGNED_GAUSSIAN_METHOD = 'generateSignedGaussians'
_SIGNED_STRING_METHOD = 'generateSignedStrings'
_SIGNED_UUID_METHOD = 'generateSignedUUIDs'
_SIGNED_BLOB_METHOD = 'generateSignedBlobs'
+_GET_RESULT_METHOD = 'getResult'
_VERIFY_SIGNATURE_METHOD = 'verifySignature'
# Blob format literals
@@ -59,7 +75,8 @@
# Default backoff to use if no advisoryDelay backoff supplied by server
_DEFAULT_DELAY = 1.0
-# On request fetch fresh allowance state if current state data is older than this value
+# On request fetch fresh allowance state if current state data is older
+# than this value
_ALLOWANCE_STATE_REFRESH_SECONDS = 3600.0
class RandomOrgSendTimeoutError(Exception):
@@ -70,6 +87,14 @@ class RandomOrgSendTimeoutError(Exception):
is exceeded before the request can be sent.
"""
+class RandomOrgKeyNonExistentError(Exception):
+ """
+ RandomOrgClient key does not exist.
+
+ Exception raised by the RandomOrgClient class when the API key
+ specified does not exist. Requests will not complete.
+ """
+
class RandomOrgKeyNotRunningError(Exception):
"""
RandomOrgClient key stopped exception.
@@ -101,6 +126,23 @@ class RandomOrgInsufficientBitsError(Exception):
to help determine if an alternative request size is appropriate.
"""
+class RandomOrgKeyInvalidAccessError(Exception):
+ """
+ RandomOrgClient key is not valid for the method requested.
+
+ Exception raised by the RandomOrgClient class when its API key
+ is not valid for the method requested. Requests will not complete.
+ """
+
+class RandomOrgKeyInvalidVersionError(Exception):
+ """
+ RandomOrgClient key is not valid for the version of the API invoked.
+
+ Exception raised by the RandomOrgClient class when its API key
+ is not valid for the version of the API invoked. Requests will not
+ complete.
+ """
+
class RandomOrgCache(object):
"""
RandomOrgCache for precaching request responses.
@@ -183,7 +225,8 @@ def _populate_queue(self):
if self._bulk_request_number > 0:
# Is there space for a bulk response in the queue?
- if self._queue.qsize() < (self._queue.maxsize - self._bulk_request_number):
+ if self._queue.qsize() < (self._queue.maxsize
+ - self._bulk_request_number):
# Issue and process request and response.
try:
@@ -191,8 +234,15 @@ def _populate_queue(self):
result = self._process_function(response)
# Split bulk response into result sets.
- for i in xrange(0, len(result), self._request_number):
- self._queue.put(result[i:i+self._request_number])
+ try:
+ # Python 2.7
+ for i in xrange(0, len(result), self._request_number):
+ self._queue.put(result[i:i+self._request_number])
+
+ except NameError:
+ # Python 3+
+ for i in range(0, len(result), self._request_number):
+ self._queue.put(result[i:i+self._request_number])
except Exception as e:
# Don't handle failures from _request_function()
@@ -296,9 +346,10 @@ class RandomOrgClient(object):
Public methods:
Basic methods for generating randomness, see:
- https://api.random.org/json-rpc/1/basic
+ https://api.random.org/json-rpc/2/basic
generate_integers -- get a list of random integers.
+ generate_integer_sequences -- get sequences of random integers.
generate_decimal_fractions -- get a list of random doubles.
generate_gaussians -- get a list of random numbers.
generate_strings -- get a list of random strings.
@@ -306,10 +357,12 @@ class RandomOrgClient(object):
generate_blobs -- get a list of random blobs.
Signed methods for generating randomness, see:
- https://api.random.org/json-rpc/1/signing
+ https://api.random.org/json-rpc/2/signed
generate_signed_integers -- get a signed response containing a list
of random integers and a signature.
+ generate_signed_integer_sequences -- get a signed response
+ containing sequences of random integers and a signature.
generate_signed_decimal_fractions -- get a signed response
containing a list of random doubles and a signature.
generate_signed_gaussians -- get a signed response containing a
@@ -320,9 +373,15 @@ class RandomOrgClient(object):
random UUIDs and a signature.
generate_signed_blobs -- get a signed response containing a list of
random blobs and a signature.
+
+ Retrieving previously generated signed results (within 24h), see:
+ https://api.random.org/json-rpc/2/signed#getResult
+
+ get_result -- retrieve previously generated signed results using
+ a serial number (restricted to within 24 hours after generation)
Signature verification for signed methods, see:
- https://api.random.org/json-rpc/1/signing
+ https://api.random.org/json-rpc/2/signed
verify_signature -- verify a response against its signature.
@@ -330,6 +389,8 @@ class RandomOrgClient(object):
create_integer_cache -- get a RandomOrgCache from which to obtain a
list of random integers.
+ create_integer_sequences_cache - get a RandomCache from which to
+ obtain sequences of random integers.
create_decimal_fraction_cache -- get a RandomOrgCache from which to
obtain a list of random doubles.
create_gaussian_cache -- get a RandomOrgCache from which to obtain
@@ -354,11 +415,12 @@ def __new__(cls, *args, **kwds):
"""
Instance creation.
- Ensure only one instace of RandomOrgClient exists per API key.
+ Ensure only one instance of RandomOrgClient exists per API key.
Create a new instance if the supplied key isn't already known,
otherwise return the previously instantiated one.
"""
- instance = RandomOrgClient.__key_indexed_instances.get(args[0], None)
+ instance = RandomOrgClient.__key_indexed_instances.get(args[0],
+ None)
if instance is None:
instance = object.__new__(cls)
@@ -367,7 +429,8 @@ def __new__(cls, *args, **kwds):
return instance
def __init__(self, api_key,
- blocking_timeout=24.0*60.0*60.0, http_timeout=120.0, serialized=True):
+ blocking_timeout=24.0*60.0*60.0, http_timeout=120.0,
+ serialized=True):
"""
Constructor.
@@ -432,24 +495,28 @@ def __init__(self, api_key,
self._backoff_error = None
else:
- logging.info("Using RandomOrgClient instance already created for key \"" + api_key + "\"")
+ logging.info("Using RandomOrgClient instance already created for key \""
+ + api_key + "\"")
# Basic methods for generating randomness, see:
- # https://api.random.org/json-rpc/1/basic
+ # https://api.random.org/json-rpc/2/basic
- def generate_integers(self, n, min, max, replacement=True):
+ def generate_integers(self, n, min, max, replacement=True, base=10):
"""
Generate random integers.
Request and return a list (size n) of true random integers
within a user-defined range from the server. See:
- https://api.random.org/json-rpc/1/basic#generateIntegers
+ https://api.random.org/json-rpc/2/basic#generateIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -458,11 +525,17 @@ def generate_integers(self, n, min, max, replacement=True):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -479,13 +552,86 @@ def generate_integers(self, n, min, max, replacement=True):
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16.
"""
- params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max, 'replacement':replacement }
+ params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max,
+ 'replacement':replacement, 'base':base }
request = self._generate_request(_INTEGER_METHOD, params)
response = self._send_request(request)
return self._extract_ints(response)
+ def generate_integer_sequences(self, n, length, min, max, replacement=True,
+ base=10):
+ """
+ Generate random integer sequences.
+
+ Request and return a list (size n) of uniform or multiform
+ sequences of true random integers
+ within a user-defined range from the server. See:
+ https://api.random.org/json-rpc/2/basic#generateIntegerSequences
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+
+ n -- How many integer sequences you need. Must be within the
+ [1,1e3] range.
+ length -- Lengths of the sequences requested. Uniform: Must be
+ an integer within the [1,1e4] range. Multiform: an array of
+ length n where each value is within the [1,1e4] range and
+ the sum of all n values is within the [1,1e4] range.
+ min -- The lower boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ max -- The upper boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ replacement -- Specifies whether the random numbers should be
+ picked with replacement. If True the resulting numbers may
+ contain duplicate values, otherwise the numbers will all be
+ unique (default True). For multiform sequences this can be
+ an array of n boolean values, each specifying whether the
+ sequence identified by its index will be created with (true)
+ or without (false) replacement.
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16. For multiform
+ sequences the values may be an array of length n with values
+ taken from the same set.
+ """
+ params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
+ 'max':max, 'replacement':replacement, 'base':base}
+ request = self._generate_request(_INTEGER_SEQUENCES_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_int_sequences(response)
+
def generate_decimal_fractions(self, n, decimal_places, replacement=True):
"""
Generate random decimal fractions.
@@ -494,12 +640,15 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
fractions, from a uniform distribution across the [0,1]
interval with a user-defined number of decimal places from the
server. See:
- https://api.random.org/json-rpc/1/basic#generateDecimalFractions
+ https://api.random.org/json-rpc/2/basic#generateDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -508,11 +657,17 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -543,12 +698,15 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
a Gaussian distribution (also known as a normal distribution).
The form uses a Box-Muller Transform to generate the Gaussian
distribution from uniformly distributed numbers. See:
- https://api.random.org/json-rpc/1/basic#generateGaussians
+ https://api.random.org/json-rpc/2/basic#generateGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -557,11 +715,17 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -579,7 +743,8 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
"""
params = { 'apiKey':self._api_key, 'n':n, 'mean':mean,
- 'standardDeviation':standard_deviation, 'significantDigits':significant_digits }
+ 'standardDeviation':standard_deviation,
+ 'significantDigits':significant_digits }
request = self._generate_request(_GAUSSIAN_METHOD, params)
response = self._send_request(request)
return self._extract_doubles(response)
@@ -590,12 +755,15 @@ def generate_strings(self, n, length, characters, replacement=True):
Request and return a list (size n) of true random unicode
strings from the server. See:
- https://api.random.org/json-rpc/1/basic#generateStrings
+ https://api.random.org/json-rpc/2/basic#generateStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -604,11 +772,17 @@ def generate_strings(self, n, length, characters, replacement=True):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -641,12 +815,15 @@ def generate_UUIDs(self, n):
Request and return a list (size n) of version 4 true random
Universally Unique IDentifiers (UUIDs) in accordance with
section 4.4 of RFC 4122, from the server. See:
- https://api.random.org/json-rpc/1/basic#generateUUIDs
+ https://api.random.org/json-rpc/2/basic#generateUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -655,11 +832,17 @@ def generate_UUIDs(self, n):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -682,12 +865,15 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
Request and return a list (size n) of Binary Large OBjects
(BLOBs) as unicode strings containing true random data from the
server. See:
- https://api.random.org/json-rpc/1/basic#generateBlobs
+ https://api.random.org/json-rpc/2/basic#generateBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -696,11 +882,17 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -723,9 +915,10 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
# Signed methods for generating randomness, see:
- # https://api.random.org/json-rpc/1/signing
+ # https://api.random.org/json-rpc/2/signed
- def generate_signed_integers(self, n, min, max, replacement=True):
+ def generate_signed_integers(self, n, min, max, replacement=True,
+ base=10, user_data=None):
"""
Generate digitally signed random integers.
@@ -734,12 +927,15 @@ def generate_signed_integers(self, n, min, max, replacement=True):
with the parsed integer list mapped to 'data', the original
response mapped to 'random', and the response's signature
mapped to 'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedIntegers
+ https://api.random.org/json-rpc/2/signed#generateSignedIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -748,11 +944,17 @@ def generate_signed_integers(self, n, min, max, replacement=True):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -769,14 +971,101 @@ def generate_signed_integers(self, n, min, max, replacement=True):
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16.
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
- params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max, 'replacement':replacement }
+ params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max,
+ 'replacement':replacement, 'base':base, 'userData':user_data }
request = self._generate_request(_SIGNED_INTEGER_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_ints)
- def generate_signed_decimal_fractions(self, n, decimal_places, replacement=True):
+ def generate_signed_integer_sequences(self, n, length, min, max,
+ replacement=True, base=10,
+ user_data=None):
+ """
+ Generate digitally signed sequences of random integers.
+
+ Request a list (size n) of sequences of true random integers within
+ a user-defined range from the server. Returns a dictionary object
+ with the parsed integer list mapped to 'data', the original
+ response mapped to 'random', and the response's signature
+ mapped to 'signature'. See:
+ https://api.random.org/json-rpc/2/signed#generateSignedIntegerSequences
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+
+ n -- How many integer sequences you need. Must be within the
+ [1,1e3] range.
+ length -- Lengths of the sequences requested. Uniform: Must be
+ an integer within the [1,1e4] range. Multiform: an array of
+ length n where each value is within the [1,1e4] range and
+ the sum of all n values is within the [1,1e4] range.
+ min -- The lower boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ max -- The upper boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ replacement -- Specifies whether the random numbers should be
+ picked with replacement. If True the resulting numbers may
+ contain duplicate values, otherwise the numbers will all be
+ unique (default True). For multiform sequences this can be
+ an array of n boolean values, each specifying whether the
+ sequence identified by its index will be created with (true)
+ or without (false) replacement.
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16. For multiform
+ sequences the values may be an array of length n with values
+ taken from the same set.
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
+ """
+
+ params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
+ 'max':max, 'replacement':replacement, 'base':base,
+ 'userData':user_data}
+ request = self._generate_request(_SIGNED_INTEGER_SEQUENCES_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_signed_response(response, self._extract_int_sequences)
+
+ def generate_signed_decimal_fractions(self, n, decimal_places,
+ replacement=True, user_data=None):
"""
Generate digitally signed random decimal fractions.
@@ -786,12 +1075,15 @@ def generate_signed_decimal_fractions(self, n, decimal_places, replacement=True)
a dictionary object with the parsed decimal fraction list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedDecimalFractions
+ https://api.random.org/json-rpc/2/signed#generateSignedDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -800,11 +1092,17 @@ def generate_signed_decimal_fractions(self, n, decimal_places, replacement=True)
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -819,15 +1117,21 @@ def generate_signed_decimal_fractions(self, n, decimal_places, replacement=True)
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
params = { 'apiKey':self._api_key, 'n':n,
- 'decimalPlaces':decimal_places, 'replacement':replacement }
+ 'decimalPlaces':decimal_places, 'replacement':replacement,
+ 'userData':user_data }
request = self._generate_request(_SIGNED_DECIMAL_FRACTION_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
- def generate_signed_gaussians(self, n, mean, standard_deviation, significant_digits):
+ def generate_signed_gaussians(self, n, mean, standard_deviation,
+ significant_digits, user_data=None):
"""
Generate digitally signed random numbers.
@@ -838,12 +1142,15 @@ def generate_signed_gaussians(self, n, mean, standard_deviation, significant_dig
dictionary object with the parsed random number list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedGaussians
+ https://api.random.org/json-rpc/2/signed#generateSignedGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -852,11 +1159,17 @@ def generate_signed_gaussians(self, n, mean, standard_deviation, significant_dig
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -871,15 +1184,22 @@ def generate_signed_gaussians(self, n, mean, standard_deviation, significant_dig
Must be within the [-1e6,1e6] range.
significant_digits -- The number of significant digits to use.
Must be within the [2,20] range.
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
params = { 'apiKey':self._api_key, 'n':n, 'mean':mean,
- 'standardDeviation':standard_deviation, 'significantDigits':significant_digits }
+ 'standardDeviation':standard_deviation,
+ 'significantDigits':significant_digits,
+ 'userData':user_data }
request = self._generate_request(_SIGNED_GAUSSIAN_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
- def generate_signed_strings(self, n, length, characters, replacement=True):
+ def generate_signed_strings(self, n, length, characters,
+ replacement=True, user_data=None):
"""
Generate digitally signed random strings.
@@ -887,12 +1207,15 @@ def generate_signed_strings(self, n, length, characters, replacement=True):
Returns a dictionary object with the parsed random string list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedStrings
+ https://api.random.org/json-rpc/2/signed#generateSignedStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -901,11 +1224,17 @@ def generate_signed_strings(self, n, length, characters, replacement=True):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -923,15 +1252,20 @@ def generate_signed_strings(self, n, length, characters, replacement=True):
picked with replacement. If True the resulting list of
strings may contain duplicates, otherwise the strings will
all be unique (default True).
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length,
- 'characters':characters, 'replacement':replacement }
+ 'characters':characters, 'replacement':replacement,
+ 'userData':user_data }
request = self._generate_request(_SIGNED_STRING_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_strings)
- def generate_signed_UUIDs(self, n):
+ def generate_signed_UUIDs(self, n, user_data=None):
"""
Generate digitally signed random UUIDs.
@@ -941,12 +1275,15 @@ def generate_signed_UUIDs(self, n):
parsed random UUID list mapped to 'data', the original response
mapped to 'random', and the response's signature mapped to
'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedUUIDs
+ https://api.random.org/json-rpc/2/signed#generateSignedUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -955,11 +1292,17 @@ def generate_signed_UUIDs(self, n):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -968,14 +1311,19 @@ def generate_signed_UUIDs(self, n):
n -- How many random UUIDs you need. Must be within the [1,1e3]
range.
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
- params = { 'apiKey':self._api_key, 'n':n }
+ params = { 'apiKey':self._api_key, 'n':n, 'userData':user_data }
request = self._generate_request(_SIGNED_UUID_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_UUIDs)
- def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
+ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
+ user_data=None):
"""
Generate digitally signed random BLOBs.
@@ -984,12 +1332,15 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
dictionary object with the parsed random BLOB list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/1/signing#generateSignedBlobs
+ https://api.random.org/json-rpc/2/signed#generateSignedBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -998,11 +1349,17 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1016,16 +1373,68 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
format -- Specifies the format in which the blobs will be
returned. Values allowed are _BLOB_FORMAT_BASE64 and
_BLOB_FORMAT_HEX (default _BLOB_FORMAT_BASE64).
+ user_data -- Contains an optional object that will be included
+ in unmodified form in the signed response along with the
+ random data. If an object is present, its maximum size in
+ encoded (string) form is 1,000 characters.
"""
- params = { 'apiKey':self._api_key, 'n':n, 'size':size, 'format':format }
+ params = { 'apiKey':self._api_key, 'n':n, 'size':size,
+ 'format':format, 'userData':user_data }
request = self._generate_request(_SIGNED_BLOB_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_blobs)
+ def get_result(self, serial_number):
+ """
+ Retrieve previously generated results.
+
+ Retrieve results generated using signed methods within the last
+ 24 hours using its serialNumber. See:
+ https://api.random.org/json-rpc/2/signed#getResult
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/2/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+ serial_number -- An integer containing the serial number
+ associated with the response you wish to retrieve.
+
+ """
+ params = { 'apiKey':self._api_key, 'serialNumber':serial_number}
+ request = self._generate_request(_GET_RESULT_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_signed_response(response, self._extract_response)
# Signature verification for signed methods, see:
- # https://api.random.org/json-rpc/1/signing
+ # https://api.random.org/json-rpc/2/signed
def verify_signature(self, random, signature):
"""
@@ -1035,12 +1444,15 @@ def verify_signature(self, random, signature):
of the methods in the Signed API with the server. This is used
to examine the authenticity of numbers. Return True on
verification success. See:
- https://api.random.org/json-rpc/1/signing#verifySignature
+ https://api.random.org/json-rpc/2/signed#verifySignature
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
- Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
server requests allowance has been exceeded and the instance is
@@ -1049,11 +1461,17 @@ def verify_signature(self, random, signature):
Raises a RandomOrgInsufficientBitsError if this API key's
server bits allowance has been exceeded.
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1074,7 +1492,8 @@ def verify_signature(self, random, signature):
# Methods used to create a cache for any given randomness request.
- def create_integer_cache(self, n, min, max, replacement=True, cache_size=20):
+ def create_integer_cache(self, n, min, max, replacement=True,
+ base=10, cache_size=20):
"""
Get a RandomOrgCache to obtain random integers.
@@ -1095,6 +1514,8 @@ def create_integer_cache(self, n, min, max, replacement=True, cache_size=20):
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16.
cache_size -- Number of result-sets for the cache to try to
maintain at any given time (default 20, minimum 2).
"""
@@ -1108,13 +1529,15 @@ def create_integer_cache(self, n, min, max, replacement=True, cache_size=20):
if replacement:
bulk_n = cache_size/2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n,
- 'min':min, 'max':max, 'replacement':replacement }
+ 'min':min, 'max':max, 'replacement':replacement,
+ 'base':base }
# not possible to make the request more efficient
else:
bulk_n = 0
params = { 'apiKey':self._api_key, 'n':n,
- 'min':min, 'max':max, 'replacement':replacement }
+ 'min':min, 'max':max, 'replacement':replacement,
+ 'base':base }
# get the request object for use in all requests from this cache
request = self._generate_request(_INTEGER_METHOD, params)
@@ -1122,7 +1545,67 @@ def create_integer_cache(self, n, min, max, replacement=True, cache_size=20):
return RandomOrgCache(self._send_request, self._extract_ints,
request, cache_size, bulk_n, n)
- def create_decimal_fraction_cache(self, n, decimal_places, replacement=True, cache_size=20):
+ def create_integer_sequences_cache(self, n, length, min, max,
+ replacement=True, base=10,
+ cache_size=20):
+ """
+ Get a RandomOrgCache to obtain random integer sequences.
+
+ The RandomOrgCache can be polled for new results conforming to
+ the output format of the input request. See output of
+ generate_integers() for the return value of a poll on
+ RandomOrgCache.
+
+ Keyword arguments:
+
+ n -- How many sequences of random integers you need. Must be
+ within the [1,1e3] range.
+ length -- Lengths of the sequences requested. Uniform: Must be
+ an integer within the [1,1e4] range. Multiform: an array of
+ length n where each value is within the [1,1e4] range and
+ the sum of all n values is within the [1,1e4] range.
+ min -- The lower boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ max -- The upper boundary for the range from which the random
+ numbers will be picked. Must be within the [-1e9,1e9] range.
+ replacement -- Specifies whether the random numbers should be
+ picked with replacement. If True the resulting numbers may
+ contain duplicate values, otherwise the numbers will all be
+ unique (default True).
+ base -- The base used to display the numbers in the sequences.
+ Must be an integer with the value 2, 8, 10 or 16.
+ cache_size -- Number of result-sets for the cache to try to
+ maintain at any given time (default 20, minimum 2).
+ """
+
+ if cache_size < 2:
+ cache_size = 2
+
+ # if possible, make requests more efficient by bulk-ordering
+ # from the server. Either 5 sets of items at a time, or
+ # cache_size/2 if 5 >= cache_size.
+ if replacement:
+ bulk_n = cache_size/2 if 5 >= cache_size else 5
+ params = { 'apiKey':self._api_key, 'n':bulk_n*n,
+ 'length':length, 'min':min, 'max':max,
+ 'replacement':replacement, 'base':base }
+
+ # not possible to make the request more efficient
+ else:
+ bulk_n = 0
+ params = { 'apiKey':self._api_key, 'n':n, 'length':length,
+ 'min':min, 'max':max, 'replacement':replacement,
+ 'base':base }
+
+ # get the request object for use in all requests from this cache
+ request = self._generate_request(_INTEGER_SEQUENCES_METHOD, params)
+
+ return RandomOrgCache(self._send_request,
+ self._extract_int_sequences,
+ request, cache_size, bulk_n, n)
+
+ def create_decimal_fraction_cache(self, n, decimal_places, replacement=True,
+ cache_size=20):
"""
Get a RandomOrgCache to obtain random decimal fractions.
@@ -1154,13 +1637,15 @@ def create_decimal_fraction_cache(self, n, decimal_places, replacement=True, cac
if replacement:
bulk_n = cache_size/2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n,
- 'decimalPlaces':decimal_places, 'replacement':replacement }
+ 'decimalPlaces':decimal_places,
+ 'replacement':replacement }
# not possible to make the request more efficient
else:
bulk_n = 0
params = { 'apiKey':self._api_key, 'n':n,
- 'decimalPlaces':decimal_places, 'replacement':replacement }
+ 'decimalPlaces':decimal_places,
+ 'replacement':replacement }
# get the request object for use in all requests from this cache
request = self._generate_request(_DECIMAL_FRACTION_METHOD, params)
@@ -1168,7 +1653,8 @@ def create_decimal_fraction_cache(self, n, decimal_places, replacement=True, cac
return RandomOrgCache(self._send_request, self._extract_doubles,
request, cache_size, bulk_n, n)
- def create_gaussian_cache(self, n, mean, standard_deviation, significant_digits, cache_size=20):
+ def create_gaussian_cache(self, n, mean, standard_deviation,
+ significant_digits, cache_size=20):
"""
Get a RandomOrgCache to obtain random numbers.
@@ -1199,7 +1685,8 @@ def create_gaussian_cache(self, n, mean, standard_deviation, significant_digits,
# if 5 >= cache_size.
bulk_n = cache_size/2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'mean':mean,
- 'standardDeviation':standard_deviation, 'significantDigits':significant_digits }
+ 'standardDeviation':standard_deviation,
+ 'significantDigits':significant_digits }
# get the request object for use in all requests from this cache
request = self._generate_request(_GAUSSIAN_METHOD, params)
@@ -1207,7 +1694,8 @@ def create_gaussian_cache(self, n, mean, standard_deviation, significant_digits,
return RandomOrgCache(self._send_request, self._extract_doubles,
request, cache_size, bulk_n, n)
- def create_string_cache(self, n, length, characters, replacement=True, cache_size=20):
+ def create_string_cache(self, n, length, characters, replacement=True,
+ cache_size=20):
"""
Get a RandomOrgCache to obtain random strings.
@@ -1288,7 +1776,8 @@ def create_UUID_cache(self, n, cache_size=10):
return RandomOrgCache(self._send_request, self._extract_UUIDs,
request, cache_size, bulk_n, n)
- def create_blob_cache(self, n, size, format=_BLOB_FORMAT_BASE64, cache_size=10):
+ def create_blob_cache(self, n, size, format=_BLOB_FORMAT_BASE64,
+ cache_size=10):
"""
Get a RandomOrgCache to obtain random blobs.
@@ -1317,7 +1806,8 @@ def create_blob_cache(self, n, size, format=_BLOB_FORMAT_BASE64, cache_size=10):
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
bulk_n = cache_size/2 if 5 >= cache_size else 5
- params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'size':size, 'format':format }
+ params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'size':size,
+ 'format':format }
# get the request object for use in all requests from this cache
request = self._generate_request(_BLOB_METHOD, params)
@@ -1341,6 +1831,9 @@ def get_requests_left(self):
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
@@ -1351,18 +1844,26 @@ def get_requests_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
"""
- if self._requests_left is None or \
- time.clock() > self._last_response_received_time + _ALLOWANCE_STATE_REFRESH_SECONDS:
- self._get_usage()
-
+ try:
+ # Python 2.7
+ if self._requests_left is None or \
+ time.clock() > (self._last_response_received_time
+ + _ALLOWANCE_STATE_REFRESH_SECONDS):
+ self._get_usage()
+ except AttributeError:
+ # Python 3.3+
+ if self._requests_left is None or \
+ time.process_time() > (self._last_response_received_time
+ + _ALLOWANCE_STATE_REFRESH_SECONDS):
+ self._get_usage()
return self._requests_left
def get_bits_left(self):
@@ -1378,6 +1879,9 @@ def get_bits_left(self):
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
Raises a RandomOrgKeyNotRunningError if this API key is stopped.
Raises a RandomOrgInsufficientRequestsError if this API key's
@@ -1388,18 +1892,26 @@ def get_bits_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/1/error-codes
+ https://api.random.org/json-rpc/2/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
"""
- if self._bits_left is None or \
- time.clock() > self._last_response_received_time + _ALLOWANCE_STATE_REFRESH_SECONDS:
- self._get_usage()
-
+ try:
+ # Python 2.7
+ if self._bits_left is None or \
+ time.clock() > (self._last_response_received_time
+ + _ALLOWANCE_STATE_REFRESH_SECONDS):
+ self._get_usage()
+ except AttributeError:
+ # Python 3.3+
+ if self._bits_left is None or \
+ time.process_time() > (self._last_response_received_time
+ + _ALLOWANCE_STATE_REFRESH_SECONDS):
+ self._get_usage()
return self._bits_left
@@ -1421,11 +1933,13 @@ def _send_serialized_request(self, request):
lock = threading.Condition()
lock.acquire()
- data = {'lock': lock, 'request': request, 'response': None, 'exception': None}
+ data = {'lock': lock, 'request': request, 'response': None,
+ 'exception': None}
self._serialized_queue.put(data)
# Wait on the Condition for the specified blocking timeout.
- lock.wait(timeout=None if self._blocking_timeout == -1 else self._blocking_timeout)
+ lock.wait(timeout=None if self._blocking_timeout == -1
+ else self._blocking_timeout)
# Lock has now either been notified or timed out.
# Examine data to determine which and react accordingly.
@@ -1434,9 +1948,12 @@ def _send_serialized_request(self, request):
if data['response'] is None and data['exception'] is None:
data['request'] = None
lock.release()
- raise RandomOrgSendTimeoutError('The defined maximum allowed blocking time of ' +
- str(self._blocking_timeout) + 's has been exceeded \
- while waiting for a synchronous request to send.')
+ raise RandomOrgSendTimeoutError('The defined maximum \
+ allowed blocking time of '
+ + str(self._blocking_timeout)
+ + 's has been exceeded while \
+ waiting for a synchronous \
+ request to send.')
# Exception on sending request.
if data['exception'] is not None:
@@ -1477,7 +1994,8 @@ def _send_request_core(self, request):
if self._backoff is not None:
# Time not yet up, throw exception.
if datetime.utcnow() < self._backoff:
- return { 'exception': RandomOrgInsufficientRequestsError(self._backoff_error) }
+ return { 'exception':
+ RandomOrgInsufficientRequestsError(self._backoff_error) }
# Time is up, clear backoff.
else:
@@ -1486,20 +2004,30 @@ def _send_request_core(self, request):
# Check server advisory delay.
self._advisory_delay_lock.acquire()
- wait = self._advisory_delay - (time.clock() - self._last_response_received_time)
+ try:
+ # Python 2.7
+ wait = self._advisory_delay - (time.clock()
+ - self._last_response_received_time)
+ except AttributeError:
+ # Python 3.3+
+ wait = self._advisory_delay - (time.process_time()
+ - self._last_response_received_time)
self._advisory_delay_lock.release()
# Wait the specified delay if necessary and if wait time is not
# longer than the set blocking_timeout.
if wait > 0:
if (self._blocking_timeout != -1 and wait > self._blocking_timeout):
- return { 'exception': RandomOrgSendTimeoutError('The server advisory delay of ' +
- str(wait) + 's is greater than the defined maximum allowed \
- blocking time of ' + str(self._blocking_timeout) + 's.') }
+ return { 'exception':
+ RandomOrgSendTimeoutError('The server advisory delay of '
+ + str(wait) + 's is greater than \
+ the defined maximum allowed \
+ blocking time of '
+ + str(self._blocking_timeout) + 's.') }
time.sleep(wait)
# Send the request & parse the response.
- response = requests.post('https://api.random.org/json-rpc/1/invoke',
+ response = requests.post('https://api.random.org/json-rpc/2/invoke',
data=json.dumps(request),
headers={'content-type': 'application/json'},
timeout=self._http_timeout)
@@ -1510,35 +2038,64 @@ def _send_request_core(self, request):
message = data['error']['message']
# RuntimeError, error codes listed under JSON-RPC Errors:
- # https://api.random.org/json-rpc/1/error-codes
- if code in [-32700] + range(-32603,-32600) + range(-32099,-32000):
- return { 'exception': RuntimeError('Error ' + str(code) + ': ' + message) }
+ # https://api.random.org/json-rpc/2/error-codes
+ if code in ([-32700] + list(range(-32603,-32600))
+ + list(range(-32099,-32000))):
+ return { 'exception': RuntimeError('Error ' + str(code)
+ + ': ' + message) }
+
+ # RandomOrgKeyNonExistentError, API key does not exist, from
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
+ elif code == 400:
+ return { 'exception':
+ RandomOrgKeyNonExistentError('Error ' + str(code)
+ + ': ' + message) }
# RandomOrgKeyNotRunningError, API key not running, from
- # RANDOM.ORG Errors: https://api.random.org/json-rpc/1/error-codes
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
elif code == 401:
- return { 'exception': RandomOrgKeyNotRunningError('Error ' +
- str(code) + ': ' + message) }
+ return { 'exception':
+ RandomOrgKeyNotRunningError('Error ' + str(code)
+ + ': ' + message) }
# RandomOrgInsufficientRequestsError, requests allowance
# exceeded, backoff until midnight UTC, from RANDOM.ORG
- # Errors: https://api.random.org/json-rpc/1/error-codes
+ # Errors: https://api.random.org/json-rpc/2/error-codes
elif code == 402:
self._backoff = datetime.utcnow().replace(day=datetime.utcnow().day+1, hour=0,
minute=0, second=0, microsecond=0)
self._backoff_error = 'Error ' + str(code) + ': ' + message
- return { 'exception': RandomOrgInsufficientRequestsError(self._backoff_error) }
+ return { 'exception':
+ RandomOrgInsufficientRequestsError(self._backoff_error) }
# RandomOrgInsufficientBitsError, bits allowance exceeded,
- # from RANDOM.ORG Errors: https://api.random.org/json-rpc/1/error-codes
+ # from RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
elif code == 403:
- return { 'exception': RandomOrgInsufficientBitsError('Error ' +
- str(code) + ': ' + message) }
+ return { 'exception':
+ RandomOrgInsufficientBitsError('Error ' + str(code)
+ + ': ' + message) }
+
+ # RandomOrgKeyInvalidAccessError, key is not valid for method
+ # requested, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/2/error-codes
+ elif code == 404:
+ return { 'exception':
+ RandomOrgKeyInvalidAccessError('Error ' + str(code)
+ + ': ' + message) }
+
+ # RandomOrgKeyInvalidVersionError, key is not valid for the
+ # version of the API you are invoking, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/2/error-codes
+ elif code == 405:
+ return { 'exception':
+ RandomOrgKeyInvalidVersionError('Error' + str(code)
+ + ': ' + message)}
# ValueError, error codes listed under RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/1/error-codes
+ # https://api.random.org/json-rpc/2/error-codes
else:
- return { 'exception': ValueError('Error ' + str(code) + ': ' + message) }
+ return { 'exception': ValueError('Error ' + str(code)
+ + ': ' + message) }
# Update usage stats
if 'requestsLeft' in data['result']:
@@ -1549,12 +2106,24 @@ def _send_request_core(self, request):
self._advisory_delay_lock.acquire()
if 'advisoryDelay' in data['result']:
# Convert millis to decimal seconds.
- self._advisory_delay = long(data['result']['advisoryDelay']) / 1000.0
+ if sys.version_info[0] < 3.0:
+ # Python 2.7
+ self._advisory_delay = (long(data['result']['advisoryDelay'])
+ / 1000.0)
+ else:
+ # Python 3+
+ self._advisory_delay = (int(data['result']['advisoryDelay'])
+ / 1000.0)
else:
# Use default if none from server.
self._advisory_delay = _DEFAULT_DELAY
- self._last_response_received_time = time.clock()
+ try:
+ # Python 2.7
+ self._last_response_received_time = time.clock()
+ except AttributeError:
+ # Python 3.3+
+ self._last_response_received_time = time.process_time()
self._advisory_delay_lock.release()
@@ -1568,7 +2137,8 @@ def _get_usage(self):
def _generate_request(self, method, params):
# Base json request.
- return { 'jsonrpc':'2.0', 'method':method, 'params':params, 'id':uuid.uuid4().hex }
+ return { 'jsonrpc':'2.0', 'method':method, 'params':params,
+ 'id':uuid.uuid4().hex }
def _extract_response(self, response):
# Gets random data.
@@ -1579,18 +2149,23 @@ def _extract_signed_response(self, response, extract_function):
return { 'data':extract_function(response),
'random':response['result']['random'],
'signature':response['result']['signature'] }
-
+
def _extract_verification_response(self, response):
# Gets verification boolean.
return bool(response['result']['authenticity'])
def _extract_ints(self, response):
# json to integer list.
- return map(int, self._extract_response(response))
+ return list(map(int, self._extract_response(response)))
+
+ def _extract_int_sequences(self, response):
+ # json to integer sequences list.
+ return [list(map(int, rest)) for rest in
+ self._extract_response(response)]
def _extract_doubles(self, response):
# json to double list.
- return map(float, self._extract_response(response))
+ return list(map(float, self._extract_response(response)))
def _extract_strings(self, response):
# json to string list (no change).
@@ -1598,9 +2173,8 @@ def _extract_strings(self, response):
def _extract_UUIDs(self, response):
# json to UUID list.
- return map(uuid.UUID, self._extract_response(response))
+ return list(map(uuid.UUID, self._extract_response(response)))
def _extract_blobs(self, response):
# json to blob list (no change).
return self._extract_response(response)
-
diff --git a/setup.py b/setup.py
index 424aa2d..f57ff82 100644
--- a/setup.py
+++ b/setup.py
@@ -6,20 +6,34 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.0.2",
+ version = "1.2",
author = "RANDOM.ORG",
author_email = "contact@random.org",
- description = ("RANDOM.ORG JSON-RPC API (Release 1) implementation."),
+ description = ("RANDOM.ORG JSON-RPC API (Release 2) implementation."),
license = "MIT",
keywords = "RANDOM.ORG random client implementation",
- url = "http://packages.python.org/rdoclient",
+ url = "https://www.random.org/",
packages=['rdoclient'],
long_description=read('README.rst'),
+ install_requires=[
+ 'requests',
+ 'six',
+ ],
+ project_urls={
+ "Documentation": "https://api.random.org/json-rpc/2",
+ "Source Code": "https://github.com/RandomOrg/JSON-RPC-Python",
+ },
+ python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.5",
+ "Programming Language :: Python :: 3.6",
+ "Programming Language :: Python :: 3.7",
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
"Topic :: Software Development :: Libraries :: Python Modules",
],
-)
\ No newline at end of file
+)
diff --git a/test_rdoclient.py b/test_rdoclient.py
index 0b7ef8a..a856df3 100644
--- a/test_rdoclient.py
+++ b/test_rdoclient.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 1) implementation tests.
+RANDOM.ORG JSON-RPC API (Release 2) implementation tests.
Run with py.test test_rdoclient.py
"""
@@ -8,10 +8,16 @@
import uuid
from datetime import datetime
-from Queue import Empty
+try:
+ # Python 2.7
+ from Queue import Empty
+except ImportError:
+ # Python 3+
+ from queue import Empty
import unittest
import pytest
+import six # Added for asserting string types (Python 2.7/3.x - unicode/string)
from rdoclient import *
@@ -24,7 +30,8 @@ class TestRandomOrgSerialClient(unittest.TestCase):
def setUp(self):
"""Create client."""
- self._serial_client = RandomOrgClient(_API_KEY_2, blocking_timeout=30, serialized=True)
+ self._serial_client = RandomOrgClient(_API_KEY_1, blocking_timeout=30,
+ serialized=True)
def tearDown(self):
"""Kill all clients."""
@@ -33,7 +40,8 @@ def tearDown(self):
def test_serial_timeout_error(self):
- """Check RandomOrgSendTimeoutError raised when allowed wait time is exceeded for a serial client."""
+ """Check RandomOrgSendTimeoutError raised when allowed wait time is
+ exceeded for a serial client."""
self._serial_client._advisory_delay = 1000
@@ -46,7 +54,8 @@ class TestRandomOrgClient(unittest.TestCase):
def setUp(self):
"""Create client."""
- self._client = RandomOrgClient(_API_KEY_1, blocking_timeout=30, serialized=False)
+ self._client = RandomOrgClient(_API_KEY_1, blocking_timeout=30,
+ serialized=False)
def tearDown(self):
"""Kill all clients."""
@@ -60,7 +69,8 @@ def test_info(self):
def test_api_key_duplication(self):
- """Check new instance isn't created for same api key, and different api key creates different instance."""
+ """Check new instance isn't created for same api key,
+ and different api key creates different instance."""
duplicate = RandomOrgClient(_API_KEY_1, serialized=False)
alternative = RandomOrgClient(_API_KEY_2, serialized=True)
@@ -69,7 +79,8 @@ def test_api_key_duplication(self):
assert duplicate != alternative
def test_runtime_error(self):
- """Check RuntimeError raised for one of several possible error codes, in this case we use "method not found"."""
+ """Check RuntimeError raised for one of several possible error codes,
+ in this case we use "method not found"."""
params = { 'apiKey':_API_KEY_1, 'n':10, 'min':0, 'max':10, 'replacement':True }
request = self._client._generate_request(_FAKE_METHOD, params)
@@ -78,17 +89,29 @@ def test_runtime_error(self):
response = self._client._send_request(request)
def test_value_error(self):
- """Check ValueError raised for an incorectly parameterised request sent to server."""
+ """Check ValueError raised for an incorrectly parameterised request
+ sent to server."""
with pytest.raises(ValueError):
response = self._client.generate_integers(10001, 0, 10)
+ def test_key_non_existent_error(self):
+ """Check RandomOrgKeyNonExistentError raised if key does not exist."""
+
+ roc = RandomOrgClient("ffffffff-ffff-ffff-ffff-ffffffffffff")
+
+ with pytest.raises(RandomOrgKeyNonExistentError):
+ response = roc.generate_integers(10, 0, 10)
+
def test_allowance_exceeded_error(self):
- """Check RandomOrgInsufficientRequestsError raised if UTC backoff is in effect."""
+ """Check RandomOrgInsufficientRequestsError raised if UTC backoff
+ is in effect."""
code = 402
- message = 'The API key has no requsts left today'
- self._client._backoff = datetime.utcnow().replace(day=datetime.utcnow().day+1, hour=0, minute=0, second=0, microsecond=0)
+ message = 'The API key has no requests left today'
+ self._client._backoff = datetime.utcnow().replace(day=datetime.utcnow().day+1,
+ hour=0, minute=0, second=0,
+ microsecond=0)
self._client._backoff_error = 'Error ' + str(code) + ': ' + message
with pytest.raises(RandomOrgInsufficientRequestsError):
@@ -118,6 +141,20 @@ def test_generate_integers(self):
for i in response:
assert isinstance(i, int)
+ def test_generate_integer_sequences(self):
+ """Check generate integer sequences returns
+ sequences of integers."""
+
+ response = self._client.generate_integer_sequences(3, [2, 3, 1],
+ 0, 10)
+
+ assert isinstance(response, list)
+
+ for i in response:
+ assert isinstance(i, list)
+ for j in i:
+ assert isinstance(j, int)
+
def test_generate_decimal_fractions(self):
"""Check generate decimal fractions returns a list of decimals."""
@@ -141,13 +178,15 @@ def test_generate_gaussians(self):
def test_generate_strings(self):
"""Check generate strings returns a list of strings."""
- response = self._client.generate_strings(10, 10, 'abcedfghijklmnopqrstuvwxyz')
+ response = self._client.generate_strings(10, 10,
+ 'abcedfghijklmnopqrstuvwxyz')
assert isinstance(response, list)
for i in response:
- print type(i)
- assert isinstance(i, unicode)
+ # Python 2.7: unicode; Python 3+: string
+ #print(type(i))
+ assert isinstance(i, six.string_types)
def test_generate_UUIDs(self):
"""Check generate UUIDs returns a list of UUIDs."""
@@ -167,11 +206,13 @@ def test_generate_blobs(self):
assert isinstance(response, list)
for i in response:
- assert isinstance(i, unicode)
+ # Python 2.7: unicode; Python 3+: string
+ assert isinstance(i, six.string_types)
def test_generate_signed_integers(self):
- """Check generate signed integers returns a list of integers and can be verified."""
+ """Check generate signed integers returns a list of
+ integers and can be verified."""
response = self._client.generate_signed_integers(10, 0, 10)
@@ -184,10 +225,34 @@ def test_generate_signed_integers(self):
for i in response['data']:
assert isinstance(i, int)
- assert self._client.verify_signature(response['random'], response['signature'])
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
+
+ def test_generate_signed_integer_sequences(self):
+ """Check generate signed integer sequences returns
+ sequences of integers and can be verified."""
+
+ response = self._client.generate_signed_integer_sequences(3,
+ [2, 3, 1],
+ 0, 10)
+
+ assert isinstance(response, dict)
+
+ assert response['data'] is not None
+ assert response['random'] is not None
+ assert response['signature'] is not None
+
+ for i in response['data']:
+ assert isinstance(i, list)
+ for j in i:
+ assert isinstance(j, int)
+
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_generate_signed_decimal_fractions(self):
- """Check generate signed decimal fractions returns a list of decimals and can be verified."""
+ """Check generate signed decimal fractions returns a list of
+ decimals and can be verified."""
response = self._client.generate_signed_decimal_fractions(10, 10)
@@ -200,12 +265,15 @@ def test_generate_signed_decimal_fractions(self):
for i in response['data']:
assert isinstance(i, float)
- assert self._client.verify_signature(response['random'], response['signature'])
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_generate_signed_gaussians(self):
- """Check generate signed gaussians returns a list of decimals and can be verified."""
+ """Check generate signed gaussians returns a list of decimals
+ and can be verified."""
- response = self._client.generate_signed_gaussians(10, 10, 0.5, 5)
+ response = self._client.generate_signed_gaussians(10, 10,
+ 0.5, 5)
assert isinstance(response, dict)
@@ -216,12 +284,15 @@ def test_generate_signed_gaussians(self):
for i in response['data']:
assert isinstance(i, float)
- assert self._client.verify_signature(response['random'], response['signature'])
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_generate_signed_strings(self):
- """Check generate signed strings returns a list of strings and can be verified."""
+ """Check generate signed strings returns a list of strings
+ and can be verified."""
- response = self._client.generate_signed_strings(10, 10, 'abcedfghijklmnopqrstuvwxyz')
+ response = self._client.generate_signed_strings(10, 10,
+ 'abcedfghijklmnopqrstuvwxyz')
assert isinstance(response, dict)
@@ -230,12 +301,15 @@ def test_generate_signed_strings(self):
assert response['signature'] is not None
for i in response['data']:
- assert isinstance(i, unicode)
+ # Python 2.7: unicode; Python 3+: string
+ assert isinstance(i, six.string_types)
- assert self._client.verify_signature(response['random'], response['signature'])
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_generate_signed_UUIDs(self):
- """Check generate signed UUIDs returns a list of UUIDs and can be verified."""
+ """Check generate signed UUIDs returns a list of UUIDs
+ and can be verified."""
response = self._client.generate_signed_UUIDs(10)
@@ -248,10 +322,12 @@ def test_generate_signed_UUIDs(self):
for i in response['data']:
assert isinstance(i, uuid.UUID)
- assert self._client.verify_signature(response['random'], response['signature'])
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_generate_signed_blobs(self):
- """Check generate signed blobs returns a list of blobs and can be verified."""
+ """Check generate signed blobs returns a list of blobs
+ and can be verified."""
response = self._client.generate_signed_blobs(10, 64)
@@ -262,9 +338,30 @@ def test_generate_signed_blobs(self):
assert response['signature'] is not None
for i in response['data']:
- assert isinstance(i, unicode)
+ # Python 2.7: unicode; Python 3+: string
+ assert isinstance(i, six.string_types)
+
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
+
+ def test_get_result(self):
+ """Check get result returns values identical to those
+ originally generated and can be verified."""
+
+ original = self._client.generate_signed_integers(5, 0, 10)
+
+ assert original['random']['serialNumber'] is not None
- assert self._client.verify_signature(response['random'], response['signature'])
+ response = self._client.get_result(original['random']['serialNumber'])
+
+ assert response['data'] is not None
+ assert response['random'] is not None
+ assert response['signature'] is not None
+
+ assert original['data'] == response['data']
+
+ assert self._client.verify_signature(response['random'],
+ response['signature'])
def test_cache(self):
@@ -274,7 +371,7 @@ def test_cache(self):
cache.stop()
with pytest.raises(Empty):
- print cache.get()
+ print(cache.get())
cache.resume()
@@ -306,6 +403,28 @@ def test_create_integer_cache(self):
for g in got:
assert isinstance(g, int)
+ def test_create_integer_sequences_cache(self):
+ """Check integer sequences cache returns sequences of
+ ints on poll."""
+
+ cache = self._client.create_integer_sequences_cache(1, 3, 0, 10,
+ cache_size=2)
+
+ got = None
+
+ while got is None:
+ try:
+ got = cache.get()
+ except Empty:
+ time.sleep(5)
+
+ assert isinstance(got, list)
+
+ for g in got:
+ assert isinstance(g, list)
+ for i in g:
+ assert isinstance(i, int)
+
def test_create_decimal_fraction_cache(self):
"""Check decimal fraction cache returns a list of decimals on poll."""
@@ -345,7 +464,8 @@ def test_create_gaussian_cache(self):
def test_create_string_cache(self):
"""Check string cache returns a list of unicode strings on poll."""
- cache = self._client.create_string_cache(10, 10, 'abcedfghijklmnopqrstuvwxyz', cache_size=2)
+ cache = self._client.create_string_cache(10, 10, 'abcedfghijklmnopqrstuvwxyz',
+ cache_size=2)
got = None
@@ -358,7 +478,8 @@ def test_create_string_cache(self):
assert isinstance(got, list)
for g in got:
- assert isinstance(g, unicode)
+ # Python 2.7: unicode; Python 3+: string
+ assert isinstance(g, six.string_types)
def test_create_UUID_cache(self):
"""Check UUID cache returns a list of UUIDs on poll."""
@@ -394,11 +515,10 @@ def test_create_blob_cache(self):
assert isinstance(got, list)
for g in got:
- assert isinstance(g, unicode)
-
+ # Python 2.7: unicode; Python 3+: string
+ assert isinstance(g, six.string_types)
def test_cached_info(self):
assert isinstance(self._client.get_requests_left(), int)
assert isinstance(self._client.get_bits_left(), int)
-
-
+
\ No newline at end of file
From 414af93dc7793e4c3d5a0d24c2fd75756a2af90b Mon Sep 17 00:00:00 2001
From: = <=>
Date: Fri, 27 Nov 2020 15:18:49 +0000
Subject: [PATCH 2/8] Release 3 Update
This library now supports Release 3 of the RANDOM.ORG JSON-RPC API.
Updates:
- Error codes (420: RandomOrgTicketNonExistentError, 421: RandomOrgTicketAPIKeyMismatchError, 422: RandomOrgTicketAlreadyUsedError, 423: RandomOrgTooManySingletonTicketsError)
- New methods (create_tickets, list_tickets and get_ticket)
- New optional paramter 'ticket_id' added to signed methods returning random values
- New test cases in test_rdoclient.py to handle the new methods and error codes
As specified in the Release 2 Update, this library can now be used for Python 2.7 & Python >= 3.5.
---
README.rst | 8 +-
rdoclient/__init__.py | 14 +-
rdoclient/rdoclient.py | 588 ++++++++++++++++++++++++++++++++++-------
setup.py | 8 +-
test_rdoclient.py | 91 ++++++-
5 files changed, 600 insertions(+), 109 deletions(-)
diff --git a/README.rst b/README.rst
index 890dd92..d33148c 100644
--- a/README.rst
+++ b/README.rst
@@ -1,9 +1,9 @@
JSON-RPC-Python
===============
-RANDOM.ORG JSON-RPC API (Release 2) implementation.
+RANDOM.ORG JSON-RPC API (Release 3) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R2). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R3). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
Installation
------------
@@ -76,9 +76,9 @@ Finally, it is possible to request live results as-soon-as-possible and without
Documentation
-------------
-For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/2
+For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/3
Tests
-----
-Note that to run the accompanying tests the _API_KEY_1 field in test_rdoclient.py must be given an authentic value, i.e. a valid API key. The _API_KEY_2 field does not need to be changed.
+Note that to run the accompanying tests the _API_KEY_1 field in test_rdoclient.py must be changed to contain a valid API key. The _API_KEY_2 field does not need to be changed.
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index dd18a7d..4a7d52e 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 2) implementation.
+RANDOM.ORG JSON-RPC API (Release 3) implementation.
Usage:
@@ -44,11 +44,11 @@
[3, 5, 2, 4, 8]
For a full list of available randomness generation functions see
-rdoclient.py documentation and https://api.random.org/json-rpc/2
+rdoclient.py documentation and https://api.random.org/json-rpc/3
"""
__title__ = 'rdoclient'
-__version__ = '1.0.0'
+__version__ = '1.3.0'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
@@ -57,12 +57,16 @@
from .rdoclient import (RandomOrgClient, RandomOrgCache, RandomOrgSendTimeoutError,
RandomOrgKeyNonExistentError, RandomOrgKeyNotRunningError,
RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError,
- RandomOrgKeyInvalidAccessError, RandomOrgKeyInvalidVersionError)
+ RandomOrgKeyInvalidAccessError, RandomOrgKeyInvalidVersionError,
+ RandomOrgTicketNonExistentError, RandomOrgTicketAPIKeyMismatchError,
+ RandomOrgTicketAlreadyUsedError, RandomOrgTooManySingletonTicketsError)
__all__ = [ 'RandomOrgClient', 'RandomOrgCache', 'RandomOrgSendTimeoutError',
'RandomOrgKeyNonExistentError', 'RandomOrgKeyNotRunningError',
'RandomOrgInsufficientRequestsError', 'RandomOrgInsufficientBitsError',
- 'RandomOrgKeyInvalidAccessError', 'RandomOrgKeyInvalidVersionError' ]
+ 'RandomOrgKeyInvalidAccessError', 'RandomOrgKeyInvalidVersionError',
+ 'RandomOrgTicketNonExistentError', 'RandomOrgTicketAPIKeyMismatchError',
+ 'RandomOrgTicketAlreadyUsedError', 'RandomOrgTooManySingletonTicketsError' ]
import logging
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index ebe4d1a..1558bf3 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -1,7 +1,7 @@
"""
-RANDOM.ORG JSON-RPC API (Release 2) implementation.
+RANDOM.ORG JSON-RPC API (Release 3) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R2).
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R3).
It provides either serialized or unserialized access to both the signed
and unsigned methods of the API through the RandomOrgClient class. It
also provides a convenience class through the RandomOrgClient class,
@@ -23,10 +23,21 @@
RandomOrgInsufficientBitsError -- bits allowance exceeded.
-RandomOrgKeyInvalidAccessError -- key is not valid for the requested method
+RandomOrgKeyInvalidAccessError -- key is not valid for the requested
+ method
-RandomOrgKeyInvalidVersionError -- key is not valid for the version of the API
+RandomOrgKeyInvalidVersionError -- key is not valid for the version
+ of the API
+RandomOrgTicketNonExistentError -- ticket does not exist.
+
+RandomOrgTicketAPIKeyMismatchError -- ticket cannot be used with
+ key specified.
+
+RandomOrgTicketAlreadyUsedError -- ticket has already been used.
+
+RandomOrgTooManySingletonTicketsError -- singleton ticket allowance
+ exceeded.
"""
from collections import OrderedDict
@@ -47,7 +58,7 @@
import requests
-# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/2/basic
+# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/3/basic
_INTEGER_METHOD = 'generateIntegers'
_INTEGER_SEQUENCES_METHOD = 'generateIntegerSequences'
_DECIMAL_FRACTION_METHOD = 'generateDecimalFractions'
@@ -57,7 +68,7 @@
_BLOB_METHOD = 'generateBlobs'
_GET_USAGE_METHOD = 'getUsage'
-# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/2/signed
+# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/3/signed
_SIGNED_INTEGER_METHOD = 'generateSignedIntegers'
_SIGNED_INTEGER_SEQUENCES_METHOD = 'generateSignedIntegerSequences'
_SIGNED_DECIMAL_FRACTION_METHOD = 'generateSignedDecimalFractions'
@@ -66,6 +77,9 @@
_SIGNED_UUID_METHOD = 'generateSignedUUIDs'
_SIGNED_BLOB_METHOD = 'generateSignedBlobs'
_GET_RESULT_METHOD = 'getResult'
+_CREATE_TICKETS_METHOD = 'createTickets'
+_LIST_TICKETS_METHOD = 'listTickets'
+_GET_TICKET_METHOD = 'getTicket'
_VERIFY_SIGNATURE_METHOD = 'verifySignature'
# Blob format literals
@@ -143,6 +157,42 @@ class RandomOrgKeyInvalidVersionError(Exception):
complete.
"""
+class RandomOrgTicketNonExistentError(Exception):
+ """
+ RandomOrgClient ticket does not exist.
+
+ Exception raised by the RandomOrgClient class when the ticket
+ specified does not exist.
+ """
+
+class RandomOrgTicketAPIKeyMismatchError(Exception):
+ """
+ RandomOrgClient ticket exists but is not for the API key specified.
+
+ Exception raised by the RandomOrgClient class when the ticket
+ specified exists but does not belong to the API key currently in
+ use.
+ """
+
+class RandomOrgTicketAlreadyUsedError(Exception):
+ """
+ RandomOrgClient ticket has already been used.
+
+ Exception raised by the RandomOrgClient class when the ticket
+ specified has already been used, i.e. it cannot be used for
+ another request.
+ """
+
+class RandomOrgTooManySingletonTicketsError(Exception):
+ """
+ RandomOrgClient key has reached the maximum number
+ of singleton tickets allowed.
+
+ Exception raised by the RandomOrgClient class when the maximum
+ number of singleton tickets associated with the API key specified
+ has been reached.
+ """
+
class RandomOrgCache(object):
"""
RandomOrgCache for precaching request responses.
@@ -337,7 +387,7 @@ class RandomOrgClient(object):
that instance will be returned on init instead of a new instance.
This class obeys most of the guidelines set forth in
- https://api.random.org/guidelines
+ https://api.random.org/json-rpc/3
All requests respect the server's advisoryDelay returned in any
responses, or use _DEFAULT_DELAY if no advisoryDelay is returned. If
the supplied API key is has exceeded its daily request allowance,
@@ -346,7 +396,7 @@ class RandomOrgClient(object):
Public methods:
Basic methods for generating randomness, see:
- https://api.random.org/json-rpc/2/basic
+ https://api.random.org/json-rpc/3/basic
generate_integers -- get a list of random integers.
generate_integer_sequences -- get sequences of random integers.
@@ -357,7 +407,7 @@ class RandomOrgClient(object):
generate_blobs -- get a list of random blobs.
Signed methods for generating randomness, see:
- https://api.random.org/json-rpc/2/signed
+ https://api.random.org/json-rpc/3/signed
generate_signed_integers -- get a signed response containing a list
of random integers and a signature.
@@ -375,13 +425,22 @@ class RandomOrgClient(object):
random blobs and a signature.
Retrieving previously generated signed results (within 24h), see:
- https://api.random.org/json-rpc/2/signed#getResult
+ https://api.random.org/json-rpc/3/signed#getResult
get_result -- retrieve previously generated signed results using
a serial number (restricted to within 24 hours after generation)
+ Tickets for use in methods which generate signed random values, see:
+ https://api.random.org/json-rpc/3/signed
+
+ create_tickets -- create tickets for use in methods that generate
+ random values with signatures
+ list_tickets -- obtain a list of tickets of a certain type (singleton,
+ head or tail)
+ get_ticket -- obtain information on a single ticket
+
Signature verification for signed methods, see:
- https://api.random.org/json-rpc/2/signed
+ https://api.random.org/json-rpc/3/signed
verify_signature -- verify a response against its signature.
@@ -500,7 +559,7 @@ def __init__(self, api_key,
# Basic methods for generating randomness, see:
- # https://api.random.org/json-rpc/2/basic
+ # https://api.random.org/json-rpc/3/basic
def generate_integers(self, n, min, max, replacement=True, base=10):
"""
@@ -508,7 +567,7 @@ def generate_integers(self, n, min, max, replacement=True, base=10):
Request and return a list (size n) of true random integers
within a user-defined range from the server. See:
- https://api.random.org/json-rpc/2/basic#generateIntegers
+ https://api.random.org/json-rpc/3/basic#generateIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -532,10 +591,10 @@ def generate_integers(self, n, min, max, replacement=True, base=10):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -570,7 +629,7 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
Request and return a list (size n) of uniform or multiform
sequences of true random integers
within a user-defined range from the server. See:
- https://api.random.org/json-rpc/2/basic#generateIntegerSequences
+ https://api.random.org/json-rpc/3/basic#generateIntegerSequences
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -594,10 +653,10 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -627,7 +686,7 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
taken from the same set.
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
- 'max':max, 'replacement':replacement, 'base':base}
+ 'max':max, 'replacement':replacement, 'base':base }
request = self._generate_request(_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
return self._extract_int_sequences(response)
@@ -640,7 +699,7 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
fractions, from a uniform distribution across the [0,1]
interval with a user-defined number of decimal places from the
server. See:
- https://api.random.org/json-rpc/2/basic#generateDecimalFractions
+ https://api.random.org/json-rpc/3/basic#generateDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -664,10 +723,10 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -698,7 +757,7 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
a Gaussian distribution (also known as a normal distribution).
The form uses a Box-Muller Transform to generate the Gaussian
distribution from uniformly distributed numbers. See:
- https://api.random.org/json-rpc/2/basic#generateGaussians
+ https://api.random.org/json-rpc/3/basic#generateGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -722,10 +781,10 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -755,7 +814,7 @@ def generate_strings(self, n, length, characters, replacement=True):
Request and return a list (size n) of true random unicode
strings from the server. See:
- https://api.random.org/json-rpc/2/basic#generateStrings
+ https://api.random.org/json-rpc/3/basic#generateStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -779,10 +838,10 @@ def generate_strings(self, n, length, characters, replacement=True):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -815,7 +874,7 @@ def generate_UUIDs(self, n):
Request and return a list (size n) of version 4 true random
Universally Unique IDentifiers (UUIDs) in accordance with
section 4.4 of RFC 4122, from the server. See:
- https://api.random.org/json-rpc/2/basic#generateUUIDs
+ https://api.random.org/json-rpc/3/basic#generateUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -839,10 +898,10 @@ def generate_UUIDs(self, n):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -865,7 +924,7 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
Request and return a list (size n) of Binary Large OBjects
(BLOBs) as unicode strings containing true random data from the
server. See:
- https://api.random.org/json-rpc/2/basic#generateBlobs
+ https://api.random.org/json-rpc/3/basic#generateBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -889,10 +948,10 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -915,10 +974,10 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
# Signed methods for generating randomness, see:
- # https://api.random.org/json-rpc/2/signed
+ # https://api.random.org/json-rpc/3/signed
def generate_signed_integers(self, n, min, max, replacement=True,
- base=10, user_data=None):
+ base=10, user_data=None, ticket_id=None):
"""
Generate digitally signed random integers.
@@ -927,7 +986,7 @@ def generate_signed_integers(self, n, min, max, replacement=True,
with the parsed integer list mapped to 'data', the original
response mapped to 'random', and the response's signature
mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedIntegers
+ https://api.random.org/json-rpc/3/signed#generateSignedIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -950,11 +1009,23 @@ def generate_signed_integers(self, n, min, max, replacement=True,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -977,17 +1048,22 @@ def generate_signed_integers(self, n, min, max, replacement=True,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max,
- 'replacement':replacement, 'base':base, 'userData':user_data }
+ 'replacement':replacement, 'base':base, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_ints)
- def generate_signed_integer_sequences(self, n, length, min, max,
- replacement=True, base=10,
- user_data=None):
+ def generate_signed_integer_sequences(self, n, length, min, max,
+ replacement=True, base=10,
+ user_data=None, ticket_id=None):
"""
Generate digitally signed sequences of random integers.
@@ -996,7 +1072,7 @@ def generate_signed_integer_sequences(self, n, length, min, max,
with the parsed integer list mapped to 'data', the original
response mapped to 'random', and the response's signature
mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedIntegerSequences
+ https://api.random.org/json-rpc/3/signed#generateSignedIntegerSequences
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1019,11 +1095,23 @@ def generate_signed_integer_sequences(self, n, length, min, max,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1055,17 +1143,22 @@ def generate_signed_integer_sequences(self, n, length, min, max,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
'max':max, 'replacement':replacement, 'base':base,
- 'userData':user_data}
+ 'userData':user_data, 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_int_sequences)
- def generate_signed_decimal_fractions(self, n, decimal_places,
- replacement=True, user_data=None):
+ def generate_signed_decimal_fractions(self, n, decimal_places,
+ replacement=True, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random decimal fractions.
@@ -1075,7 +1168,7 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
a dictionary object with the parsed decimal fraction list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedDecimalFractions
+ https://api.random.org/json-rpc/3/signed#generateSignedDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1098,11 +1191,23 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1121,17 +1226,22 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n,
'decimalPlaces':decimal_places, 'replacement':replacement,
- 'userData':user_data }
+ 'userData':user_data, 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_DECIMAL_FRACTION_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
def generate_signed_gaussians(self, n, mean, standard_deviation,
- significant_digits, user_data=None):
+ significant_digits, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random numbers.
@@ -1142,7 +1252,7 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
dictionary object with the parsed random number list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedGaussians
+ https://api.random.org/json-rpc/3/signed#generateSignedGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1165,11 +1275,23 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1188,18 +1310,23 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n, 'mean':mean,
'standardDeviation':standard_deviation,
'significantDigits':significant_digits,
- 'userData':user_data }
+ 'userData':user_data, 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_GAUSSIAN_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
def generate_signed_strings(self, n, length, characters,
- replacement=True, user_data=None):
+ replacement=True, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random strings.
@@ -1207,7 +1334,7 @@ def generate_signed_strings(self, n, length, characters,
Returns a dictionary object with the parsed random string list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedStrings
+ https://api.random.org/json-rpc/3/signed#generateSignedStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1230,11 +1357,23 @@ def generate_signed_strings(self, n, length, characters,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1256,16 +1395,20 @@ def generate_signed_strings(self, n, length, characters,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length,
'characters':characters, 'replacement':replacement,
- 'userData':user_data }
+ 'userData':user_data, 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_STRING_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_strings)
- def generate_signed_UUIDs(self, n, user_data=None):
+ def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
"""
Generate digitally signed random UUIDs.
@@ -1275,7 +1418,7 @@ def generate_signed_UUIDs(self, n, user_data=None):
parsed random UUID list mapped to 'data', the original response
mapped to 'random', and the response's signature mapped to
'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedUUIDs
+ https://api.random.org/json-rpc/3/signed#generateSignedUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1298,11 +1441,23 @@ def generate_signed_UUIDs(self, n, user_data=None):
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1315,15 +1470,20 @@ def generate_signed_UUIDs(self, n, user_data=None):
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
- params = { 'apiKey':self._api_key, 'n':n, 'userData':user_data }
+ params = { 'apiKey':self._api_key, 'n':n, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_UUID_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_UUIDs)
- def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
- user_data=None):
+ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
+ user_data=None, ticket_id=None):
"""
Generate digitally signed random BLOBs.
@@ -1332,7 +1492,7 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
dictionary object with the parsed random BLOB list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/2/signed#generateSignedBlobs
+ https://api.random.org/json-rpc/3/signed#generateSignedBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1355,11 +1515,23 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketAPIKeyMismatchError if the ticket used
+ exists but is not associated with the API key specified.
+
+ Raises a RandomOrgTicketAlreadyUsedError if a ticket has already
+ been used.
+
+ Raises a RandomOrgTooManySingletonTicketsError when the maximum
+ number of singleton tickets for this API key has been reached.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1377,10 +1549,15 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
encoded (string) form is 1,000 characters.
+ ticket_id -- A string with ticket identifier obtained via the
+ create_tickets method. Specifying a value for ticket_id will
+ cause RANDOM.ORG to record that the ticket was used to generate
+ the requested random values. Each ticket can only be used once.
"""
params = { 'apiKey':self._api_key, 'n':n, 'size':size,
- 'format':format, 'userData':user_data }
+ 'format':format, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_BLOB_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_blobs)
@@ -1391,7 +1568,7 @@ def get_result(self, serial_number):
Retrieve results generated using signed methods within the last
24 hours using its serialNumber. See:
- https://api.random.org/json-rpc/2/signed#getResult
+ https://api.random.org/json-rpc/3/signed#getResult
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1415,10 +1592,10 @@ def get_result(self, serial_number):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1433,8 +1610,166 @@ def get_result(self, serial_number):
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_response)
+ def create_tickets(self, n, show_result):
+ """
+ Create tickets to be used in signed value-generating methods.
+
+ This method creates a number of tickets. The tickets can be
+ used in one of the methods that generate random values. See:
+ https://api.random.org/json-rpc/3/signed#createTickets
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+
+ n -- The number of tickets requested. This must be a number
+ in the [1, 50] range.
+ showResult -- A boolean value that determines how much information
+ calls to get_ticket will return. If show_result is false, getTicket
+ will return only the basic ticket information. If show_result is
+ true, the full random and signature objects from the response that
+ was used to satisfy the ticket is returned.
+ """
+
+ params = { 'apiKey':self._api_key, 'n':n, 'showResult':show_result }
+ request = self._generate_request(_CREATE_TICKETS_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_tickets(response)
+
+ def list_tickets(self, ticket_type):
+ """
+ Obtain information about tickets linked with your API key.
+
+ This method obtains information about tickets that exist
+ for a given API key. See:
+ https://api.random.org/json-rpc/3/signed#listTickets
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+
+ ticket_type -- A string describing the type of tickets you want to obtain
+ information about. Possible values are singleton, head and tail.
+ Specifying singleton will cause list_tickets to return tickets that
+ have no previous or next tickets. Specifying head will return tickets
+ that do not have a previous ticket but that do have a next ticket.
+ Specifying tail will cause list_tickets to return tickets that have a
+ previous ticket but do not have a next ticket.
+ """
+ params = { 'apiKey':self._api_key, 'ticketType':ticket_type }
+ request = self._generate_request(_LIST_TICKETS_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_tickets(response)
+
+ def get_ticket(self, ticket_id):
+ """
+ Obtain information about a single ticket.
+
+ This method obtains information about a single ticket. If
+ the ticket has showResult set to true and has been used,
+ get ticket will return the values generated. See:
+ https://api.random.org/json-rpc/3/signed#getTicket
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/3/error-codes
+
+ Can also raise connection errors as described here:
+ http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+
+ Keyword arguments:
+
+ ticket_id -- A string containing a ticket identifier returned by a prior
+ call to the create_tickets method.
+ """
+
+ params = { 'ticketId':ticket_id }
+ request = self._generate_request(_GET_TICKET_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_tickets(response)
+
+
# Signature verification for signed methods, see:
- # https://api.random.org/json-rpc/2/signed
+ # https://api.random.org/json-rpc/3/signed
def verify_signature(self, random, signature):
"""
@@ -1444,7 +1779,7 @@ def verify_signature(self, random, signature):
of the methods in the Signed API with the server. This is used
to examine the authenticity of numbers. Return True on
verification success. See:
- https://api.random.org/json-rpc/2/signed#verifySignature
+ https://api.random.org/json-rpc/3/signed#verifySignature
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1468,10 +1803,10 @@ def verify_signature(self, random, signature):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1484,6 +1819,11 @@ def verify_signature(self, random, signature):
the random field originates from.
"""
+ # Ensuring that ticketData is in the correct order for
+ # Release 3 and older versions of Python where dictionaries
+ # are not ordered
+ if sys.version_info[0] < 3.6:
+ random = self._order_ticket_data(random)
params = { 'random':random, 'signature':signature }
request = self._generate_request(_VERIFY_SIGNATURE_METHOD, params)
response = self._send_request(request)
@@ -1604,7 +1944,7 @@ def create_integer_sequences_cache(self, n, length, min, max,
self._extract_int_sequences,
request, cache_size, bulk_n, n)
- def create_decimal_fraction_cache(self, n, decimal_places, replacement=True,
+ def create_decimal_fraction_cache(self, n, decimal_places, replacement=True,
cache_size=20):
"""
Get a RandomOrgCache to obtain random decimal fractions.
@@ -1844,10 +2184,10 @@ def get_requests_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1892,10 +2232,10 @@ def get_bits_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/2/error-codes
+ https://api.random.org/json-rpc/3/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -2027,7 +2367,7 @@ def _send_request_core(self, request):
time.sleep(wait)
# Send the request & parse the response.
- response = requests.post('https://api.random.org/json-rpc/2/invoke',
+ response = requests.post('https://api.random.org/json-rpc/3/invoke',
data=json.dumps(request),
headers={'content-type': 'application/json'},
timeout=self._http_timeout)
@@ -2038,21 +2378,21 @@ def _send_request_core(self, request):
message = data['error']['message']
# RuntimeError, error codes listed under JSON-RPC Errors:
- # https://api.random.org/json-rpc/2/error-codes
+ # https://api.random.org/json-rpc/3/error-codes
if code in ([-32700] + list(range(-32603,-32600))
+ list(range(-32099,-32000))):
return { 'exception': RuntimeError('Error ' + str(code)
+ ': ' + message) }
# RandomOrgKeyNonExistentError, API key does not exist, from
- # RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
elif code == 400:
return { 'exception':
RandomOrgKeyNonExistentError('Error ' + str(code)
+ ': ' + message) }
# RandomOrgKeyNotRunningError, API key not running, from
- # RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
elif code == 401:
return { 'exception':
RandomOrgKeyNotRunningError('Error ' + str(code)
@@ -2060,7 +2400,7 @@ def _send_request_core(self, request):
# RandomOrgInsufficientRequestsError, requests allowance
# exceeded, backoff until midnight UTC, from RANDOM.ORG
- # Errors: https://api.random.org/json-rpc/2/error-codes
+ # Errors: https://api.random.org/json-rpc/3/error-codes
elif code == 402:
self._backoff = datetime.utcnow().replace(day=datetime.utcnow().day+1, hour=0,
minute=0, second=0, microsecond=0)
@@ -2069,7 +2409,7 @@ def _send_request_core(self, request):
RandomOrgInsufficientRequestsError(self._backoff_error) }
# RandomOrgInsufficientBitsError, bits allowance exceeded,
- # from RANDOM.ORG Errors: https://api.random.org/json-rpc/2/error-codes
+ # from RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
elif code == 403:
return { 'exception':
RandomOrgInsufficientBitsError('Error ' + str(code)
@@ -2077,7 +2417,7 @@ def _send_request_core(self, request):
# RandomOrgKeyInvalidAccessError, key is not valid for method
# requested, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/2/error-codes
+ # https://api.random.org/json-rpc/3/error-codes
elif code == 404:
return { 'exception':
RandomOrgKeyInvalidAccessError('Error ' + str(code)
@@ -2085,14 +2425,50 @@ def _send_request_core(self, request):
# RandomOrgKeyInvalidVersionError, key is not valid for the
# version of the API you are invoking, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/2/error-codes
+ # https://api.random.org/json-rpc/3/error-codes
elif code == 405:
return { 'exception':
RandomOrgKeyInvalidVersionError('Error' + str(code)
+ ': ' + message)}
+ # RandomOrgTicketNonExistentError, the ticket specified does
+ # not exist, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/3/error-codes
+ elif code == 420:
+ return { 'exception':
+ RandomOrgTicketNonExistentError('Error' + str(code)
+ + ': ' + message)}
+
+ # RandomOrgTicketAPIKeyMismatchError, the ticket specified
+ # exists but is not for the API key you specified,
+ # from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/3/error-codes
+ elif code == 421:
+ return { 'exception':
+ RandomOrgTicketAPIKeyMismatchError('Error' + str(code)
+ + ': ' + message)}
+
+ # RandomOrgTicketAlreadyUsedError, the ticket specified has
+ # already been used, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/3/error-codes
+ elif code == 422:
+ return { 'exception':
+ RandomOrgTicketAlreadyUsedError('Error' + str(code)
+ + ': ' + message)}
+
+ # RandomOrgTooManySingletonTicketsError, the maximum number
+ # of singleton tickets available for your API key has been
+ # reached, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/3/error-codes
+ elif code == 423:
+ return { 'exception':
+ RandomOrgTooManySingletonTicketsError('Error'
+ + str(code)
+ + ': '
+ + message)}
+
# ValueError, error codes listed under RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/2/error-codes
+ # https://api.random.org/json-rpc/3/error-codes
else:
return { 'exception': ValueError('Error ' + str(code)
+ ': ' + message) }
@@ -2154,6 +2530,10 @@ def _extract_verification_response(self, response):
# Gets verification boolean.
return bool(response['result']['authenticity'])
+ def _extract_tickets(self, response):
+ # Gets list of tickets for create_tickets method
+ return response['result']
+
def _extract_ints(self, response):
# json to integer list.
return list(map(int, self._extract_response(response)))
@@ -2178,3 +2558,21 @@ def _extract_UUIDs(self, response):
def _extract_blobs(self, response):
# json to blob list (no change).
return self._extract_response(response)
+
+ def _order_ticket_data(self, random):
+ # orders the information in ticketData to ensure successful signature
+ # verification for Python 2.7 (R3)
+ if random['ticketData'] is not None:
+ random_ordered = OrderedDict()
+ for key in random:
+ if key == 'ticketData':
+ ticket_data = OrderedDict()
+ ticket_data['ticketId'] = random['ticketData']['ticketId']
+ ticket_data['previousTicketId'] = random['ticketData']['previousTicketId']
+ ticket_data['nextTicketId'] = random['ticketData']['nextTicketId']
+ random_ordered[key] = ticket_data
+ else:
+ random_ordered[key] = random[key]
+ return random_ordered
+ else:
+ return random
diff --git a/setup.py b/setup.py
index f57ff82..b4be204 100644
--- a/setup.py
+++ b/setup.py
@@ -6,10 +6,10 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.2",
+ version = "1.3",
author = "RANDOM.ORG",
author_email = "contact@random.org",
- description = ("RANDOM.ORG JSON-RPC API (Release 2) implementation."),
+ description = ("RANDOM.ORG JSON-RPC API (Release 3) implementation."),
license = "MIT",
keywords = "RANDOM.ORG random client implementation",
url = "https://www.random.org/",
@@ -20,12 +20,12 @@ def read(fname):
'six',
],
project_urls={
- "Documentation": "https://api.random.org/json-rpc/2",
+ "Documentation": "https://api.random.org/json-rpc/3",
"Source Code": "https://github.com/RandomOrg/JSON-RPC-Python",
},
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
classifiers=[
- "Development Status :: 3 - Alpha",
+ "Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
diff --git a/test_rdoclient.py b/test_rdoclient.py
index a856df3..56fc09e 100644
--- a/test_rdoclient.py
+++ b/test_rdoclient.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 2) implementation tests.
+RANDOM.ORG JSON-RPC API (Release 3) implementation tests.
Run with py.test test_rdoclient.py
"""
@@ -130,6 +130,34 @@ def test_timeout_error(self):
self._client._advisory_delay = 1.0
+ def test_ticket_non_existent_error(self):
+ """Check RandomOrgTicketNonExistentError raised when a
+ ticket does not exist."""
+
+ with pytest.raises(RandomOrgTicketNonExistentError):
+ response = self._client.generate_signed_integers(10, 0, 10,
+ ticket_id="ffffffffffffffff")
+
+ def test_ticket_key_mismatch_error(self):
+ """Check RandomOrgTicketAPIKeyMismatchError raised when the ticket
+ is not for the API key."""
+
+ with pytest.raises(RandomOrgTicketAPIKeyMismatchError):
+ response = self._client.generate_signed_integers(10, 0, 10,
+ ticket_id="d5b8f6d03f99a134")
+
+ def test_ticket_already_used_error(self):
+ """Check RandomOrgTicketAlreadyUsedError raised when ticket has
+ already been used."""
+
+ ticket = self._client.create_tickets(1, True)
+ ticket_id = ticket[0]['ticketId']
+ response = self._client.generate_signed_integers(10, 0, 10,
+ ticket_id=ticket_id)
+
+ with pytest.raises(RandomOrgTicketAlreadyUsedError):
+ response = self._client.generate_signed_integers(10, 0, 10,
+ ticket_id=ticket_id)
def test_generate_integers(self):
"""Check generate integers returns a list of integers."""
@@ -363,6 +391,67 @@ def test_get_result(self):
assert self._client.verify_signature(response['random'],
response['signature'])
+ def test_create_tickets(self):
+ """Check create tickets returns a list of tickets."""
+ response = self._client.create_tickets(2, False)
+
+ assert response is not None
+
+ for i in response:
+ assert i['ticketId'] is not None
+
+ def test_list_tickets(self):
+ """Check list tickets returns a list of tickets of the correct type."""
+ response = self._client.list_tickets("singleton")
+
+ if response is not None:
+ for i in response:
+ assert i['ticketId'] is not None
+ assert i['nextTicketId'] is None
+ assert i['previousTicketId'] is None
+
+ response = self._client.list_tickets("head")
+
+ if response is not None:
+ for i in response:
+ assert i['ticketId'] is not None
+ assert i['nextTicketId'] is not None
+ assert i['previousTicketId'] is None
+
+ response = self._client.list_tickets("tail")
+
+ if response is not None:
+ for i in response:
+ assert i['ticketId'] is not None
+ assert i['nextTicketId'] is None
+ assert i['previousTicketId'] is not None
+
+ def test_get_ticket(self):
+ """Check get ticket returns a ticket and that (when used) the
+ results can be verified."""
+ ticket = self._client.create_tickets(1, True)
+
+ assert ticket[0] is not None
+ assert ticket[0]['ticketId'] is not None
+
+ ticket_id = ticket[0]['ticketId']
+ response = self._client.get_ticket(ticket_id)
+ assert response['ticketId'] == ticket_id
+ assert response['showResult']
+ assert response['usedTime'] is None
+
+ random_integers = self._client.generate_signed_integers(5, 0, 10,
+ ticket_id=ticket_id)
+ assert self._client.verify_signature(random_integers['random'],
+ random_integers['signature'])
+
+ response = self._client.get_ticket(ticket_id)
+
+ assert response['ticketId'] == ticket_id
+ assert response['showResult']
+ assert response['usedTime'] is not None
+ assert self._client.verify_signature(response['result']['random'],
+ response['result']['signature'])
def test_cache(self):
"""Test empty cache and stop/resume functionality."""
From 45f951815b78487b83159a0a2a3f02162278b1ab Mon Sep 17 00:00:00 2001
From: = <=>
Date: Tue, 15 Dec 2020 15:59:51 +0000
Subject: [PATCH 3/8] Bug Fix: "base" parameter
- Non-decimal base parameters for generate_integers, generate_integer_sequences, generate_signed_integers and generate_signed_integer_sequences return string/unicode values
- Python 3.+ bug fix for creating caches
Note: These updates are part of the 1.3.1 release of the rdoclient package on PyPI.
---
rdoclient/__init__.py | 2 +-
rdoclient/rdoclient.py | 73 ++++++++++++++++++++++++++++--------------
setup.py | 2 +-
test_rdoclient.py | 2 ++
4 files changed, 53 insertions(+), 26 deletions(-)
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index 4a7d52e..8774e88 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -48,7 +48,7 @@
"""
__title__ = 'rdoclient'
-__version__ = '1.3.0'
+__version__ = '1.3.1'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index 1558bf3..e60bb4f 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -245,6 +245,12 @@ def __init__(self, request_function, process_function, request,
self._bulk_request_number = bulk_request_number
self._request_number = request_number
+ # Handle integers with non-decimal base
+ if 'base' in self._request['params']:
+ self._decimal = self._request['params']['base'] == 10
+ else:
+ self._decimal = True
+
# Condition lock to allow notification when an item is consumed
# or pause state is updated.
self._lock = threading.Condition()
@@ -279,9 +285,12 @@ def _populate_queue(self):
- self._bulk_request_number):
# Issue and process request and response.
- try:
+ try:
response = self._request_function(self._request)
- result = self._process_function(response)
+ if self._decimal:
+ result = self._process_function(response)
+ else:
+ result = self._process_function(response, self._decimal)
# Split bulk response into result sets.
try:
@@ -309,8 +318,10 @@ def _populate_queue(self):
elif not self._queue.full():
try:
response = self._request_function(self._request)
- self._queue.put(self._process_function(response))
-
+ if self._decimal:
+ self._queue.put(self._process_function(response))
+ else:
+ self._queue.put(self._process_function(response, self._decimal))
except Exception as e:
# Don't handle failures from _request_function()
# Just try again later.
@@ -619,7 +630,7 @@ def generate_integers(self, n, min, max, replacement=True, base=10):
'replacement':replacement, 'base':base }
request = self._generate_request(_INTEGER_METHOD, params)
response = self._send_request(request)
- return self._extract_ints(response)
+ return self._extract_ints(response, base == 10)
def generate_integer_sequences(self, n, length, min, max, replacement=True,
base=10):
@@ -689,7 +700,7 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
'max':max, 'replacement':replacement, 'base':base }
request = self._generate_request(_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
- return self._extract_int_sequences(response)
+ return self._extract_int_sequences(response, base == 10)
def generate_decimal_fractions(self, n, decimal_places, replacement=True):
"""
@@ -1059,7 +1070,7 @@ def generate_signed_integers(self, n, min, max, replacement=True,
'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_METHOD, params)
response = self._send_request(request)
- return self._extract_signed_response(response, self._extract_ints)
+ return self._extract_signed_response(response, self._extract_ints, base == 10)
def generate_signed_integer_sequences(self, n, length, min, max,
replacement=True, base=10,
@@ -1154,7 +1165,8 @@ def generate_signed_integer_sequences(self, n, length, min, max,
'userData':user_data, 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
- return self._extract_signed_response(response, self._extract_int_sequences)
+ return self._extract_signed_response(response, self._extract_int_sequences,
+ base == 10)
def generate_signed_decimal_fractions(self, n, decimal_places,
replacement=True, user_data=None,
@@ -1867,7 +1879,7 @@ def create_integer_cache(self, n, min, max, replacement=True,
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
if replacement:
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n,
'min':min, 'max':max, 'replacement':replacement,
'base':base }
@@ -1925,7 +1937,7 @@ def create_integer_sequences_cache(self, n, length, min, max,
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
if replacement:
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n,
'length':length, 'min':min, 'max':max,
'replacement':replacement, 'base':base }
@@ -1975,7 +1987,7 @@ def create_decimal_fraction_cache(self, n, decimal_places, replacement=True,
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
if replacement:
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n,
'decimalPlaces':decimal_places,
'replacement':replacement }
@@ -2023,7 +2035,7 @@ def create_gaussian_cache(self, n, mean, standard_deviation,
# make requests more efficient by bulk-ordering from the
# server. Either 5 sets of items at a time, or cache_size/2
# if 5 >= cache_size.
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'mean':mean,
'standardDeviation':standard_deviation,
'significantDigits':significant_digits }
@@ -2068,7 +2080,7 @@ def create_string_cache(self, n, length, characters, replacement=True,
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
if replacement:
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'length':length,
'characters':characters, 'replacement':replacement }
@@ -2107,7 +2119,7 @@ def create_UUID_cache(self, n, cache_size=10):
# make requests more efficient by bulk-ordering
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n }
# get the request object for use in all requests from this cache
@@ -2145,7 +2157,7 @@ def create_blob_cache(self, n, size, format=_BLOB_FORMAT_BASE64,
# make requests more efficient by bulk-ordering
# from the server. Either 5 sets of items at a time, or
# cache_size/2 if 5 >= cache_size.
- bulk_n = cache_size/2 if 5 >= cache_size else 5
+ bulk_n = cache_size//2 if 5 >= cache_size else 5
params = { 'apiKey':self._api_key, 'n':bulk_n*n, 'size':size,
'format':format }
@@ -2520,11 +2532,17 @@ def _extract_response(self, response):
# Gets random data.
return response['result']['random']['data']
- def _extract_signed_response(self, response, extract_function):
+ def _extract_signed_response(self, response, extract_function,
+ decimal=True):
# Gets all random data and signature.
- return { 'data':extract_function(response),
- 'random':response['result']['random'],
- 'signature':response['result']['signature'] }
+ if decimal:
+ return { 'data':extract_function(response),
+ 'random':response['result']['random'],
+ 'signature':response['result']['signature'] }
+ else:
+ return { 'data':extract_function(response, decimal),
+ 'random':response['result']['random'],
+ 'signature':response['result']['signature']}
def _extract_verification_response(self, response):
# Gets verification boolean.
@@ -2534,14 +2552,21 @@ def _extract_tickets(self, response):
# Gets list of tickets for create_tickets method
return response['result']
- def _extract_ints(self, response):
+ def _extract_ints(self, response, decimal=True):
# json to integer list.
- return list(map(int, self._extract_response(response)))
+ if decimal:
+ return list(map(int, self._extract_response(response)))
+ else:
+ return self._extract_response(response)
- def _extract_int_sequences(self, response):
+ def _extract_int_sequences(self, response, decimal=True):
# json to integer sequences list.
- return [list(map(int, rest)) for rest in
- self._extract_response(response)]
+ if decimal:
+ return [list(map(int, rest)) for rest in
+ self._extract_response(response)]
+ else:
+ return [list(rest) for rest
+ in self._extract_response(response)]
def _extract_doubles(self, response):
# json to double list.
diff --git a/setup.py b/setup.py
index b4be204..5c7eba4 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.3",
+ version = "1.3.1",
author = "RANDOM.ORG",
author_email = "contact@random.org",
description = ("RANDOM.ORG JSON-RPC API (Release 3) implementation."),
diff --git a/test_rdoclient.py b/test_rdoclient.py
index 56fc09e..f278442 100644
--- a/test_rdoclient.py
+++ b/test_rdoclient.py
@@ -35,6 +35,7 @@ def setUp(self):
def tearDown(self):
"""Kill all clients."""
+ delattr(self._serial_client, '_api_key')
self._serial_client = None
RandomOrgClient.__key_indexed_instances = {}
@@ -59,6 +60,7 @@ def setUp(self):
def tearDown(self):
"""Kill all clients."""
+ delattr(self._client, '_api_key')
self._client = None
RandomOrgClient.__key_indexed_instances = {}
From df364f27892997cc7adb88b4d83d0208cc6887ac Mon Sep 17 00:00:00 2001
From: = <=>
Date: Fri, 2 Apr 2021 12:52:02 +0100
Subject: [PATCH 4/8] Release 4 Updates
This library now supports Release 4 of the RANDOM.ORG JSON-RPC API.
Updates:
- New optional parameter 'pregenerated_randomization' added to all value-generating methods, Basic and Signed API
- New optional parameter 'license_data' added to all signed value-generating methods
- Error codes (424: RandomOrgLicenseDataRequiredError, 425: RandomOrgLicenseDataNotAllowedError)
- Removed '_order_ticket_data' method as R4 does not have this issue when using Python 2.7
- Added a test for the optional 'pregenerated_randomization' parameter
- Updated README, setup and __init__ to specify R4 instead of R3
- Added methods to generate the signature verification URL and HTML forms (create_url and create_html) using the random object and signature returned from any of the signed (value generating) methods; the URL and HTML form link to the same web page that is also shown when a result is verified using the online Signature Verification Form, see: https://api.random.org/signatures/form
As specified in the Release 2 and 3 updates, this library can be used for Python 2.7 & Python >= 3.5.
---
README.rst | 6 +-
rdoclient/__init__.py | 6 +-
rdoclient/rdoclient.py | 704 ++++++++++++++++++++++++++++++++---------
setup.py | 6 +-
test_rdoclient.py | 23 +-
5 files changed, 588 insertions(+), 157 deletions(-)
diff --git a/README.rst b/README.rst
index d33148c..81a3655 100644
--- a/README.rst
+++ b/README.rst
@@ -1,9 +1,9 @@
JSON-RPC-Python
===============
-RANDOM.ORG JSON-RPC API (Release 3) implementation.
+RANDOM.ORG JSON-RPC API (Release 4) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R3). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R4). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
Installation
------------
@@ -76,7 +76,7 @@ Finally, it is possible to request live results as-soon-as-possible and without
Documentation
-------------
-For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/3
+For a full list of available randomness generation functions and other features see rdoclient.py documentation and https://api.random.org/json-rpc/4
Tests
-----
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index 8774e88..94eb8f2 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 3) implementation.
+RANDOM.ORG JSON-RPC API (Release 4) implementation.
Usage:
@@ -44,11 +44,11 @@
[3, 5, 2, 4, 8]
For a full list of available randomness generation functions see
-rdoclient.py documentation and https://api.random.org/json-rpc/3
+rdoclient.py documentation and https://api.random.org/json-rpc/4
"""
__title__ = 'rdoclient'
-__version__ = '1.3.1'
+__version__ = '1.4'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index e60bb4f..db86ac7 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -1,7 +1,7 @@
"""
-RANDOM.ORG JSON-RPC API (Release 3) implementation.
+RANDOM.ORG JSON-RPC API (Release 4) implementation.
-This is a Python implementation of the RANDOM.ORG JSON-RPC API (R3).
+This is a Python implementation of the RANDOM.ORG JSON-RPC API (R4).
It provides either serialized or unserialized access to both the signed
and unsigned methods of the API through the RandomOrgClient class. It
also provides a convenience class through the RandomOrgClient class,
@@ -24,10 +24,10 @@
RandomOrgInsufficientBitsError -- bits allowance exceeded.
RandomOrgKeyInvalidAccessError -- key is not valid for the requested
- method
+ method.
RandomOrgKeyInvalidVersionError -- key is not valid for the version
- of the API
+ of the API.
RandomOrgTicketNonExistentError -- ticket does not exist.
@@ -38,9 +38,14 @@
RandomOrgTooManySingletonTicketsError -- singleton ticket allowance
exceeded.
+RandomOrgLicenseDataRequiredError -- your API key type requires valid
+ license data.
+RandomOrgLicenseDataNotAllowedError -- your API key type does not support
+ the license data parameter.
"""
from collections import OrderedDict
+import base64
import json
import logging
import threading
@@ -58,7 +63,7 @@
import requests
-# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/3/basic
+# Basic RANDOM.ORG API functions https://api.random.org/json-rpc/4/basic
_INTEGER_METHOD = 'generateIntegers'
_INTEGER_SEQUENCES_METHOD = 'generateIntegerSequences'
_DECIMAL_FRACTION_METHOD = 'generateDecimalFractions'
@@ -68,7 +73,7 @@
_BLOB_METHOD = 'generateBlobs'
_GET_USAGE_METHOD = 'getUsage'
-# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/3/signed
+# Signed RANDOM.ORG API functions https://api.random.org/json-rpc/4/signed
_SIGNED_INTEGER_METHOD = 'generateSignedIntegers'
_SIGNED_INTEGER_SEQUENCES_METHOD = 'generateSignedIntegerSequences'
_SIGNED_DECIMAL_FRACTION_METHOD = 'generateSignedDecimalFractions'
@@ -193,6 +198,24 @@ class RandomOrgTooManySingletonTicketsError(Exception):
has been reached.
"""
+class RandomOrgLicenseDataRequiredError(Exception):
+ """
+ RandomOrgClient key requires the license data parameter.
+
+ Exception raised by the RandomOrgClient class when the license data
+ parameter of a signed method returning random values is not supplied,
+ but the type of API key specified requires the request to contain valid
+ license data.
+ """
+
+class RandomOrgLicenseDataNotAllowedError(Exception):
+ """
+ RandomOrgClient key does not support license data.
+
+ Exception raised by the RandomOrgClient class when the license data parameter
+ is used but the API key supplied does not support the use of license data.
+ """
+
class RandomOrgCache(object):
"""
RandomOrgCache for precaching request responses.
@@ -285,7 +308,7 @@ def _populate_queue(self):
- self._bulk_request_number):
# Issue and process request and response.
- try:
+ try:
response = self._request_function(self._request)
if self._decimal:
result = self._process_function(response)
@@ -337,7 +360,7 @@ def stop(self):
"""
Stop cache.
- Cache will not contine to populate itself.
+ Cache will not continue to populate itself.
"""
self._paused = True
@@ -398,7 +421,7 @@ class RandomOrgClient(object):
that instance will be returned on init instead of a new instance.
This class obeys most of the guidelines set forth in
- https://api.random.org/json-rpc/3
+ https://api.random.org/json-rpc/4
All requests respect the server's advisoryDelay returned in any
responses, or use _DEFAULT_DELAY if no advisoryDelay is returned. If
the supplied API key is has exceeded its daily request allowance,
@@ -407,7 +430,7 @@ class RandomOrgClient(object):
Public methods:
Basic methods for generating randomness, see:
- https://api.random.org/json-rpc/3/basic
+ https://api.random.org/json-rpc/4/basic
generate_integers -- get a list of random integers.
generate_integer_sequences -- get sequences of random integers.
@@ -418,7 +441,7 @@ class RandomOrgClient(object):
generate_blobs -- get a list of random blobs.
Signed methods for generating randomness, see:
- https://api.random.org/json-rpc/3/signed
+ https://api.random.org/json-rpc/4/signed
generate_signed_integers -- get a signed response containing a list
of random integers and a signature.
@@ -436,13 +459,13 @@ class RandomOrgClient(object):
random blobs and a signature.
Retrieving previously generated signed results (within 24h), see:
- https://api.random.org/json-rpc/3/signed#getResult
+ https://api.random.org/json-rpc/4/signed#getResult
get_result -- retrieve previously generated signed results using
a serial number (restricted to within 24 hours after generation)
Tickets for use in methods which generate signed random values, see:
- https://api.random.org/json-rpc/3/signed
+ https://api.random.org/json-rpc/4/signed
create_tickets -- create tickets for use in methods that generate
random values with signatures
@@ -451,7 +474,7 @@ class RandomOrgClient(object):
get_ticket -- obtain information on a single ticket
Signature verification for signed methods, see:
- https://api.random.org/json-rpc/3/signed
+ https://api.random.org/json-rpc/4/signed
verify_signature -- verify a response against its signature.
@@ -570,15 +593,16 @@ def __init__(self, api_key,
# Basic methods for generating randomness, see:
- # https://api.random.org/json-rpc/3/basic
+ # https://api.random.org/json-rpc/4/basic
- def generate_integers(self, n, min, max, replacement=True, base=10):
+ def generate_integers(self, n, min, max, replacement=True, base=10,
+ pregenerated_randomization=None):
"""
Generate random integers.
Request and return a list (size n) of true random integers
within a user-defined range from the server. See:
- https://api.random.org/json-rpc/3/basic#generateIntegers
+ https://api.random.org/json-rpc/4/basic#generateIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -602,10 +626,10 @@ def generate_integers(self, n, min, max, replacement=True, base=10):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -624,23 +648,38 @@ def generate_integers(self, n, min, max, replacement=True, base=10):
unique (default True).
base -- The base used to display the numbers in the sequences.
Must be an integer with the value 2, 8, 10 or 16.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max,
- 'replacement':replacement, 'base':base }
+ 'replacement':replacement, 'base':base,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_INTEGER_METHOD, params)
response = self._send_request(request)
return self._extract_ints(response, base == 10)
def generate_integer_sequences(self, n, length, min, max, replacement=True,
- base=10):
+ base=10, pregenerated_randomization=None):
"""
Generate random integer sequences.
Request and return a list (size n) of uniform or multiform
sequences of true random integers
within a user-defined range from the server. See:
- https://api.random.org/json-rpc/3/basic#generateIntegerSequences
+ https://api.random.org/json-rpc/4/basic#generateIntegerSequences
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -664,10 +703,10 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -695,14 +734,30 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
Must be an integer with the value 2, 8, 10 or 16. For multiform
sequences the values may be an array of length n with values
taken from the same set.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
- 'max':max, 'replacement':replacement, 'base':base }
+ 'max':max, 'replacement':replacement, 'base':base,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
return self._extract_int_sequences(response, base == 10)
- def generate_decimal_fractions(self, n, decimal_places, replacement=True):
+ def generate_decimal_fractions(self, n, decimal_places, replacement=True,
+ pregenerated_randomization=None):
"""
Generate random decimal fractions.
@@ -710,7 +765,7 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
fractions, from a uniform distribution across the [0,1]
interval with a user-defined number of decimal places from the
server. See:
- https://api.random.org/json-rpc/3/basic#generateDecimalFractions
+ https://api.random.org/json-rpc/4/basic#generateDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -734,10 +789,10 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -752,15 +807,31 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True):
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
params = { 'apiKey':self._api_key, 'n':n,
- 'decimalPlaces':decimal_places, 'replacement':replacement }
+ 'decimalPlaces':decimal_places, 'replacement':replacement,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_DECIMAL_FRACTION_METHOD, params)
response = self._send_request(request)
return self._extract_doubles(response)
- def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
+ def generate_gaussians(self, n, mean, standard_deviation, significant_digits,
+ pregenerated_randomization=None):
"""
Generate random numbers.
@@ -768,7 +839,7 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
a Gaussian distribution (also known as a normal distribution).
The form uses a Box-Muller Transform to generate the Gaussian
distribution from uniformly distributed numbers. See:
- https://api.random.org/json-rpc/3/basic#generateGaussians
+ https://api.random.org/json-rpc/4/basic#generateGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -792,10 +863,10 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -810,22 +881,38 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits):
Must be within the [-1e6,1e6] range.
significant_digits -- The number of significant digits to use.
Must be within the [2,20] range.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
params = { 'apiKey':self._api_key, 'n':n, 'mean':mean,
'standardDeviation':standard_deviation,
- 'significantDigits':significant_digits }
+ 'significantDigits':significant_digits,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_GAUSSIAN_METHOD, params)
response = self._send_request(request)
return self._extract_doubles(response)
- def generate_strings(self, n, length, characters, replacement=True):
+ def generate_strings(self, n, length, characters, replacement=True,
+ pregenerated_randomization=None):
"""
Generate random strings.
Request and return a list (size n) of true random unicode
strings from the server. See:
- https://api.random.org/json-rpc/3/basic#generateStrings
+ https://api.random.org/json-rpc/4/basic#generateStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -849,10 +936,10 @@ def generate_strings(self, n, length, characters, replacement=True):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -870,22 +957,37 @@ def generate_strings(self, n, length, characters, replacement=True):
picked with replacement. If True the resulting list of
strings may contain duplicates, otherwise the strings will
all be unique (default True).
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length,
- 'characters':characters, 'replacement':replacement }
+ 'characters':characters, 'replacement':replacement,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_STRING_METHOD, params)
response = self._send_request(request)
return self._extract_strings(response)
- def generate_UUIDs(self, n):
+ def generate_UUIDs(self, n, pregenerated_randomization=None):
"""
Generate random UUIDs.
Request and return a list (size n) of version 4 true random
Universally Unique IDentifiers (UUIDs) in accordance with
section 4.4 of RFC 4122, from the server. See:
- https://api.random.org/json-rpc/3/basic#generateUUIDs
+ https://api.random.org/json-rpc/4/basic#generateUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -909,10 +1011,10 @@ def generate_UUIDs(self, n):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -921,21 +1023,37 @@ def generate_UUIDs(self, n):
n -- How many random UUIDs you need. Must be within the [1,1e3]
range.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
"""
- params = { 'apiKey':self._api_key, 'n':n }
+ params = { 'apiKey':self._api_key, 'n':n,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_UUID_METHOD, params)
response = self._send_request(request)
return self._extract_UUIDs(response)
- def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
+ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
+ pregenerated_randomization=None):
"""
Generate random BLOBs.
Request and return a list (size n) of Binary Large OBjects
(BLOBs) as unicode strings containing true random data from the
server. See:
- https://api.random.org/json-rpc/3/basic#generateBlobs
+ https://api.random.org/json-rpc/4/basic#generateBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -959,10 +1077,10 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -976,19 +1094,36 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64):
format -- Specifies the format in which the blobs will be
returned. Values allowed are _BLOB_FORMAT_BASE64 and
_BLOB_FORMAT_HEX (default _BLOB_FORMAT_BASE64).
- """
-
- params = { 'apiKey':self._api_key, 'n':n, 'size':size, 'format':format }
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ """
+
+ params = { 'apiKey':self._api_key, 'n':n, 'size':size, 'format':format,
+ 'pregeneratedRandomization':pregenerated_randomization }
request = self._generate_request(_BLOB_METHOD, params)
response = self._send_request(request)
return self._extract_blobs(response)
# Signed methods for generating randomness, see:
- # https://api.random.org/json-rpc/3/signed
+ # https://api.random.org/json-rpc/4/signed
def generate_signed_integers(self, n, min, max, replacement=True,
- base=10, user_data=None, ticket_id=None):
+ base=10, pregenerated_randomization=None,
+ license_data=None, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random integers.
@@ -997,7 +1132,7 @@ def generate_signed_integers(self, n, min, max, replacement=True,
with the parsed integer list mapped to 'data', the original
response mapped to 'random', and the response's signature
mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedIntegers
+ https://api.random.org/json-rpc/4/signed#generateSignedIntegers
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1033,10 +1168,10 @@ def generate_signed_integers(self, n, min, max, replacement=True,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1055,6 +1190,26 @@ def generate_signed_integers(self, n, min, max, replacement=True,
unique (default True).
base -- The base used to display the numbers in the sequences.
Must be an integer with the value 2, 8, 10 or 16.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1066,7 +1221,9 @@ def generate_signed_integers(self, n, min, max, replacement=True,
"""
params = { 'apiKey':self._api_key, 'n':n, 'min':min, 'max':max,
- 'replacement':replacement, 'base':base, 'userData':user_data,
+ 'replacement':replacement, 'base':base,
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_METHOD, params)
response = self._send_request(request)
@@ -1074,7 +1231,9 @@ def generate_signed_integers(self, n, min, max, replacement=True,
def generate_signed_integer_sequences(self, n, length, min, max,
replacement=True, base=10,
- user_data=None, ticket_id=None):
+ pregenerated_randomization=None,
+ license_data=None, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed sequences of random integers.
@@ -1083,7 +1242,7 @@ def generate_signed_integer_sequences(self, n, length, min, max,
with the parsed integer list mapped to 'data', the original
response mapped to 'random', and the response's signature
mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedIntegerSequences
+ https://api.random.org/json-rpc/4/signed#generateSignedIntegerSequences
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1119,10 +1278,10 @@ def generate_signed_integer_sequences(self, n, length, min, max,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1150,6 +1309,26 @@ def generate_signed_integer_sequences(self, n, length, min, max,
Must be an integer with the value 2, 8, 10 or 16. For multiform
sequences the values may be an array of length n with values
taken from the same set.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1162,14 +1341,18 @@ def generate_signed_integer_sequences(self, n, length, min, max,
params = { 'apiKey':self._api_key, 'n':n, 'length':length, 'min':min,
'max':max, 'replacement':replacement, 'base':base,
- 'userData':user_data, 'ticketId':ticket_id }
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_INTEGER_SEQUENCES_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_int_sequences,
base == 10)
def generate_signed_decimal_fractions(self, n, decimal_places,
- replacement=True, user_data=None,
+ replacement=True,
+ pregenerated_randomization=None,
+ license_data=None, user_data=None,
ticket_id=None):
"""
Generate digitally signed random decimal fractions.
@@ -1180,7 +1363,7 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
a dictionary object with the parsed decimal fraction list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedDecimalFractions
+ https://api.random.org/json-rpc/4/signed#generateSignedDecimalFractions
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1216,10 +1399,10 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1234,6 +1417,26 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
picked with replacement. If True the resulting numbers may
contain duplicate values, otherwise the numbers will all be
unique (default True).
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1246,13 +1449,17 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
params = { 'apiKey':self._api_key, 'n':n,
'decimalPlaces':decimal_places, 'replacement':replacement,
- 'userData':user_data, 'ticketId':ticket_id }
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_DECIMAL_FRACTION_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
def generate_signed_gaussians(self, n, mean, standard_deviation,
- significant_digits, user_data=None,
+ significant_digits,
+ pregenerated_randomization=None,
+ license_data=None, user_data=None,
ticket_id=None):
"""
Generate digitally signed random numbers.
@@ -1264,7 +1471,7 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
dictionary object with the parsed random number list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedGaussians
+ https://api.random.org/json-rpc/4/signed#generateSignedGaussians
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1300,10 +1507,10 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1318,6 +1525,26 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
Must be within the [-1e6,1e6] range.
significant_digits -- The number of significant digits to use.
Must be within the [2,20] range.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1330,14 +1557,18 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
params = { 'apiKey':self._api_key, 'n':n, 'mean':mean,
'standardDeviation':standard_deviation,
- 'significantDigits':significant_digits,
- 'userData':user_data, 'ticketId':ticket_id }
+ 'significantDigits':significant_digits,
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_GAUSSIAN_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_doubles)
def generate_signed_strings(self, n, length, characters,
- replacement=True, user_data=None,
+ replacement=True,
+ pregenerated_randomization=None,
+ license_data=None, user_data=None,
ticket_id=None):
"""
Generate digitally signed random strings.
@@ -1346,7 +1577,7 @@ def generate_signed_strings(self, n, length, characters,
Returns a dictionary object with the parsed random string list
mapped to 'data', the original response mapped to 'random', and
the response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedStrings
+ https://api.random.org/json-rpc/4/signed#generateSignedStrings
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1382,10 +1613,10 @@ def generate_signed_strings(self, n, length, characters,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1403,6 +1634,26 @@ def generate_signed_strings(self, n, length, characters,
picked with replacement. If True the resulting list of
strings may contain duplicates, otherwise the strings will
all be unique (default True).
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1414,13 +1665,17 @@ def generate_signed_strings(self, n, length, characters,
"""
params = { 'apiKey':self._api_key, 'n':n, 'length':length,
- 'characters':characters, 'replacement':replacement,
- 'userData':user_data, 'ticketId':ticket_id }
+ 'characters':characters, 'replacement':replacement,
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
+ 'ticketId':ticket_id }
request = self._generate_request(_SIGNED_STRING_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_strings)
- def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
+ def generate_signed_UUIDs(self, n, pregenerated_randomization=None,
+ license_data=None, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random UUIDs.
@@ -1430,7 +1685,7 @@ def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
parsed random UUID list mapped to 'data', the original response
mapped to 'random', and the response's signature mapped to
'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedUUIDs
+ https://api.random.org/json-rpc/4/signed#generateSignedUUIDs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1466,10 +1721,10 @@ def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1478,6 +1733,26 @@ def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
n -- How many random UUIDs you need. Must be within the [1,1e3]
range.
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1488,14 +1763,18 @@ def generate_signed_UUIDs(self, n, user_data=None, ticket_id=None):
the requested random values. Each ticket can only be used once.
"""
- params = { 'apiKey':self._api_key, 'n':n, 'userData':user_data,
+ params = { 'apiKey':self._api_key, 'n':n,
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
'ticketId':ticket_id }
request = self._generate_request(_SIGNED_UUID_METHOD, params)
response = self._send_request(request)
return self._extract_signed_response(response, self._extract_UUIDs)
def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
- user_data=None, ticket_id=None):
+ pregenerated_randomization=None,
+ license_data=None, user_data=None,
+ ticket_id=None):
"""
Generate digitally signed random BLOBs.
@@ -1504,7 +1783,7 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
dictionary object with the parsed random BLOB list mapped to
'data', the original response mapped to 'random', and the
response's signature mapped to 'signature'. See:
- https://api.random.org/json-rpc/3/signed#generateSignedBlobs
+ https://api.random.org/json-rpc/4/signed#generateSignedBlobs
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1540,10 +1819,10 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
number of singleton tickets for this API key has been reached.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1557,6 +1836,26 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
format -- Specifies the format in which the blobs will be
returned. Values allowed are _BLOB_FORMAT_BASE64 and
_BLOB_FORMAT_HEX (default _BLOB_FORMAT_BASE64).
+ pregenerated_randomization -- Allows the client to specify that
+ the random values should be generated from a pregenerated,
+ historical randomization instead of a one-time on-the-fly
+ randomization. There are three possible cases:
+ - null: the standard way of calling for random values, i.e.
+ true randomness is generated and discarded afterwards
+ - date: RANDOM.ORG uses historical true randomness generated
+ on the corresponding date (past or present, format:
+ { "date": "YYYY-MM-DD" })
+ - id: RANDOM.ORG uses historical true randomness derived
+ from the corresponding identifier in a deterministic
+ manner. Format: { "id": "PERSISTENT-IDENTIFIER" } where
+ "PERSISTENT-IDENTIFIER" is a string with length in the
+ [1,64] range
+ license_data -- Allows the caller to include data of relevance to
+ the license that is associated with the API Key.This is mandatory
+ for API Keys with the license type "Flexible Gambling" and follows
+ the format { "maxPayout": { "currency": "XTS", "amount": 0.0 } }.
+ This information is used in licensing requested random values and
+ in billing. The currently supported currencies are: "USD".
user_data -- Contains an optional object that will be included
in unmodified form in the signed response along with the
random data. If an object is present, its maximum size in
@@ -1568,7 +1867,9 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
"""
params = { 'apiKey':self._api_key, 'n':n, 'size':size,
- 'format':format, 'userData':user_data,
+ 'format':format,
+ 'pregeneratedRandomization':pregenerated_randomization,
+ 'licenseData':license_data, 'userData':user_data,
'ticketId':ticket_id }
request = self._generate_request(_SIGNED_BLOB_METHOD, params)
response = self._send_request(request)
@@ -1580,7 +1881,7 @@ def get_result(self, serial_number):
Retrieve results generated using signed methods within the last
24 hours using its serialNumber. See:
- https://api.random.org/json-rpc/3/signed#getResult
+ https://api.random.org/json-rpc/4/signed#getResult
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1604,10 +1905,10 @@ def get_result(self, serial_number):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1628,7 +1929,7 @@ def create_tickets(self, n, show_result):
This method creates a number of tickets. The tickets can be
used in one of the methods that generate random values. See:
- https://api.random.org/json-rpc/3/signed#createTickets
+ https://api.random.org/json-rpc/4/signed#createTickets
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1652,10 +1953,10 @@ def create_tickets(self, n, show_result):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1682,7 +1983,7 @@ def list_tickets(self, ticket_type):
This method obtains information about tickets that exist
for a given API key. See:
- https://api.random.org/json-rpc/3/signed#listTickets
+ https://api.random.org/json-rpc/4/signed#listTickets
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1706,10 +2007,10 @@ def list_tickets(self, ticket_type):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1736,7 +2037,7 @@ def get_ticket(self, ticket_id):
This method obtains information about a single ticket. If
the ticket has showResult set to true and has been used,
get ticket will return the values generated. See:
- https://api.random.org/json-rpc/3/signed#getTicket
+ https://api.random.org/json-rpc/4/signed#getTicket
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1759,11 +2060,14 @@ def get_ticket(self, ticket_id):
Raises a RandomOrgKeyInvalidVersionError if this API key is not
valid for the version of the API invoked.
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1781,7 +2085,7 @@ def get_ticket(self, ticket_id):
# Signature verification for signed methods, see:
- # https://api.random.org/json-rpc/3/signed
+ # https://api.random.org/json-rpc/4/signed
def verify_signature(self, random, signature):
"""
@@ -1791,7 +2095,7 @@ def verify_signature(self, random, signature):
of the methods in the Signed API with the server. This is used
to examine the authenticity of numbers. Return True on
verification success. See:
- https://api.random.org/json-rpc/3/signed#verifySignature
+ https://api.random.org/json-rpc/4/signed#verifySignature
Raises a RandomOrgSendTimeoutError if time spent waiting before
request is sent exceeds this instance's blocking_timeout.
@@ -1815,10 +2119,10 @@ def verify_signature(self, random, signature):
valid for the version of the API invoked.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -1831,16 +2135,96 @@ def verify_signature(self, random, signature):
the random field originates from.
"""
- # Ensuring that ticketData is in the correct order for
- # Release 3 and older versions of Python where dictionaries
- # are not ordered
- if sys.version_info[0] < 3.6:
- random = self._order_ticket_data(random)
params = { 'random':random, 'signature':signature }
request = self._generate_request(_VERIFY_SIGNATURE_METHOD, params)
response = self._send_request(request)
return self._extract_verification_response(response)
+
+ def create_url(self, random, signature):
+ """
+ Create the URL for the signature verification page of a signed
+ response.
+
+ Create the URL for the signature verification page of a response
+ previously received from one of the methods in the Signed API with
+ the server. The web-page accessible from this URL will contain the
+ details of the response used in this method, provided that the
+ signature can be verified. This URL is also shown under "Show
+ Technical Details" when the online Signature Verification Form is
+ used to validate a signature. See:
+ https://api.random.org/signatures/form
+
+ Please note that, when using Python 2.7, the URL generated by this
+ method may differ from that shown when using the online Signature
+ Verification Form. This is because dictionaries in Python 2.7 are
+ not ordered and the encoded string will reflect this. The URL will
+ still work as expected.
+
+ Raises a ValueError when the length of the generated URL exceeds
+ the maximum length allowed (2,046 characters). The random object
+ may be too large, i.e., too many random values were requested.
+
+ Keyword arguments:
+
+ random -- The random field from a response returned by
+ RANDOM.ORG through one of the Signed API methods.
+ signature -- The signature field from the same response that
+ the random field originates from.
+ """
+ # ensure that input is formatted correctly and is url-safe
+ random = self._url_formatting(random, True)
+ signature = self._url_formatting(signature)
+
+ # create full url
+ url = 'https://api.random.org/signatures/form?format=json'
+ url += '&random=' + random
+ url += '&signature=' + signature
+ # throw an error is the maximum length allowed (2,046 characters)
+ # is exceeded
+ if len(url) > 2046:
+ return ValueError('Error: URL exceeds maximum length (2,046 characters).')
+
+ return url
+
+ def create_html(self, random, signature):
+ """
+ Create the HTML form for the signature verification page of a signed
+ response.
+
+ Create the HTML form for the signature verification page of a response
+ previously received from one of the methods in the Signed API with
+ the server. The web-page accessible from the "Validate" button created
+ will contain the details of the response used in this method, provided
+ that the signature can be verified. The same HTML form is also shown
+ under "Show Technical Details" when the online Signature Verification
+ Form is used to validate a signature. See:
+ https://api.random.org/signatures/form
+
+ Please note that, when using Python 2.7, the HTML form generated by
+ this method may differ from that shown when using the online Signature
+ Verification Form. This is because dictionaries in Python 2.7 are
+ not ordered and the "random" input field will reflect this. The form
+ will still work as expected.
+
+ Keyword arguments:
+
+ random -- The random field from a response returned by
+ RANDOM.ORG through one of the Signed API methods.
+ signature -- The signature field from the same response that
+ the random field originates from.
+ """
+ # if necessary, turn the random object (dict) into a string
+ if isinstance(random, dict):
+ random = json.dumps(random)
+
+ s = '
'
+
+ return s
# Methods used to create a cache for any given randomness request.
@@ -2196,10 +2580,10 @@ def get_requests_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -2244,10 +2628,10 @@ def get_bits_left(self):
server bits allowance has been exceeded.
Raises a ValueError on RANDOM.ORG Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Raises a RuntimeError on JSON-RPC Errors, error descriptions:
- https://api.random.org/json-rpc/3/error-codes
+ https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
@@ -2379,7 +2763,7 @@ def _send_request_core(self, request):
time.sleep(wait)
# Send the request & parse the response.
- response = requests.post('https://api.random.org/json-rpc/3/invoke',
+ response = requests.post('https://api.random.org/json-rpc/4/invoke',
data=json.dumps(request),
headers={'content-type': 'application/json'},
timeout=self._http_timeout)
@@ -2390,21 +2774,21 @@ def _send_request_core(self, request):
message = data['error']['message']
# RuntimeError, error codes listed under JSON-RPC Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
if code in ([-32700] + list(range(-32603,-32600))
+ list(range(-32099,-32000))):
return { 'exception': RuntimeError('Error ' + str(code)
+ ': ' + message) }
# RandomOrgKeyNonExistentError, API key does not exist, from
- # RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/4/error-codes
elif code == 400:
return { 'exception':
RandomOrgKeyNonExistentError('Error ' + str(code)
+ ': ' + message) }
# RandomOrgKeyNotRunningError, API key not running, from
- # RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
+ # RANDOM.ORG Errors: https://api.random.org/json-rpc/4/error-codes
elif code == 401:
return { 'exception':
RandomOrgKeyNotRunningError('Error ' + str(code)
@@ -2412,7 +2796,7 @@ def _send_request_core(self, request):
# RandomOrgInsufficientRequestsError, requests allowance
# exceeded, backoff until midnight UTC, from RANDOM.ORG
- # Errors: https://api.random.org/json-rpc/3/error-codes
+ # Errors: https://api.random.org/json-rpc/4/error-codes
elif code == 402:
self._backoff = datetime.utcnow().replace(day=datetime.utcnow().day+1, hour=0,
minute=0, second=0, microsecond=0)
@@ -2421,7 +2805,7 @@ def _send_request_core(self, request):
RandomOrgInsufficientRequestsError(self._backoff_error) }
# RandomOrgInsufficientBitsError, bits allowance exceeded,
- # from RANDOM.ORG Errors: https://api.random.org/json-rpc/3/error-codes
+ # from RANDOM.ORG Errors: https://api.random.org/json-rpc/4/error-codes
elif code == 403:
return { 'exception':
RandomOrgInsufficientBitsError('Error ' + str(code)
@@ -2429,7 +2813,7 @@ def _send_request_core(self, request):
# RandomOrgKeyInvalidAccessError, key is not valid for method
# requested, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 404:
return { 'exception':
RandomOrgKeyInvalidAccessError('Error ' + str(code)
@@ -2437,7 +2821,7 @@ def _send_request_core(self, request):
# RandomOrgKeyInvalidVersionError, key is not valid for the
# version of the API you are invoking, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 405:
return { 'exception':
RandomOrgKeyInvalidVersionError('Error' + str(code)
@@ -2445,7 +2829,7 @@ def _send_request_core(self, request):
# RandomOrgTicketNonExistentError, the ticket specified does
# not exist, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 420:
return { 'exception':
RandomOrgTicketNonExistentError('Error' + str(code)
@@ -2454,7 +2838,7 @@ def _send_request_core(self, request):
# RandomOrgTicketAPIKeyMismatchError, the ticket specified
# exists but is not for the API key you specified,
# from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 421:
return { 'exception':
RandomOrgTicketAPIKeyMismatchError('Error' + str(code)
@@ -2462,7 +2846,7 @@ def _send_request_core(self, request):
# RandomOrgTicketAlreadyUsedError, the ticket specified has
# already been used, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 422:
return { 'exception':
RandomOrgTicketAlreadyUsedError('Error' + str(code)
@@ -2471,16 +2855,36 @@ def _send_request_core(self, request):
# RandomOrgTooManySingletonTicketsError, the maximum number
# of singleton tickets available for your API key has been
# reached, from RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
elif code == 423:
return { 'exception':
RandomOrgTooManySingletonTicketsError('Error'
+ str(code)
+ ': '
+ message)}
+
+ # RandomOrgLicenseDataRequiredError, your API key requires the
+ # license_data parameter be used, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/4/error-codes
+ elif code == 424:
+ return { 'exception':
+ RandomOrgLicenseDataRequiredError('Error'
+ + str(code)
+ + ': ' + message)}
+ # RandomOrgLicenseDataNotAllowedError, your API key does not
+ # support the use of the license_data parameter,
+ # from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/4/error-codes
+ elif code == 425:
+ return { 'exception':
+ RandomOrgLicenseDataNotAllowedError('Error'
+ + str(code)
+ + ': '
+ + message)}
+
# ValueError, error codes listed under RANDOM.ORG Errors:
- # https://api.random.org/json-rpc/3/error-codes
+ # https://api.random.org/json-rpc/4/error-codes
else:
return { 'exception': ValueError('Error ' + str(code)
+ ': ' + message) }
@@ -2567,7 +2971,7 @@ def _extract_int_sequences(self, response, decimal=True):
else:
return [list(rest) for rest
in self._extract_response(response)]
-
+
def _extract_doubles(self, response):
# json to double list.
return list(map(float, self._extract_response(response)))
@@ -2583,21 +2987,27 @@ def _extract_UUIDs(self, response):
def _extract_blobs(self, response):
# json to blob list (no change).
return self._extract_response(response)
-
- def _order_ticket_data(self, random):
- # orders the information in ticketData to ensure successful signature
- # verification for Python 2.7 (R3)
- if random['ticketData'] is not None:
- random_ordered = OrderedDict()
- for key in random:
- if key == 'ticketData':
- ticket_data = OrderedDict()
- ticket_data['ticketId'] = random['ticketData']['ticketId']
- ticket_data['previousTicketId'] = random['ticketData']['previousTicketId']
- ticket_data['nextTicketId'] = random['ticketData']['nextTicketId']
- random_ordered[key] = ticket_data
- else:
- random_ordered[key] = random[key]
- return random_ordered
- else:
- return random
+
+ def _url_formatting(self, s, encode = False):
+ # adjust the formatting of elements used in url
+ if isinstance(s, dict):
+ s = json.dumps(s, separators=(',', ':'))
+
+ if encode:
+ s = s.encode()
+ s = base64.b64encode(s)
+ s = s.decode()
+
+ # replace certain characters to make them url-safe
+ # (Percent-Encoding as described in RFC 3986 for PHP)
+ s = s.replace('=', '%3D')
+ s = s.replace('+', '%2B')
+ s = s.replace('/', '%2F')
+
+ # return formatted string
+ return s
+
+ def _input_html(self, type, name, value):
+ # helper function to create html code with input tags
+ return ('')
diff --git a/setup.py b/setup.py
index 5c7eba4..0e048ec 100644
--- a/setup.py
+++ b/setup.py
@@ -6,10 +6,10 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.3.1",
+ version = "1.4",
author = "RANDOM.ORG",
author_email = "contact@random.org",
- description = ("RANDOM.ORG JSON-RPC API (Release 3) implementation."),
+ description = ("RANDOM.ORG JSON-RPC API (Release 4) implementation."),
license = "MIT",
keywords = "RANDOM.ORG random client implementation",
url = "https://www.random.org/",
@@ -20,7 +20,7 @@ def read(fname):
'six',
],
project_urls={
- "Documentation": "https://api.random.org/json-rpc/3",
+ "Documentation": "https://api.random.org/json-rpc/4",
"Source Code": "https://github.com/RandomOrg/JSON-RPC-Python",
},
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
diff --git a/test_rdoclient.py b/test_rdoclient.py
index f278442..121e7c9 100644
--- a/test_rdoclient.py
+++ b/test_rdoclient.py
@@ -1,5 +1,5 @@
"""
-RANDOM.ORG JSON-RPC API (Release 3) implementation tests.
+RANDOM.ORG JSON-RPC API (Release 4) implementation tests.
Run with py.test test_rdoclient.py
"""
@@ -161,6 +161,27 @@ def test_ticket_already_used_error(self):
response = self._client.generate_signed_integers(10, 0, 10,
ticket_id=ticket_id)
+ def test_pregenerated_randomization(self):
+ """Check methods return identical values when using same date or id."""
+
+ response_a = self._client.generate_integers(10, 0, 10,
+ pregenerated_randomization
+ ={"date":"2020-01-01"})
+ time.sleep(1)
+ response_b = self._client.generate_integers(10, 0, 10,
+ pregenerated_randomization
+ ={"date":"2020-01-01"})
+ assert response_a == response_b
+
+ response_a = self._client.generate_integers(10, 0, 10,
+ pregenerated_randomization
+ ={"id":"foobar"})
+ time.sleep(1)
+ response_b = self._client.generate_integers(10, 0, 10,
+ pregenerated_randomization
+ ={"id":"foobar"})
+ assert response_a == response_b
+
def test_generate_integers(self):
"""Check generate integers returns a list of integers."""
From 12d25e6187b87431b22a39946109bc58f89f51c3 Mon Sep 17 00:00:00 2001
From: = <=>
Date: Mon, 21 Jun 2021 11:03:29 +0100
Subject: [PATCH 5/8] Update create_url
Minor update to ensure that URLs produced by the create_url method are encoded correctly (safe). If the input parameters are not already base64 encoded, they are encoded in the helper function.
These updates are included in the most recent version of the package on PyPI (1.4.1).
---
rdoclient/__init__.py | 2 +-
rdoclient/rdoclient.py | 17 +++++++++++------
setup.py | 2 +-
3 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index 94eb8f2..3ca8048 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -48,7 +48,7 @@
"""
__title__ = 'rdoclient'
-__version__ = '1.4'
+__version__ = '1.4.1'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index db86ac7..183ca57 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -48,6 +48,7 @@
import base64
import json
import logging
+import re
import threading
import time
import sys
@@ -2172,7 +2173,7 @@ def create_url(self, random, signature):
the random field originates from.
"""
# ensure that input is formatted correctly and is url-safe
- random = self._url_formatting(random, True)
+ random = self._url_formatting(random)
signature = self._url_formatting(signature)
# create full url
@@ -2183,9 +2184,9 @@ def create_url(self, random, signature):
# throw an error is the maximum length allowed (2,046 characters)
# is exceeded
if len(url) > 2046:
- return ValueError('Error: URL exceeds maximum length (2,046 characters).')
+ raise ValueError('Error: URL exceeds maximum length (2,046 characters).')
- return url
+ return url
def create_html(self, random, signature):
"""
@@ -2988,13 +2989,17 @@ def _extract_blobs(self, response):
# json to blob list (no change).
return self._extract_response(response)
- def _url_formatting(self, s, encode = False):
+ def _url_formatting(self, s):
# adjust the formatting of elements used in url
if isinstance(s, dict):
s = json.dumps(s, separators=(',', ':'))
- if encode:
- s = s.encode()
+ # check if the string is base64 encoded
+ b64_pattern = '^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$'
+ is_b64 = re.search(b64_pattern, s)
+
+ if not is_b64:
+ s = s.encode()
s = base64.b64encode(s)
s = s.decode()
diff --git a/setup.py b/setup.py
index 0e048ec..c30f7b7 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.4",
+ version = "1.4.1",
author = "RANDOM.ORG",
author_email = "contact@random.org",
description = ("RANDOM.ORG JSON-RPC API (Release 4) implementation."),
From 40eae6a0f4cfbb51fa74fe51a7aef4c0db935e05 Mon Sep 17 00:00:00 2001
From: = <=>
Date: Fri, 17 Dec 2021 11:56:39 +0000
Subject: [PATCH 6/8] Update documentation
Updating the README to include a description of URL and HTML methods.
The description of the package was also updated to reflect it being the official package.
A new version of this library was published to PyPI - 1.4.2
---
README.rst | 6 +++++-
rdoclient/__init__.py | 2 +-
setup.py | 4 ++--
3 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/README.rst b/README.rst
index 81a3655..4ee8c47 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
JSON-RPC-Python
===============
-RANDOM.ORG JSON-RPC API (Release 4) implementation.
+The official RANDOM.ORG JSON-RPC API (Release 4) implementation for Python 2 and 3.
This is a Python implementation of the RANDOM.ORG JSON-RPC API (R4). It provides either serialized or unserialized access to both the signed and unsigned methods of the API through the RandomOrgClient class. It also provides a convenience class through the RandomOrgClient class, the RandomOrgCache, for precaching requests. In the context of this module, a serialized client is one for which the sequence of requests matches the sequence of responses.
@@ -73,6 +73,10 @@ Finally, it is possible to request live results as-soon-as-possible and without
>>> r.generate_integers(5, 0, 10)
[3, 5, 2, 4, 8]
+Signature Verification
+----------------------
+There are two additional methods to generate signature verification URLs and HTML forms (*create_url* and *create_html*) using the random object and signature returned from any of the signed (value generating) methods. The generated URLs and HTML forms link to the same web page that is also shown when a result is verified using the online `Signature Verification Form `_.
+
Documentation
-------------
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index 3ca8048..0401033 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -48,7 +48,7 @@
"""
__title__ = 'rdoclient'
-__version__ = '1.4.1'
+__version__ = '1.4.2'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
diff --git a/setup.py b/setup.py
index c30f7b7..85f1d2e 100644
--- a/setup.py
+++ b/setup.py
@@ -6,10 +6,10 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.4.1",
+ version = "1.4.2",
author = "RANDOM.ORG",
author_email = "contact@random.org",
- description = ("RANDOM.ORG JSON-RPC API (Release 4) implementation."),
+ description = ("The official RANDOM.ORG JSON-RPC API (Release 4) implementation for Python 2 and 3."),
license = "MIT",
keywords = "RANDOM.ORG random client implementation",
url = "https://www.random.org/",
From a16276028e868fc2969502f4537980ab0341898a Mon Sep 17 00:00:00 2001
From: RandomOrg
Date: Mon, 18 Jul 2022 11:28:35 +0100
Subject: [PATCH 7/8] Documentation updates - v1.4.3
Minor update to replace unsafe links in the documentation (specifically links to potential exceptions raised by the requests library).
---
rdoclient/__init__.py | 2 +-
rdoclient/rdoclient.py | 42 +++++++++++++++++++++---------------------
setup.py | 2 +-
3 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index 0401033..d4d7fc0 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -48,7 +48,7 @@
"""
__title__ = 'rdoclient'
-__version__ = '1.4.2'
+__version__ = '1.4.3'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index 183ca57..265a2f7 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -633,7 +633,7 @@ def generate_integers(self, n, min, max, replacement=True, base=10,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -710,7 +710,7 @@ def generate_integer_sequences(self, n, length, min, max, replacement=True,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -796,7 +796,7 @@ def generate_decimal_fractions(self, n, decimal_places, replacement=True,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -870,7 +870,7 @@ def generate_gaussians(self, n, mean, standard_deviation, significant_digits,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -943,7 +943,7 @@ def generate_strings(self, n, length, characters, replacement=True,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1018,7 +1018,7 @@ def generate_UUIDs(self, n, pregenerated_randomization=None):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1084,7 +1084,7 @@ def generate_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1175,7 +1175,7 @@ def generate_signed_integers(self, n, min, max, replacement=True,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1285,7 +1285,7 @@ def generate_signed_integer_sequences(self, n, length, min, max,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1406,7 +1406,7 @@ def generate_signed_decimal_fractions(self, n, decimal_places,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1514,7 +1514,7 @@ def generate_signed_gaussians(self, n, mean, standard_deviation,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1620,7 +1620,7 @@ def generate_signed_strings(self, n, length, characters,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1728,7 +1728,7 @@ def generate_signed_UUIDs(self, n, pregenerated_randomization=None,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1826,7 +1826,7 @@ def generate_signed_blobs(self, n, size, format=_BLOB_FORMAT_BASE64,
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -1912,7 +1912,7 @@ def get_result(self, serial_number):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
serial_number -- An integer containing the serial number
@@ -1960,7 +1960,7 @@ def create_tickets(self, n, show_result):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -2014,7 +2014,7 @@ def list_tickets(self, ticket_type):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -2071,7 +2071,7 @@ def get_ticket(self, ticket_id):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -2126,7 +2126,7 @@ def verify_signature(self, random, signature):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
Keyword arguments:
@@ -2587,7 +2587,7 @@ def get_requests_left(self):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
"""
try:
# Python 2.7
@@ -2635,7 +2635,7 @@ def get_bits_left(self):
https://api.random.org/json-rpc/4/error-codes
Can also raise connection errors as described here:
- http://docs.python-requests.org/en/v2.0-0/user/quickstart/#errors-and-exceptions
+ https://requests.readthedocs.io/en/latest/api/#exceptions
"""
try:
# Python 2.7
diff --git a/setup.py b/setup.py
index 85f1d2e..39eccc6 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.4.2",
+ version = "1.4.3",
author = "RANDOM.ORG",
author_email = "contact@random.org",
description = ("The official RANDOM.ORG JSON-RPC API (Release 4) implementation for Python 2 and 3."),
From fec2fbabfb08a79a33ebc718c4d6272a17181269 Mon Sep 17 00:00:00 2001
From: RandomOrg
Date: Tue, 11 Apr 2023 17:45:48 +0100
Subject: [PATCH 8/8] Added reveal_tickets method
Added the reveal_tickets method, as well as a new error (RandomOrgTicketNotYetUsedError, code 426) and appropriate tests.
pypi version update 1.4.3 -> 1.5.0
---
README.rst | 2 +-
rdoclient/__init__.py | 10 ++--
rdoclient/rdoclient.py | 111 ++++++++++++++++++++++++++++++++++++-----
setup.py | 4 +-
test_rdoclient.py | 60 ++++++++++++++++++++++
5 files changed, 170 insertions(+), 17 deletions(-)
diff --git a/README.rst b/README.rst
index 4ee8c47..784aab7 100644
--- a/README.rst
+++ b/README.rst
@@ -27,7 +27,7 @@ Requires the `six `_ lib:
$ pip install six
-Note that the required dependencies 'requests' and 'six' are installed automatically, when using pip install for 'rdoclient' version >= 1.2 (Update: November 2020).
+Note that the required dependencies 'requests' and 'six' are installed automatically, when using pip install for 'rdoclient' version >= 1.2.
Usage
-----
diff --git a/rdoclient/__init__.py b/rdoclient/__init__.py
index d4d7fc0..3e26fef 100644
--- a/rdoclient/__init__.py
+++ b/rdoclient/__init__.py
@@ -48,7 +48,7 @@
"""
__title__ = 'rdoclient'
-__version__ = '1.4.3'
+__version__ = '1.5.0'
__build__ = 0x010000
__author__ = 'RANDOM.ORG'
__license__ = 'MIT'
@@ -59,14 +59,18 @@
RandomOrgInsufficientRequestsError, RandomOrgInsufficientBitsError,
RandomOrgKeyInvalidAccessError, RandomOrgKeyInvalidVersionError,
RandomOrgTicketNonExistentError, RandomOrgTicketAPIKeyMismatchError,
- RandomOrgTicketAlreadyUsedError, RandomOrgTooManySingletonTicketsError)
+ RandomOrgTicketAlreadyUsedError, RandomOrgTooManySingletonTicketsError,
+ RandomOrgTicketNotYetUsedError, RandomOrgLicenseDataRequiredError,
+ RandomOrgLicenseDataNotAllowedError)
__all__ = [ 'RandomOrgClient', 'RandomOrgCache', 'RandomOrgSendTimeoutError',
'RandomOrgKeyNonExistentError', 'RandomOrgKeyNotRunningError',
'RandomOrgInsufficientRequestsError', 'RandomOrgInsufficientBitsError',
'RandomOrgKeyInvalidAccessError', 'RandomOrgKeyInvalidVersionError',
'RandomOrgTicketNonExistentError', 'RandomOrgTicketAPIKeyMismatchError',
- 'RandomOrgTicketAlreadyUsedError', 'RandomOrgTooManySingletonTicketsError' ]
+ 'RandomOrgTicketAlreadyUsedError', 'RandomOrgTooManySingletonTicketsError',
+ 'RandomOrgTicketNotYetUsedError', 'RandomOrgLicenseDataRequiredError',
+ 'RandomOrgLicenseDataNotAllowedError' ]
import logging
diff --git a/rdoclient/rdoclient.py b/rdoclient/rdoclient.py
index 265a2f7..dce6b15 100644
--- a/rdoclient/rdoclient.py
+++ b/rdoclient/rdoclient.py
@@ -84,6 +84,7 @@
_SIGNED_BLOB_METHOD = 'generateSignedBlobs'
_GET_RESULT_METHOD = 'getResult'
_CREATE_TICKETS_METHOD = 'createTickets'
+_REVEAL_TICKETS_METHOD = 'revealTickets'
_LIST_TICKETS_METHOD = 'listTickets'
_GET_TICKET_METHOD = 'getTicket'
_VERIFY_SIGNATURE_METHOD = 'verifySignature'
@@ -198,6 +199,15 @@ class RandomOrgTooManySingletonTicketsError(Exception):
number of singleton tickets associated with the API key specified
has been reached.
"""
+
+class RandomOrgTicketNotYetUsedError(Exception):
+ """
+ RandomOrgClient ticket has not yet been used.
+
+ Exception raised by the RandomOrgClient class when the ticket
+ specified in the reveal_tickets method has not yet been used
+ to generate values.
+ """
class RandomOrgLicenseDataRequiredError(Exception):
"""
@@ -1976,7 +1986,73 @@ def create_tickets(self, n, show_result):
params = { 'apiKey':self._api_key, 'n':n, 'showResult':show_result }
request = self._generate_request(_CREATE_TICKETS_METHOD, params)
response = self._send_request(request)
- return self._extract_tickets(response)
+ return self._extract_result(response)
+
+ def reveal_tickets(self, ticket_id):
+ """
+ Change the show_result value of a ticket and its predecessors to
+ "True".
+
+ This method marks a specific ticket and all its predecessors in its
+ chain as being revealed, meaning that subsequent calls to get_ticket
+ will return the full details of the tickets, including the random
+ values produced when the tickets were used. Using reveal_tickets
+ effectively changes the value of show_result (which was specified
+ when the first ticket in the chain was created using create_tickets)
+ from "False" to "True". The reason that not only the ticket specified
+ (but also its predecessors in its chain) are revealed is to ensure
+ maximum transparency. The method does not affect any successors to
+ the ticket in the chain. Returns a number value that specifies how
+ many tickets were revealed. This will include the ticket specified
+ as well as all its predecessors. If this method is invoked on a ticket
+ that is already revealed (or which was created with show_result set
+ to True), then the value returned will be zero. See:
+ https://api.random.org/json-rpc/4/signed#revealTickets
+
+ Raises a RandomOrgSendTimeoutError if time spent waiting before
+ request is sent exceeds this instance's blocking_timeout.
+
+ Raises a RandomOrgKeyNonExistentError if this API key does not
+ exist.
+
+ Raises a RandomOrgKeyNotRunningError if this API key is stopped.
+
+ Raises a RandomOrgInsufficientRequestsError if this API key's
+ server requests allowance has been exceeded and the instance is
+ backing off until midnight UTC.
+
+ Raises a RandomOrgInsufficientBitsError if this API key's
+ server bits allowance has been exceeded.
+
+ Raises a RandomOrgKeyInvalidAccessError if this API key is not
+ valid for this method.
+
+ Raises a RandomOrgKeyInvalidVersionError if this API key is not
+ valid for the version of the API invoked.
+
+ Raises a RandomOrgTicketNonExistentError if the ticket used
+ does not exist.
+
+ Raises a RandomOrgTicketNotYetUsedError if the ticket specified
+ has not yet been used.
+
+ Raises a ValueError on RANDOM.ORG Errors, error descriptions:
+ https://api.random.org/json-rpc/4/error-codes
+
+ Raises a RuntimeError on JSON-RPC Errors, error descriptions:
+ https://api.random.org/json-rpc/4/error-codes
+
+ Can also raise connection errors as described here:
+ https://requests.readthedocs.io/en/latest/api/#exceptions
+
+ Keyword arguments:
+
+ ticket_id -- A string value that uniquely identifies the ticket.
+ """
+ params = { 'apiKey':self._api_key, 'ticketId':ticket_id }
+ request = self._generate_request(_REVEAL_TICKETS_METHOD, params)
+ response = self._send_request(request)
+ return self._extract_result(response)
def list_tickets(self, ticket_type):
"""
@@ -2029,7 +2105,7 @@ def list_tickets(self, ticket_type):
params = { 'apiKey':self._api_key, 'ticketType':ticket_type }
request = self._generate_request(_LIST_TICKETS_METHOD, params)
response = self._send_request(request)
- return self._extract_tickets(response)
+ return self._extract_result(response)
def get_ticket(self, ticket_id):
"""
@@ -2082,7 +2158,7 @@ def get_ticket(self, ticket_id):
params = { 'ticketId':ticket_id }
request = self._generate_request(_GET_TICKET_METHOD, params)
response = self._send_request(request)
- return self._extract_tickets(response)
+ return self._extract_result(response)
# Signature verification for signed methods, see:
@@ -2825,7 +2901,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 405:
return { 'exception':
- RandomOrgKeyInvalidVersionError('Error' + str(code)
+ RandomOrgKeyInvalidVersionError('Error ' + str(code)
+ ': ' + message)}
# RandomOrgTicketNonExistentError, the ticket specified does
@@ -2833,7 +2909,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 420:
return { 'exception':
- RandomOrgTicketNonExistentError('Error' + str(code)
+ RandomOrgTicketNonExistentError('Error ' + str(code)
+ ': ' + message)}
# RandomOrgTicketAPIKeyMismatchError, the ticket specified
@@ -2842,7 +2918,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 421:
return { 'exception':
- RandomOrgTicketAPIKeyMismatchError('Error' + str(code)
+ RandomOrgTicketAPIKeyMismatchError('Error ' + str(code)
+ ': ' + message)}
# RandomOrgTicketAlreadyUsedError, the ticket specified has
@@ -2850,7 +2926,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 422:
return { 'exception':
- RandomOrgTicketAlreadyUsedError('Error' + str(code)
+ RandomOrgTicketAlreadyUsedError('Error ' + str(code)
+ ': ' + message)}
# RandomOrgTooManySingletonTicketsError, the maximum number
@@ -2859,7 +2935,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 423:
return { 'exception':
- RandomOrgTooManySingletonTicketsError('Error'
+ RandomOrgTooManySingletonTicketsError('Error '
+ str(code)
+ ': '
+ message)}
@@ -2869,7 +2945,7 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 424:
return { 'exception':
- RandomOrgLicenseDataRequiredError('Error'
+ RandomOrgLicenseDataRequiredError('Error '
+ str(code)
+ ': ' + message)}
@@ -2879,7 +2955,17 @@ def _send_request_core(self, request):
# https://api.random.org/json-rpc/4/error-codes
elif code == 425:
return { 'exception':
- RandomOrgLicenseDataNotAllowedError('Error'
+ RandomOrgLicenseDataNotAllowedError('Error '
+ + str(code)
+ + ': '
+ + message)}
+
+ # RandomOrgTicketNotYetUsedError, the ticket you specified has
+ # not yet been used, from RANDOM.ORG Errors:
+ # https://api.random.org/json-rpc/4/error-codes
+ elif code == 426:
+ return { 'exception':
+ RandomOrgTicketNotYetUsedError('Error '
+ str(code)
+ ': '
+ message)}
@@ -2953,8 +3039,9 @@ def _extract_verification_response(self, response):
# Gets verification boolean.
return bool(response['result']['authenticity'])
- def _extract_tickets(self, response):
- # Gets list of tickets for create_tickets method
+ def _extract_result(self, response):
+ # Gets 'result' property of a return object. Primarily used
+ # for ticket-related methods.
return response['result']
def _extract_ints(self, response, decimal=True):
diff --git a/setup.py b/setup.py
index 39eccc6..7e5f392 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ def read(fname):
setup(
name = "rdoclient",
- version = "1.4.3",
+ version = "1.5.0",
author = "RANDOM.ORG",
author_email = "contact@random.org",
description = ("The official RANDOM.ORG JSON-RPC API (Release 4) implementation for Python 2 and 3."),
@@ -34,6 +34,8 @@ def read(fname):
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries :: Python Modules",
],
)
diff --git a/test_rdoclient.py b/test_rdoclient.py
index 121e7c9..d46408b 100644
--- a/test_rdoclient.py
+++ b/test_rdoclient.py
@@ -161,6 +161,16 @@ def test_ticket_already_used_error(self):
response = self._client.generate_signed_integers(10, 0, 10,
ticket_id=ticket_id)
+ def test_ticket_not_yet_used_error(self):
+ """Check RandomOrgTicketNotYetUsedError raised when ticket hasn't been
+ used yet (when revealing)."""
+
+ ticket = self._client.create_tickets(1, False)
+ ticket_id = ticket[0]['ticketId']
+
+ with pytest.raises(RandomOrgTicketNotYetUsedError):
+ response = self._client.reveal_tickets(ticket_id)
+
def test_pregenerated_randomization(self):
"""Check methods return identical values when using same date or id."""
@@ -422,6 +432,56 @@ def test_create_tickets(self):
for i in response:
assert i['ticketId'] is not None
+
+ def test_reveal_tickets_single(self):
+ """Check if a single ticket is revealed correctly."""
+ ticket = self._client.create_tickets(1, False)
+ ticket_id = ticket[0]['ticketId']
+
+ response = self._client.generate_signed_integers(5, 0, 10,
+ ticket_id=ticket_id)
+
+ # Before revealing.
+ get_ticket = self._client.get_ticket(ticket_id)
+ assert 'result' not in get_ticket
+
+ reveal = self._client.reveal_tickets(ticket_id)
+ assert reveal['ticketCount'] == 1
+
+ # After revealing.
+ get_ticket = self._client.get_ticket(ticket_id)
+ assert get_ticket['result'] is not None
+ assert get_ticket['result']['random']['data'] == response['data']
+
+ def test_reveal_tickets_multiple(self):
+ n = 3
+
+ ticket = self._client.create_tickets(1, False)
+ ticket_id = ticket[0]['ticketId']
+ results = {}
+ get_ticket = None
+
+ for i in range(n):
+ if i > 0 and get_ticket != None:
+ ticket_id = get_ticket['nextTicketId']
+
+ response = self._client.generate_signed_integers(5, 0, 10,
+ ticket_id=ticket_id)
+
+ results[ticket_id] = response;
+
+ # Before revealing.
+ get_ticket = self._client.get_ticket(ticket_id)
+ assert 'result' not in get_ticket
+
+ reveal = self._client.reveal_tickets(ticket_id)
+ assert reveal['ticketCount'] == n
+
+ # After revealing.
+ for id, value in results.items():
+ get_ticket = self._client.get_ticket(id)
+ assert get_ticket['result'] is not None
+ assert get_ticket['result']['random']['data'] == value['data']
def test_list_tickets(self):
"""Check list tickets returns a list of tickets of the correct type."""