2 If you execute force_repeatability() then the following things are changed in the runtime:
4 1. random.random() and its sibling functions, and random.Random.seed() in the random module are seeded with a known seed so that they will return the same sequence on each run.
5 2. os.urandom() is replaced by a fake urandom that returns a pseudorandom sequence.
6 3. time.time() is replaced by a fake time that returns an incrementing number. (Original time.time is available as time.realtime.)
8 Which seed will be used?
10 If the environment variable REPEATABLE_RANDOMNESS_SEED is set, then it will use that. Else, it will use the current real time. In either case it logs the seed that it used.
14 1. If some code has acquired a random.Random object before force_repeatability() is executed, then that Random object will produce non-reproducible results. For example, the tempfile module in the Python Standard Library does this.
15 2. Likewise if some code called time.time() before force_repeatability() was called, then it will have gotten a real time stamp. For example, trial does this. (Then it later subtracts that real timestamp from a faketime timestamp to calculate elapsed time, resulting in a large negative elapsed time.)
16 3. The output from the fake urandom has weird distribution for performance reasons-- every byte after the first 20 bytes resulting from a single call to os.urandom() is zero. In practice this hasn't caused any problems.
19 import os, random, time
20 if not hasattr(time, "realtime"):
21 time.realtime = time.time
22 if not hasattr(os, "realurandom"):
23 os.realurandom = os.urandom
24 if not hasattr(random, "realseed"):
25 random.realseed = random.seed
29 def force_repeatability():
35 time.faketime = faketime
38 from allmydata.util.idlib import i2b
41 z = i2b(random.getrandbits(20*8))
45 z = i2b(random.getrandbits(n*8))
46 x = z + "0" * (n-len(z))
49 os.fakeurandom = fakeurandom
50 os.urandom = fakeurandom
54 SEED = os.environ.get('REPEATABLE_RANDOMNESS_SEED', None)
57 # Generate a seed which is integral and fairly short (to ease cut-and-paste, writing it down, etc.).
60 t += (subsec * 1000000)
64 sys.stdout.write("REPEATABLE_RANDOMNESS_SEED: %s\n" % SEED) ; sys.stdout.flush()
65 sys.stdout.write("In order to reproduce this run of the code, set the environment variable \"REPEATABLE_RANDOMNESS_SEED\" to %s before executing.\n" % SEED) ; sys.stdout.flush()
68 def seed_which_refuses(a):
69 sys.stdout.write("I refuse to reseed to %s. Go away!\n" % (a,)) ; sys.stdout.flush()
71 random.realseed = random.seed
72 random.seed = seed_which_refuses
76 setutil.RandomSet.DETERMINISTIC = True
78 def restore_real_clock():
79 time.time = time.realtime
81 def restore_real_urandom():
82 os.urandom = os.realurandom
84 def restore_real_seed():
85 random.seed = random.realseed
87 def restore_non_repeatability():
89 restore_real_urandom()