From c70811426918a29a5cb30247e07cf6301bd822da Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 10:41:10 -0500 Subject: [PATCH 01/12] Add strategy for drawing numpy random number generators --- hypothesis-python/setup.py | 2 +- .../src/hypothesis/extra/numpy.py | 72 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/hypothesis-python/setup.py b/hypothesis-python/setup.py index 62b5180018..987db2ab69 100644 --- a/hypothesis-python/setup.py +++ b/hypothesis-python/setup.py @@ -58,7 +58,7 @@ def local_file(name): "pytz": ["pytz>=2014.1"], "dateutil": ["python-dateutil>=1.4"], "lark": ["lark-parser>=0.6.5"], - "numpy": ["numpy>=1.9.0"], + "numpy": ["numpy>=1.19.0"], "pandas": ["pandas>=1.0"], "pytest": ["pytest>=4.6"], "dpcontracts": ["dpcontracts>=0.4"], diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index 06376ae42a..d4376ae686 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -9,9 +9,10 @@ # obtain one at https://mozilla.org/MPL/2.0/. import math -from typing import Any, Mapping, Optional, Sequence, Tuple, Union +from typing import Any, Mapping, Optional, Sequence, Tuple, Type, Union import numpy as np +from numpy.random import PCG64 from hypothesis import strategies as st from hypothesis._settings import note_deprecation @@ -35,7 +36,7 @@ from hypothesis.internal.reflection import proxies from hypothesis.internal.validation import check_type from hypothesis.strategies._internal.strategies import T, check_strategy -from hypothesis.strategies._internal.utils import defines_strategy +from hypothesis.strategies._internal.utils import cacheable, defines_strategy __all__ = [ "BroadcastableShapes", @@ -59,6 +60,7 @@ "mutually_broadcastable_shapes", "basic_indices", "integer_array_indices", + "rand_generators", ] TIME_RESOLUTIONS = tuple("Y M D h m s ms us ns ps fs as".split()) @@ -955,3 +957,69 @@ def array_for(index_shape, size): return result_shape.flatmap( lambda index_shape: st.tuples(*(array_for(index_shape, size) for size in shape)) ) + + +class NumpyGeneratorStrategy(st.SearchStrategy): + class _GeneratorWithSeedRepr(np.random.Generator): + # must be attached to the instance externally + _hypothesis_initial_seed: int + + def __repr__(self) -> str: + return f"Generator({type(self.bit_generator).__name__}({self._hypothesis_initial_seed!r}))" + + def __init__(self, bit_gen_types: Tuple[Type[np.random.BitGenerator], ...]): + assert bit_gen_types + self._bit_gen_types = ( + st.just(bit_gen_types[0]) + if len(bit_gen_types) == 1 + else st.sampled_from(bit_gen_types) + ) + + def do_draw(self, data): + CurrentBitGenerator = data.draw(self._bit_gen_types) + seed = data.draw_bits(64) + gen = self._GeneratorWithSeedRepr(CurrentBitGenerator(seed)) + gen._hypothesis_initial_seed = seed + return gen + + +@cacheable +@defines_strategy() +def rand_generators( + __g: Type[np.random.BitGenerator] = PCG64, + *bit_generator_types: Type[np.random.BitGenerator], +) -> st.SearchStrategy[np.random.Generator]: + """Generates instances of + :obj:`numpy.random.Generator ` backed by a + bit-generator initialized with a Hypothesis-drawn 64 bit seed. + + Accepts one or more + :obj:`numpy.random.BitGenerator ` types, + defaulting to PCG64, that will be sampled from during example generation. + Examples from this strategy shrink towards a generator backed by the + first-specified bit-generator type, seeded with 0. + + This is the recommended way for utilizing diverse, reproducible sources of random number generation in Hypothesis tests. The resulting generators are of a special Hypothesis subclass whose repr displays the initial seed for the bit generator. + + .. code-block:: pycon + + >>> [randoms().example() for _ in range(3)] + [Generator(PCG64(17731618377865219412)), + Generator(PCG64(16938332804403789103)), + Generator(PCG64(9641801721570554589))] + + >>> from numpy.random import MT19937, PCG64 + >>> # specifying multiple bit-generator types + >>> [randoms(MT19937, PCG64).example() for _ in range(3)] + [Generator(PCG64(1138900339423482065)), + Generator(MT19937(13796052070681794055)), + Generator(MT19937(16637614687104877655))] + + """ + bit_generator_types = (__g,) + bit_generator_types + for g in bit_generator_types: + if not issubclass(g, np.random.BitGenerator): + raise InvalidArgument( + f"`randoms` must be passed BitGenerator subclasses. Got " "{g}" + ) + return NumpyGeneratorStrategy(bit_generator_types) From 77b5d68263380ab73c4656d598676907146b3e9f Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 10:42:16 -0500 Subject: [PATCH 02/12] wrap lines --- hypothesis-python/src/hypothesis/extra/numpy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index d4376ae686..b175b2ce5d 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -999,7 +999,9 @@ def rand_generators( Examples from this strategy shrink towards a generator backed by the first-specified bit-generator type, seeded with 0. - This is the recommended way for utilizing diverse, reproducible sources of random number generation in Hypothesis tests. The resulting generators are of a special Hypothesis subclass whose repr displays the initial seed for the bit generator. + This is the recommended way for utilizing diverse, reproducible sources of random + number generation in Hypothesis tests. The resulting generators are of a special + Hypothesis subclass whose repr displays the initial seed for the bit generator. .. code-block:: pycon From ac4c9adc6278bc739f030d79021dc857ffae7acb Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 10:57:26 -0500 Subject: [PATCH 03/12] fix test that can never fail --- hypothesis-python/tests/numpy/test_randomness.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hypothesis-python/tests/numpy/test_randomness.py b/hypothesis-python/tests/numpy/test_randomness.py index 6a9d724a83..7fb3bb842b 100644 --- a/hypothesis-python/tests/numpy/test_randomness.py +++ b/hypothesis-python/tests/numpy/test_randomness.py @@ -15,15 +15,19 @@ def test_numpy_prng_is_seeded(): - first = [] prng_state = np.random.get_state() @given(none()) def inner(_): - val = np.random.bytes(10) - if not first: - first.append(val) - assert val == first[0], "Numpy random module should be reproducible" + # Hypothesis sets seed to 0 by default + implicitly_seeded_val = np.random.bytes(10) + + np.random.seed(0) + explicitly_seeded_val = np.random.bytes(10) + + assert ( + implicitly_seeded_val == explicitly_seeded_val + ), "Numpy random module should be reproducible" inner() From d69961ca79994fa2c7b3c2bf4e732353888548d6 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 11:43:41 -0500 Subject: [PATCH 04/12] Add tests for rand_generators --- .../src/hypothesis/extra/numpy.py | 9 +++- .../tests/numpy/test_argument_validation.py | 18 ++++++++ .../tests/numpy/test_gen_data.py | 46 +++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index b175b2ce5d..45b36785e4 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -1020,8 +1020,13 @@ def rand_generators( """ bit_generator_types = (__g,) + bit_generator_types for g in bit_generator_types: - if not issubclass(g, np.random.BitGenerator): + if ( + not isinstance(g, type) + or not issubclass(g, np.random.BitGenerator) + or g is np.random.BitGenerator + ): raise InvalidArgument( - f"`randoms` must be passed BitGenerator subclasses. Got " "{g}" + f"`randoms` must be passed BitGenerator subclasses (BitGenerator " + "itself is not a valid implementation). Got {g}" ) return NumpyGeneratorStrategy(bit_generator_types) diff --git a/hypothesis-python/tests/numpy/test_argument_validation.py b/hypothesis-python/tests/numpy/test_argument_validation.py index 3215945152..5f0fe0e316 100644 --- a/hypothesis-python/tests/numpy/test_argument_validation.py +++ b/hypothesis-python/tests/numpy/test_argument_validation.py @@ -23,6 +23,10 @@ def e(a, **kwargs): return pytest.param(a, kwargs, id=f"{a.__name__}({kw})") +def p(a, *args): + return pytest.param(a, args, id=f"{a.__name__}({', '.join(str(x) for x in args)})") + + @pytest.mark.parametrize( ("function", "kwargs"), [ @@ -288,3 +292,17 @@ def test_raise_invalid_argument(function, kwargs): def test_raise_invalid_argument_deprecated(function, kwargs): with pytest.raises(InvalidArgument): function(**kwargs).example() + + +@pytest.mark.parametrize( + ("function", "args"), + [ + p(nps.rand_generators, True), + p(nps.rand_generators, numpy.random.Generator), + p(nps.rand_generators, numpy.random.BitGenerator), + p(nps.rand_generators, numpy.random.PCG64, numpy.random.BitGenerator), + ], +) +def test_raise_invalid_pos_args(function, args): + with pytest.raises(InvalidArgument): + function(*args).example() diff --git a/hypothesis-python/tests/numpy/test_gen_data.py b/hypothesis-python/tests/numpy/test_gen_data.py index 554efc1df8..e52f7d4f0a 100644 --- a/hypothesis-python/tests/numpy/test_gen_data.py +++ b/hypothesis-python/tests/numpy/test_gen_data.py @@ -11,9 +11,11 @@ import sys from functools import reduce from itertools import zip_longest +from typing import List, Type import numpy as np import pytest +from numpy.random import BitGenerator, Generator from hypothesis import ( HealthCheck, @@ -1203,6 +1205,13 @@ def test_basic_indices_generate_valid_indexers( assert np.shares_memory(view, array) +ALL_BIT_GENERATORS: List[Type[BitGenerator]] = [ + x + for x in (getattr(np.random, name) for name in np.random.__all__) + if isinstance(x, type) and issubclass(x, BitGenerator) and x is not BitGenerator +] + + # addresses https://github.com/HypothesisWorks/hypothesis/issues/2582 @given( nps.arrays( @@ -1212,3 +1221,40 @@ def test_basic_indices_generate_valid_indexers( def test_array_owns_memory(x: np.ndarray): assert x.base is None assert x[...].base is x + + +@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +@given(data=st.data()) +def test_fuzz_all_bit_generators(BitGen: Type[BitGenerator], data: st.DrawFn): + # ensure that our seed mechanism works for all bit generators + gen = data.draw(nps.rand_generators(BitGen)) + assert isinstance(gen.bit_generator, BitGen) + gen.normal() + + +@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +def test_random_generators_have_accurate_reprs(BitGen: Type[BitGenerator]): + cache = [] + name_to_bit_gen = {x.__name__: x for x in ALL_BIT_GENERATORS} + + @settings(max_examples=10) + @given(nps.rand_generators(BitGen)) + def runner(generator): + cache.append(generator) + + runner() + + for gen in cache: + # reconstruct bit-generator from repr and ensure they generate same numbers + _, bit_gen_name, seed_tail = repr(gen).split("(") + bit_gen_type = name_to_bit_gen[bit_gen_name] + seed = int(seed_tail[:-2]) + reconstructed_gen = Generator(bit_gen_type(seed)) + # It would be preferable to compare bit-generator states, but + # different bit generators implement state differently, and some + # require traversing pytrees with array leaves -- not easy to compare + # + # So instead we compare that the generators match 10 consecutive + # draws. + for _ in range(10): + assert gen.uniform() == reconstructed_gen.uniform() From 20931bf7f583547ce46e2877fca5d1e57c4ef01d Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 11:51:39 -0500 Subject: [PATCH 05/12] add more tests --- hypothesis-python/tests/common/debug.py | 6 +++++- hypothesis-python/tests/numpy/test_gen_data.py | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/hypothesis-python/tests/common/debug.py b/hypothesis-python/tests/common/debug.py index 8c75d2974d..3da031ac0e 100644 --- a/hypothesis-python/tests/common/debug.py +++ b/hypothesis-python/tests/common/debug.py @@ -8,6 +8,8 @@ # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. +from typing import Any, Callable + from hypothesis import ( HealthCheck, Verbosity, @@ -74,7 +76,9 @@ def inner(x): ) -def find_any(definition, condition=lambda _: True, settings=None): +def find_any( + definition, condition: Callable[[Any], bool] = lambda _: True, settings=None +): settings = settings or Settings.default return minimal( definition, diff --git a/hypothesis-python/tests/numpy/test_gen_data.py b/hypothesis-python/tests/numpy/test_gen_data.py index e52f7d4f0a..d3a9cbc58e 100644 --- a/hypothesis-python/tests/numpy/test_gen_data.py +++ b/hypothesis-python/tests/numpy/test_gen_data.py @@ -1237,7 +1237,7 @@ def test_random_generators_have_accurate_reprs(BitGen: Type[BitGenerator]): cache = [] name_to_bit_gen = {x.__name__: x for x in ALL_BIT_GENERATORS} - @settings(max_examples=10) + @settings(max_examples=10, database=None) @given(nps.rand_generators(BitGen)) def runner(generator): cache.append(generator) @@ -1258,3 +1258,17 @@ def runner(generator): # draws. for _ in range(10): assert gen.uniform() == reconstructed_gen.uniform() + + +@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +def test_samples_from_bit_generators(BitGen: Type[BitGenerator]): + find_any( + nps.rand_generators(*ALL_BIT_GENERATORS), + lambda gen: type(gen.bit_generator) is BitGen, + ) + + +@settings(max_examples=10) +@given(nps.rand_generators()) +def test_default_bit_generator_matches_default_rng_backing(gen: Generator): + assert type(gen.bit_generator) is type(np.random.default_rng().bit_generator) From a0fdffd7b31d5a81e10df2b33116094d4811af88 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 11:58:04 -0500 Subject: [PATCH 06/12] Register strategy for numpy.random.Generator --- .../src/hypothesis/extra/numpy.py | 11 ++++++++++- .../hypothesis/strategies/_internal/types.py | 11 ++++++++++- .../tests/numpy/test_gen_data.py | 19 ++++++------------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index 45b36785e4..fde711d883 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -9,7 +9,7 @@ # obtain one at https://mozilla.org/MPL/2.0/. import math -from typing import Any, Mapping, Optional, Sequence, Tuple, Type, Union +from typing import Any, List, Mapping, Optional, Sequence, Tuple, Type, Union import numpy as np from numpy.random import PCG64 @@ -959,6 +959,15 @@ def array_for(index_shape, size): ) +_ALL_BIT_GENERATORS: List[Type[np.random.BitGenerator]] = [ + x + for x in (getattr(np.random, name) for name in np.random.__all__) + if isinstance(x, type) + and issubclass(x, np.random.BitGenerator) + and x is not np.random.BitGenerator +] + + class NumpyGeneratorStrategy(st.SearchStrategy): class _GeneratorWithSeedRepr(np.random.Generator): # must be attached to the instance externally diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py index 4e410bcf58..d8260e2a2d 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py @@ -590,10 +590,19 @@ def _networks(bits): try: # pragma: no cover import numpy as np - from hypothesis.extra.numpy import array_dtypes, array_shapes, arrays, scalar_dtypes + from hypothesis.extra.numpy import ( + _ALL_BIT_GENERATORS, + array_dtypes, + array_shapes, + arrays, + rand_generators, + scalar_dtypes, + ) _global_type_lookup[np.dtype] = array_dtypes() _global_type_lookup[np.ndarray] = arrays(scalar_dtypes(), array_shapes(max_dims=2)) + # is it too intense to sample from all available bit-generators + _global_type_lookup[np.random.Generator] = rand_generators(*_ALL_BIT_GENERATORS) except ImportError: pass diff --git a/hypothesis-python/tests/numpy/test_gen_data.py b/hypothesis-python/tests/numpy/test_gen_data.py index d3a9cbc58e..88ae01cbb2 100644 --- a/hypothesis-python/tests/numpy/test_gen_data.py +++ b/hypothesis-python/tests/numpy/test_gen_data.py @@ -11,7 +11,7 @@ import sys from functools import reduce from itertools import zip_longest -from typing import List, Type +from typing import Type import numpy as np import pytest @@ -1205,13 +1205,6 @@ def test_basic_indices_generate_valid_indexers( assert np.shares_memory(view, array) -ALL_BIT_GENERATORS: List[Type[BitGenerator]] = [ - x - for x in (getattr(np.random, name) for name in np.random.__all__) - if isinstance(x, type) and issubclass(x, BitGenerator) and x is not BitGenerator -] - - # addresses https://github.com/HypothesisWorks/hypothesis/issues/2582 @given( nps.arrays( @@ -1223,7 +1216,7 @@ def test_array_owns_memory(x: np.ndarray): assert x[...].base is x -@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +@pytest.mark.parametrize("BitGen", nps._ALL_BIT_GENERATORS) @given(data=st.data()) def test_fuzz_all_bit_generators(BitGen: Type[BitGenerator], data: st.DrawFn): # ensure that our seed mechanism works for all bit generators @@ -1232,10 +1225,10 @@ def test_fuzz_all_bit_generators(BitGen: Type[BitGenerator], data: st.DrawFn): gen.normal() -@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +@pytest.mark.parametrize("BitGen", nps._ALL_BIT_GENERATORS) def test_random_generators_have_accurate_reprs(BitGen: Type[BitGenerator]): cache = [] - name_to_bit_gen = {x.__name__: x for x in ALL_BIT_GENERATORS} + name_to_bit_gen = {x.__name__: x for x in nps._ALL_BIT_GENERATORS} @settings(max_examples=10, database=None) @given(nps.rand_generators(BitGen)) @@ -1260,10 +1253,10 @@ def runner(generator): assert gen.uniform() == reconstructed_gen.uniform() -@pytest.mark.parametrize("BitGen", ALL_BIT_GENERATORS) +@pytest.mark.parametrize("BitGen", nps._ALL_BIT_GENERATORS) def test_samples_from_bit_generators(BitGen: Type[BitGenerator]): find_any( - nps.rand_generators(*ALL_BIT_GENERATORS), + nps.rand_generators(*nps._ALL_BIT_GENERATORS), lambda gen: type(gen.bit_generator) is BitGen, ) From de63ac588b79695a63a761ab57a17233670a7526 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:04:43 -0500 Subject: [PATCH 07/12] add release notes --- hypothesis-python/RELEASE.rst | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..85bf56a133 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,9 @@ +RELEASE_TYPE: minor + +This release adds the strategy :func:`~hypothesis.extra.numpy.rand_generators`, which +draws instances of :obj:`numpy.random.Generator ` backed +by a bit-generator initialized with a Hypothesis-controlled seed. Fail cases display +the initial seed that was used to create the generator, enabling reproducibility. + +Accordingly, Hypothesis can now infer a strategy for the ``numpy.random.Generator`` +type. From 5b680cc081a2fa525dc61fffd7ab1ccbee0b3d52 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:06:47 -0500 Subject: [PATCH 08/12] fix example --- hypothesis-python/src/hypothesis/extra/numpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index fde711d883..68be43630e 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -1014,14 +1014,14 @@ def rand_generators( .. code-block:: pycon - >>> [randoms().example() for _ in range(3)] + >>> [rand_generators().example() for _ in range(3)] [Generator(PCG64(17731618377865219412)), Generator(PCG64(16938332804403789103)), Generator(PCG64(9641801721570554589))] >>> from numpy.random import MT19937, PCG64 >>> # specifying multiple bit-generator types - >>> [randoms(MT19937, PCG64).example() for _ in range(3)] + >>> [rand_generators(MT19937, PCG64).example() for _ in range(3)] [Generator(PCG64(1138900339423482065)), Generator(MT19937(13796052070681794055)), Generator(MT19937(16637614687104877655))] From a93e563366ede2ee52bdce28aa0ad6321e338cc8 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:14:16 -0500 Subject: [PATCH 09/12] note numpy version bump --- hypothesis-python/RELEASE.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst index 85bf56a133..1334bdb491 100644 --- a/hypothesis-python/RELEASE.rst +++ b/hypothesis-python/RELEASE.rst @@ -1,9 +1,10 @@ RELEASE_TYPE: minor +This release increases Hypothesis' minimum supported version of NumPy to 1.19.0. + This release adds the strategy :func:`~hypothesis.extra.numpy.rand_generators`, which draws instances of :obj:`numpy.random.Generator ` backed by a bit-generator initialized with a Hypothesis-controlled seed. Fail cases display the initial seed that was used to create the generator, enabling reproducibility. - Accordingly, Hypothesis can now infer a strategy for the ``numpy.random.Generator`` type. From 0dee6274cd76165b1fc9908948a0f4cf931242b9 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:27:51 -0500 Subject: [PATCH 10/12] fix error message, remove cacheable decorator --- hypothesis-python/src/hypothesis/extra/numpy.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index 68be43630e..549563dd75 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -36,7 +36,7 @@ from hypothesis.internal.reflection import proxies from hypothesis.internal.validation import check_type from hypothesis.strategies._internal.strategies import T, check_strategy -from hypothesis.strategies._internal.utils import cacheable, defines_strategy +from hypothesis.strategies._internal.utils import defines_strategy __all__ = [ "BroadcastableShapes", @@ -992,7 +992,6 @@ def do_draw(self, data): return gen -@cacheable @defines_strategy() def rand_generators( __g: Type[np.random.BitGenerator] = PCG64, @@ -1035,7 +1034,7 @@ def rand_generators( or g is np.random.BitGenerator ): raise InvalidArgument( - f"`randoms` must be passed BitGenerator subclasses (BitGenerator " - "itself is not a valid implementation). Got {g}" + f"`rand_generators` must be passed BitGenerator subclasses " + "(BitGenerator itself is not a valid implementation). Got {g}" ) return NumpyGeneratorStrategy(bit_generator_types) From 9b1f80e99a742c138e382c466ad7ecb96a3f7f91 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:35:55 -0500 Subject: [PATCH 11/12] improve docs --- hypothesis-python/src/hypothesis/extra/numpy.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index 549563dd75..4d74e4be3b 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -1007,9 +1007,10 @@ def rand_generators( Examples from this strategy shrink towards a generator backed by the first-specified bit-generator type, seeded with 0. - This is the recommended way for utilizing diverse, reproducible sources of random - number generation in Hypothesis tests. The resulting generators are of a special - Hypothesis subclass whose repr displays the initial seed for the bit generator. + This is the recommended way for utilizing diverse, reproducible sources of + NumPy-based random number generation in Hypothesis tests. The resulting generators + are of a special Hypothesis subclass whose repr displays the initial seed for the + bit generator. .. code-block:: pycon From 0285de1e62dc1a36bbfbe56e0dcade2b6a14c1c0 Mon Sep 17 00:00:00 2001 From: Ryan Soklaski Date: Sat, 19 Nov 2022 12:52:09 -0500 Subject: [PATCH 12/12] fix strategy signature --- hypothesis-python/src/hypothesis/extra/numpy.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py index 4d74e4be3b..407fee9772 100644 --- a/hypothesis-python/src/hypothesis/extra/numpy.py +++ b/hypothesis-python/src/hypothesis/extra/numpy.py @@ -994,7 +994,6 @@ def do_draw(self, data): @defines_strategy() def rand_generators( - __g: Type[np.random.BitGenerator] = PCG64, *bit_generator_types: Type[np.random.BitGenerator], ) -> st.SearchStrategy[np.random.Generator]: """Generates instances of @@ -1003,7 +1002,7 @@ def rand_generators( Accepts one or more :obj:`numpy.random.BitGenerator ` types, - defaulting to PCG64, that will be sampled from during example generation. + defaulting to ``PCG64``, that will be sampled from during example generation. Examples from this strategy shrink towards a generator backed by the first-specified bit-generator type, seeded with 0. @@ -1024,10 +1023,11 @@ def rand_generators( >>> [rand_generators(MT19937, PCG64).example() for _ in range(3)] [Generator(PCG64(1138900339423482065)), Generator(MT19937(13796052070681794055)), - Generator(MT19937(16637614687104877655))] - + Generator(MT19937(16637614687104877655)) """ - bit_generator_types = (__g,) + bit_generator_types + if not bit_generator_types: + bit_generator_types = (PCG64,) + for g in bit_generator_types: if ( not isinstance(g, type)