Commit f763a728 authored by Raymond Hettinger's avatar Raymond Hettinger

Document which part of the random module module are guaranteed.

parent 435cb0f2
...@@ -51,18 +51,23 @@ from sources provided by the operating system. ...@@ -51,18 +51,23 @@ from sources provided by the operating system.
Bookkeeping functions: Bookkeeping functions:
.. function:: seed([x]) .. function:: seed([x], version=2)
Initialize the basic random number generator. Optional argument *x* can be any Initialize the random number generator.
:term:`hashable` object. If *x* is omitted or ``None``, current system time is used;
current system time is also used to initialize the generator when the module is
first imported. If randomness sources are provided by the operating system,
they are used instead of the system time (see the :func:`os.urandom` function
for details on availability).
If *x* is not ``None`` or an int, ``hash(x)`` is used instead. If *x* is an If *x* is omitted or ``None``, the current system time is used. If
int, *x* is used directly. randomness sources are provided by the operating system, they are used
instead of the system time (see the :func:`os.urandom` function for details
on availability).
If *x* is an int, it is used directly.
With version 2 (the default), a :class:`str`, :class:`bytes`, or :class:`bytearray`
object gets converted to a :class:`int` and all of its bits are used. With version 1,
the :func:`hash` of *x* is used instead.
.. versionchanged:: 3.2
Moved to the version 2 scheme which uses all of the bits in a string seed.
.. function:: getstate() .. function:: getstate()
......
...@@ -91,13 +91,17 @@ class Random(_random.Random): ...@@ -91,13 +91,17 @@ class Random(_random.Random):
self.seed(x) self.seed(x)
self.gauss_next = None self.gauss_next = None
def seed(self, a=None): def seed(self, a=None, version=2):
"""Initialize internal state from hashable object. """Initialize internal state from hashable object.
None or no argument seeds from current time or from an operating None or no argument seeds from current time or from an operating
system specific randomness source if available. system specific randomness source if available.
If a is not None or an int, hash(a) is used instead. For version 2 (the default), all of the bits are used if a is a str,
bytes, or bytearray. For version 1, the hash() of a is used instead.
If a is an int, all bits are used.
""" """
if a is None: if a is None:
...@@ -107,6 +111,11 @@ class Random(_random.Random): ...@@ -107,6 +111,11 @@ class Random(_random.Random):
import time import time
a = int(time.time() * 256) # use fractional seconds a = int(time.time() * 256) # use fractional seconds
if version == 2 and isinstance(a, (str, bytes, bytearray)):
if isinstance(a, str):
a = a.encode("utf8")
a = int(_hexlify(a), 16)
super().seed(a) super().seed(a)
self.gauss_next = None self.gauss_next = None
......
...@@ -39,7 +39,7 @@ class TestBasicOps(unittest.TestCase): ...@@ -39,7 +39,7 @@ class TestBasicOps(unittest.TestCase):
self.gen.seed(arg) self.gen.seed(arg)
for arg in [list(range(3)), dict(one=1)]: for arg in [list(range(3)), dict(one=1)]:
self.assertRaises(TypeError, self.gen.seed, arg) self.assertRaises(TypeError, self.gen.seed, arg)
self.assertRaises(TypeError, self.gen.seed, 1, 2) self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4)
self.assertRaises(TypeError, type(self.gen), []) self.assertRaises(TypeError, type(self.gen), [])
def test_sample(self): def test_sample(self):
...@@ -223,6 +223,21 @@ class SystemRandom_TestBasicOps(TestBasicOps): ...@@ -223,6 +223,21 @@ class SystemRandom_TestBasicOps(TestBasicOps):
class MersenneTwister_TestBasicOps(TestBasicOps): class MersenneTwister_TestBasicOps(TestBasicOps):
gen = random.Random() gen = random.Random()
def test_guaranteed_stable(self):
# These sequences are guaranteed to stay the same across versions of python
self.gen.seed(3456147, version=1)
self.assertEqual([self.gen.random().hex() for i in range(4)],
['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1',
'0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1'])
self.gen.seed("the quick brown fox", version=1)
self.assertEqual([self.gen.random().hex() for i in range(4)],
['0x1.9ee265c177cdep-2', '0x1.bad51092e3c25p-1',
'0x1.85ff833f71576p-1', '0x1.87efb37462927p-1'])
self.gen.seed("the quick brown fox", version=2)
self.assertEqual([self.gen.random().hex() for i in range(4)],
['0x1.1294009b9eda4p-2', '0x1.2ff96171b0010p-1',
'0x1.459e0989bd8e0p-5', '0x1.8b5f55892ddcbp-1'])
def test_setstate_first_arg(self): def test_setstate_first_arg(self):
self.assertRaises(ValueError, self.gen.setstate, (1, None, None)) self.assertRaises(ValueError, self.gen.setstate, (1, None, None))
......
...@@ -18,6 +18,9 @@ Library ...@@ -18,6 +18,9 @@ Library
* Document which parts of the module are guaranteed to stay the same * Document which parts of the module are guaranteed to stay the same
across versions and which parts are subject to change. across versions and which parts are subject to change.
* Update the seed() method to use all of the bits in a string
instead of just the hash value.
- collections.OrderedDict now supports a new method for repositioning - collections.OrderedDict now supports a new method for repositioning
keys to either end. keys to either end.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment