From 48a623569b352dfc15956e98f59ceae704f16c4c Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 17:40:20 +0100 Subject: [PATCH 1/5] Noted zero sides can be generated by `xps.indices()` --- hypothesis-python/src/hypothesis/extra/array_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hypothesis-python/src/hypothesis/extra/array_api.py b/hypothesis-python/src/hypothesis/extra/array_api.py index c94e306efb..1d281dccc1 100644 --- a/hypothesis-python/src/hypothesis/extra/array_api.py +++ b/hypothesis-python/src/hypothesis/extra/array_api.py @@ -584,7 +584,6 @@ def _unsigned_integer_dtypes( ``sizes`` contains the unsigned integer sizes in bits, defaulting to ``(8, 16, 32, 64)`` which covers all valid sizes. """ - if isinstance(sizes, int): sizes = (sizes,) check_valid_sizes("int", sizes, (8, 16, 32, 64)) @@ -605,7 +604,6 @@ def _floating_dtypes( ``sizes`` contains the floating-point sizes in bits, defaulting to ``(32, 64)`` which covers all valid sizes. """ - if isinstance(sizes, int): sizes = (sizes,) check_valid_sizes("int", sizes, (32, 64)) @@ -662,7 +660,7 @@ def indices( allow_ellipsis: bool = True, ) -> st.SearchStrategy[BasicIndex]: """Return a strategy for :xp-ref:`valid indices ` of - arrays with the specified shape. + arrays with the specified shape, which may include dimensions of size zero. It generates tuples containing some mix of integers, :obj:`python:slice` objects, and ``...`` (an ``Ellipsis``). When a length-one tuple would be From d544b8eb3fb08ea675c2ba3de6425491f88c535d Mon Sep 17 00:00:00 2001 From: Matthew Barber Date: Wed, 6 Oct 2021 17:40:43 +0100 Subject: [PATCH 2/5] Clarified logic for `xps.from_dtype()` minimisation test --- hypothesis-python/tests/array_api/test_from_dtype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypothesis-python/tests/array_api/test_from_dtype.py b/hypothesis-python/tests/array_api/test_from_dtype.py index 998e3e5885..709a954033 100644 --- a/hypothesis-python/tests/array_api/test_from_dtype.py +++ b/hypothesis-python/tests/array_api/test_from_dtype.py @@ -101,4 +101,4 @@ def test_from_dtype_with_kwargs(data, dtype, kwargs, predicate): def test_can_minimize_floats(): """Inferred float strategy minimizes to a good example.""" smallest = minimal(xps.from_dtype(xp.float32), lambda n: n >= 1.0) - assert smallest in (1, 50) + assert smallest == 1 From 29c31789962dd1dd49dc7694e27506438b297403 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 20 Oct 2021 11:23:59 +0100 Subject: [PATCH 3/5] Docstrings for `array_api/test_indices.py` --- .../tests/array_api/test_indices.py | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/hypothesis-python/tests/array_api/test_indices.py b/hypothesis-python/tests/array_api/test_indices.py index 0ca1269d51..87760d8158 100644 --- a/hypothesis-python/tests/array_api/test_indices.py +++ b/hypothesis-python/tests/array_api/test_indices.py @@ -21,32 +21,41 @@ from tests.common.debug import find_any -def test_indices_options(): - indexers = ( +def test_generate_indices_with_and_without_ellipsis(): + """Strategy can generate indices with and without Ellipsis.""" + strat = ( xps.array_shapes(min_dims=1, max_dims=32) .flatmap(xps.indices) .map(lambda idx: idx if isinstance(idx, tuple) else (idx,)) ) - find_any(indexers, lambda ix: Ellipsis in ix) - find_any(indexers, lambda ix: Ellipsis not in ix) + find_any(strat, lambda ix: Ellipsis in ix) + find_any(strat, lambda ix: Ellipsis not in ix) -def test_indices_can_generate_empty_tuple(): +def test_generate_empty_tuple(): + """Strategy generates empty tuples as indices.""" find_any(xps.indices(shape=(0, 0), allow_ellipsis=True), lambda ix: ix == ()) -def test_indices_can_generate_non_tuples(): +def test_generate_non_tuples(): + """Strategy generates non-tuples as indices.""" find_any( xps.indices(shape=(0, 0), allow_ellipsis=True), lambda ix: not isinstance(ix, tuple), ) -def test_indices_can_generate_long_ellipsis(): - # Runs of slice(None) - such as [0,:,:,:,0] - can be replaced by e.g. [0,...,0] +def test_generate_long_ellipsis(): + """Strategy can replace runs of slice(None) with Ellipsis. + + We specifically test if [0,...,0] is generated alongside [0,:,:,:,0] + """ + strat = xps.indices(shape=(1, 0, 0, 0, 1), max_dims=3, allow_ellipsis=True) + find_any(strat, lambda ix: len(ix) == 3 and ix[1] == Ellipsis) find_any( - xps.indices(shape=(1, 0, 0, 0, 1), max_dims=3, allow_ellipsis=True), - lambda ix: len(ix) == 3 and ix[1] == Ellipsis, + strat, + lambda ix: len(ix) == 5 + and all(isinstance(key, slice) and key == slice(None) for key in ix[1:3]), ) @@ -74,6 +83,7 @@ def test_indices_effeciently_generate_indexers(_): data=st.data(), ) def test_indices_generate_valid_indexers(shape, allow_ellipsis, data): + """Strategy generates valid indices.""" min_dims = data.draw(st.integers(0, len(shape)), label="min_dims") max_dims = data.draw( st.none() | st.integers(min_dims, len(shape)), label="max_dims" From 9c0a54abe9e45dc1c6fdf01be55713a8efc3903b Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 20 Oct 2021 11:34:06 +0100 Subject: [PATCH 4/5] Raise `InvalidArgument` with bad shape in `xps.arrays()` --- hypothesis-python/RELEASE.rst | 4 ++++ hypothesis-python/src/hypothesis/extra/array_api.py | 11 ++++++----- .../tests/array_api/test_argument_validation.py | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) 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..567d891614 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,4 @@ +RELEASE_TYPE: patch + +This patch adds an error for when ``shapes`` in :func:`xps.arrays()` is not +passed as either a valid shape or strategy. diff --git a/hypothesis-python/src/hypothesis/extra/array_api.py b/hypothesis-python/src/hypothesis/extra/array_api.py index 1d281dccc1..b29703d239 100644 --- a/hypothesis-python/src/hypothesis/extra/array_api.py +++ b/hypothesis-python/src/hypothesis/extra/array_api.py @@ -468,16 +468,17 @@ def _arrays( return dtype.flatmap( lambda d: _arrays(xp, d, shape, elements=elements, fill=fill, unique=unique) ) + elif isinstance(dtype, str): + dtype = dtype_from_name(xp, dtype) + if isinstance(shape, st.SearchStrategy): return shape.flatmap( lambda s: _arrays(xp, dtype, s, elements=elements, fill=fill, unique=unique) ) - - if isinstance(dtype, str): - dtype = dtype_from_name(xp, dtype) - - if isinstance(shape, int): + elif isinstance(shape, int): shape = (shape,) + elif not isinstance(shape, tuple): + raise InvalidArgument(f"shape={shape} is not a valid shape or strategy") check_argument( all(isinstance(x, int) and x >= 0 for x in shape), f"shape={shape!r}, but all dimensions must be non-negative integers.", diff --git a/hypothesis-python/tests/array_api/test_argument_validation.py b/hypothesis-python/tests/array_api/test_argument_validation.py index 7afbd961ec..7717abdd1c 100644 --- a/hypothesis-python/tests/array_api/test_argument_validation.py +++ b/hypothesis-python/tests/array_api/test_argument_validation.py @@ -33,6 +33,7 @@ def e(a, **kwargs): e(xps.arrays, dtype=xp.int8, shape=(0.5,)), e(xps.arrays, dtype=xp.int8, shape=1, fill=3), e(xps.arrays, dtype=xp.int8, shape=1, elements="not a strategy"), + e(xps.arrays, dtype=xp.int8, shape=lambda: "not a strategy"), e(xps.array_shapes, min_side=2, max_side=1), e(xps.array_shapes, min_dims=3, max_dims=2), e(xps.array_shapes, min_dims=-1), From d997b9621a8fe107cb07da6426831f85fba86843 Mon Sep 17 00:00:00 2001 From: Matthew Date: Wed, 20 Oct 2021 12:09:25 +0100 Subject: [PATCH 5/5] Replace lambda with a string expression in validation test case Avoids differing memory reference(?) issues --- hypothesis-python/tests/array_api/test_argument_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypothesis-python/tests/array_api/test_argument_validation.py b/hypothesis-python/tests/array_api/test_argument_validation.py index 7717abdd1c..9ffbde890c 100644 --- a/hypothesis-python/tests/array_api/test_argument_validation.py +++ b/hypothesis-python/tests/array_api/test_argument_validation.py @@ -33,7 +33,7 @@ def e(a, **kwargs): e(xps.arrays, dtype=xp.int8, shape=(0.5,)), e(xps.arrays, dtype=xp.int8, shape=1, fill=3), e(xps.arrays, dtype=xp.int8, shape=1, elements="not a strategy"), - e(xps.arrays, dtype=xp.int8, shape=lambda: "not a strategy"), + e(xps.arrays, dtype=xp.int8, shape="not a shape or strategy"), e(xps.array_shapes, min_side=2, max_side=1), e(xps.array_shapes, min_dims=3, max_dims=2), e(xps.array_shapes, min_dims=-1),