Skip to content

Commit 5fdf452

Browse files
authored
Merge pull request #3139 from Zac-HD/use-global-random
2 parents e72ce42 + b3a7496 commit 5fdf452

File tree

8 files changed

+36
-22
lines changed

8 files changed

+36
-22
lines changed

hypothesis-python/RELEASE.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
RELEASE_TYPE: patch
2+
3+
This patch gives Hypothesis it's own internal :class:`~random.Random` instance,
4+
ensuring that test suites which reset the global random state don't induce
5+
weird correlations between property-based tests (:issue:`2135`).

hypothesis-python/src/hypothesis/core.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import datetime
2121
import inspect
2222
import io
23-
import random as rnd_module
2423
import sys
2524
import time
2625
import traceback
@@ -120,6 +119,7 @@
120119

121120
running_under_pytest = False
122121
global_force_seed = None
122+
_hypothesis_global_random = None
123123

124124

125125
@attr.s()
@@ -419,7 +419,10 @@ def get_random_for_wrapped_test(test, wrapped_test):
419419
elif global_force_seed is not None:
420420
return Random(global_force_seed)
421421
else:
422-
seed = rnd_module.getrandbits(128)
422+
global _hypothesis_global_random
423+
if _hypothesis_global_random is None:
424+
_hypothesis_global_random = Random()
425+
seed = _hypothesis_global_random.getrandbits(128)
423426
wrapped_test._hypothesis_internal_use_generated_seed = seed
424427
return Random(seed)
425428

hypothesis-python/src/hypothesis/internal/conjecture/shrinking/dfas.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def normalize(
238238
required_successes=100,
239239
allowed_to_update=False,
240240
max_dfas=10,
241+
random=None,
241242
):
242243
"""Attempt to ensure that this test function successfully normalizes - i.e.
243244
whenever it declares a test case to be interesting, we are able
@@ -267,6 +268,7 @@ def normalize(
267268
test_function,
268269
settings=settings(database=None, suppress_health_check=HealthCheck.all()),
269270
ignore_limits=True,
271+
random=random,
270272
)
271273

272274
seen = set()

hypothesis-python/tests/cover/test_fuzz_one_input.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
# END HEADER
1515

1616
import io
17-
import random
1817
from operator import attrgetter
1918

2019
import pytest
@@ -24,6 +23,11 @@
2423
from hypothesis.errors import InvalidArgument
2524
from hypothesis.internal.conjecture.shrinker import sort_key
2625

26+
try:
27+
from random import randbytes
28+
except ImportError: # New in Python 3.9
29+
from secrets import token_bytes as randbytes
30+
2731

2832
@pytest.mark.parametrize(
2933
"buffer_type",
@@ -53,7 +57,7 @@ def test(s):
5357
# find a failing example.
5458
with pytest.raises(AssertionError):
5559
for _ in range(1000):
56-
buf = bytes(random.getrandbits(8) for _ in range(1000))
60+
buf = randbytes(1000)
5761
seeds.append(buf)
5862
test.hypothesis.fuzz_one_input(buffer_type(buf))
5963

@@ -95,7 +99,7 @@ def test(s):
9599
raise AssertionError("Unreachable because there are no valid examples")
96100

97101
for _ in range(100):
98-
buf = bytes(random.getrandbits(8) for _ in range(3))
102+
buf = randbytes(3)
99103
ret = test.hypothesis.fuzz_one_input(buf)
100104
assert ret is None
101105

hypothesis-python/tests/cover/test_random_module.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import pytest
1919

20-
from hypothesis import find, given, register_random, reporting, strategies as st
20+
from hypothesis import core, find, given, register_random, reporting, strategies as st
2121
from hypothesis.errors import InvalidArgument
2222
from hypothesis.internal import entropy
2323
from hypothesis.internal.entropy import deterministic_PRNG
@@ -120,20 +120,26 @@ def test(r):
120120

121121
test()
122122
state_a = random.getstate()
123+
state_a2 = core._hypothesis_global_random.getstate()
123124

124125
test()
125126
state_b = random.getstate()
127+
state_b2 = core._hypothesis_global_random.getstate()
126128

127-
assert state_a != state_b
129+
assert state_a == state_b
130+
assert state_a2 != state_b2
128131

129132

130133
def test_find_does_not_pollute_state():
131134
with deterministic_PRNG():
132135

133136
find(st.random_module(), lambda r: True)
134137
state_a = random.getstate()
138+
state_a2 = core._hypothesis_global_random.getstate()
135139

136140
find(st.random_module(), lambda r: True)
137141
state_b = random.getstate()
142+
state_b2 = core._hypothesis_global_random.getstate()
138143

139-
assert state_a != state_b
144+
assert state_a == state_b
145+
assert state_a2 != state_b2

hypothesis-python/tests/nocover/test_randomization.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,18 @@
1313
#
1414
# END HEADER
1515

16-
import random
17-
1816
from pytest import raises
1917

20-
from hypothesis import Verbosity, find, given, settings, strategies as st
18+
from hypothesis import Verbosity, core, find, given, settings, strategies as st
2119

2220
from tests.common.utils import no_shrink
2321

2422

25-
def test_seeds_off_random():
23+
def test_seeds_off_internal_random():
2624
s = settings(phases=no_shrink, database=None)
27-
r = random.getstate()
25+
r = core._hypothesis_global_random.getstate()
2826
x = find(st.integers(), lambda x: True, settings=s)
29-
random.setstate(r)
27+
core._hypothesis_global_random.setstate(r)
3028
y = find(st.integers(), lambda x: True, settings=s)
3129
assert x == y
3230

hypothesis-python/tests/quality/test_normalization.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# END HEADER
1515

1616
from itertools import islice
17+
from random import Random
1718

1819
import pytest
1920

@@ -56,10 +57,6 @@ def test_function(data):
5657

5758
@pytest.mark.parametrize("strategy", [st.emails(), st.complex_numbers()], ids=repr)
5859
def test_harder_strategies_normalize_to_minimal(strategy, normalize_kwargs):
59-
import random
60-
61-
random.seed(0)
62-
6360
def test_function(data):
6461
try:
6562
v = data.draw(strategy)
@@ -68,4 +65,4 @@ def test_function(data):
6865
data.output = repr(v)
6966
data.mark_interesting()
7067

71-
dfas.normalize(repr(strategy), test_function, **normalize_kwargs)
68+
dfas.normalize(repr(strategy), test_function, random=Random(0), **normalize_kwargs)

hypothesis-python/tests/quality/test_zig_zagging.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
#
1414
# END HEADER
1515

16-
import random
1716
from math import log
17+
from random import Random
1818

1919
from hypothesis import (
2020
HealthCheck,
@@ -77,8 +77,6 @@ def problem(draw):
7777
def test_avoids_zig_zag_trap(p):
7878
b, marker, lower_bound = p
7979

80-
random.seed(0)
81-
8280
n_bits = 8 * (len(b) + 1)
8381

8482
def test_function(data):
@@ -95,6 +93,7 @@ def test_function(data):
9593
test_function,
9694
database_key=None,
9795
settings=settings(base_settings, phases=(Phase.generate, Phase.shrink)),
96+
random=Random(0),
9897
)
9998

10099
runner.cached_test_function(b + bytes([0]) + b + bytes([1]) + marker)

0 commit comments

Comments
 (0)