From 8497cb94d796b5f2e321ee0d9eae44fbd8bc0eb6 Mon Sep 17 00:00:00 2001 From: sangarshanan Date: Sat, 25 Jul 2020 19:38:55 +0530 Subject: [PATCH 1/4] Generate negative indices from the slices() strategy --- CONTRIBUTING.rst | 2 ++ RELEASE.rst | 7 +++++++ .../src/hypothesis/internal/validation.py | 2 +- .../src/hypothesis/strategies/_internal/core.py | 13 +++++++++---- hypothesis-python/tests/cover/test_slices.py | 17 +++++++++++++---- 5 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 RELEASE.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 3c012cb250..99fe792398 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -298,3 +298,5 @@ their individual contributions. * `Wilfred Hughes `_ * `Zac Hatfield-Dodds `_ (zac.hatfield.dodds@gmail.com) * `Zebulun Arendsee `_ (zbwrnz@gmail.com) +* `Sangarshanan `_ (sangarshanan1998@gmail.com) + diff --git a/RELEASE.rst b/RELEASE.rst new file mode 100644 index 0000000000..4ba9fec1e4 --- /dev/null +++ b/RELEASE.rst @@ -0,0 +1,7 @@ +RELEASE_TYPE: minor + +The :func:`~hypothesis.strategies.slices` strategy can now generate slices for empty sequences, +slices with negative start and stop indices (from the end of the sequence), +and ``step=None`` in place of ``step=1``. + +Thanks to Sangarshanan for implementing this feature at the EuroPython sprints! diff --git a/hypothesis-python/src/hypothesis/internal/validation.py b/hypothesis-python/src/hypothesis/internal/validation.py index 0140aa131c..b92607b43f 100644 --- a/hypothesis-python/src/hypothesis/internal/validation.py +++ b/hypothesis-python/src/hypothesis/internal/validation.py @@ -110,7 +110,7 @@ def check_valid_size(value, name): Otherwise raises InvalidArgument. """ - if value is None and name != "min_size": + if value is None and name not in ("min_size", "size"): return check_type(int, value, name) if value < 0: diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py index 4952ed09fe..9e54040adb 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py @@ -2173,15 +2173,16 @@ def functions( def slices(draw: Any, size: int) -> slice: """Generates slices that will select indices up to the supplied size - Generated slices will have start and stop indices that range from 0 to size - 1 + Generated slices will have start and stop indices that range from -size to size - 1 and will step in the appropriate direction. Slices should only produce an empty selection if the start and end are the same. Examples from this strategy shrink toward 0 and smaller values """ - check_valid_integer(size, "size") - if size is None or size < 1: - raise InvalidArgument("size=%r must be at least one" % size) + check_valid_size(size, "size") + if size == 0: + step = draw(none() | integers().filter(bool)) + return slice(None, None, step) min_start = min_stop = 0 max_start = max_stop = size @@ -2205,4 +2206,8 @@ def slices(draw: Any, size: int) -> slice: if (stop or 0) < (start or 0): step *= -1 + if draw(booleans()) and (start is not None and stop is not None): + start -= size + stop -= size + return slice(start, stop, step) diff --git a/hypothesis-python/tests/cover/test_slices.py b/hypothesis-python/tests/cover/test_slices.py index 7125bcdb9c..14274f58fe 100644 --- a/hypothesis-python/tests/cover/test_slices.py +++ b/hypothesis-python/tests/cover/test_slices.py @@ -24,28 +24,30 @@ @use_several_sizes def test_stop_stays_within_bounds(size): assert_all_examples( - st.slices(size), lambda x: x.stop is None or (x.stop >= 0 and x.stop <= size) + st.slices(size), + lambda x: x.stop is None or (x.stop >= -size and x.stop <= size), ) @use_several_sizes def test_start_stay_within_bounds(size): assert_all_examples( - st.slices(size), lambda x: x.start is None or (x.start >= 0 and x.start <= size) + st.slices(size), + lambda x: x.start is None or (x.start >= -size and x.start <= size), ) @use_several_sizes def test_step_stays_within_bounds(size): # indices -> (start, stop, step) - # Stop is exclusive so we use -1 as the floor. + # Stop is exclusive so we use -size as the floor. # This uses the indices that slice produces to make this test more readable # due to how splice processes None being a little complex assert_all_examples( st.slices(size), lambda x: ( x.indices(size)[0] + x.indices(size)[2] <= size - and x.indices(size)[0] + x.indices(size)[2] >= -1 + and x.indices(size)[0] + x.indices(size)[2] >= -size ) or x.start == x.stop, ) @@ -98,3 +100,10 @@ def test_start_will_equal_0(size): @settings(deadline=None) def test_start_will_equal_stop(size): find_any(st.slices(size), lambda x: x.start == x.stop) + + +@settings(deadline=None) +def test_size_is_equal_0(): + assert_all_examples( + st.slices(0), lambda x: x.step != 0 and x.start is None and x.stop is None + ) From ba753b66a4401b661d11dd6aab26ae1fde4b6458 Mon Sep 17 00:00:00 2001 From: sangarshanan Date: Sat, 25 Jul 2020 19:53:48 +0530 Subject: [PATCH 2/4] Move release to -pythonand seperate out start and stop --- CONTRIBUTING.rst | 1 - RELEASE.rst => hypothesis-python/RELEASE.rst | 0 .../src/hypothesis/strategies/_internal/core.py | 8 +++++--- hypothesis-python/tests/cover/test_slices.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) rename RELEASE.rst => hypothesis-python/RELEASE.rst (100%) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 99fe792398..5378e33a03 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -299,4 +299,3 @@ their individual contributions. * `Zac Hatfield-Dodds `_ (zac.hatfield.dodds@gmail.com) * `Zebulun Arendsee `_ (zbwrnz@gmail.com) * `Sangarshanan `_ (sangarshanan1998@gmail.com) - diff --git a/RELEASE.rst b/hypothesis-python/RELEASE.rst similarity index 100% rename from RELEASE.rst rename to hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py index 9e54040adb..b98d4d1e04 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py @@ -2206,8 +2206,10 @@ def slices(draw: Any, size: int) -> slice: if (stop or 0) < (start or 0): step *= -1 - if draw(booleans()) and (start is not None and stop is not None): - start -= size - stop -= size + if draw(booleans()): + if start is not None: + start -= size + if stop is not None: + stop -= size return slice(start, stop, step) diff --git a/hypothesis-python/tests/cover/test_slices.py b/hypothesis-python/tests/cover/test_slices.py index 14274f58fe..5945fd903b 100644 --- a/hypothesis-python/tests/cover/test_slices.py +++ b/hypothesis-python/tests/cover/test_slices.py @@ -40,7 +40,7 @@ def test_start_stay_within_bounds(size): @use_several_sizes def test_step_stays_within_bounds(size): # indices -> (start, stop, step) - # Stop is exclusive so we use -size as the floor. + # Stop is exclusive so we use -1 as the floor. # This uses the indices that slice produces to make this test more readable # due to how splice processes None being a little complex assert_all_examples( From bd91b85b3d96bf70456f678546036650a131ef2d Mon Sep 17 00:00:00 2001 From: sangarshanan Date: Sat, 25 Jul 2020 20:09:32 +0530 Subject: [PATCH 3/4] Remove settings decorator Changes to contributors Change the nested if --- CONTRIBUTING.rst | 2 +- .../src/hypothesis/strategies/_internal/core.py | 9 ++++----- hypothesis-python/tests/cover/test_slices.py | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5378e33a03..09455c0c31 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -277,6 +277,7 @@ their individual contributions. * `Ryan Turner `_ (ryan.turner@uber.com) * `Sam Bishop (TechDragon) `_ (sam@techdragon.io) * `Sam Hames `_ +* `Sangarshanan `_ (sangarshanan1998@gmail.com) * `Sanyam Khurana `_ * `Saul Shanabrook `_ (s.shanabrook@gmail.com) * `Stuart Cook `_ @@ -298,4 +299,3 @@ their individual contributions. * `Wilfred Hughes `_ * `Zac Hatfield-Dodds `_ (zac.hatfield.dodds@gmail.com) * `Zebulun Arendsee `_ (zbwrnz@gmail.com) -* `Sangarshanan `_ (sangarshanan1998@gmail.com) diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py index b98d4d1e04..9a4b938798 100644 --- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py +++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py @@ -2206,10 +2206,9 @@ def slices(draw: Any, size: int) -> slice: if (stop or 0) < (start or 0): step *= -1 - if draw(booleans()): - if start is not None: - start -= size - if stop is not None: - stop -= size + if draw(booleans()) and start is not None: + start -= size + if draw(booleans()) and stop is not None: + stop -= size return slice(start, stop, step) diff --git a/hypothesis-python/tests/cover/test_slices.py b/hypothesis-python/tests/cover/test_slices.py index 5945fd903b..2fef0c3814 100644 --- a/hypothesis-python/tests/cover/test_slices.py +++ b/hypothesis-python/tests/cover/test_slices.py @@ -102,7 +102,6 @@ def test_start_will_equal_stop(size): find_any(st.slices(size), lambda x: x.start == x.stop) -@settings(deadline=None) def test_size_is_equal_0(): assert_all_examples( st.slices(0), lambda x: x.step != 0 and x.start is None and x.stop is None From a8d46ce4ae02ff5ff4f113b5b0857dddb9faa325 Mon Sep 17 00:00:00 2001 From: sangarshanan Date: Sat, 25 Jul 2020 20:42:31 +0530 Subject: [PATCH 4/4] Fix tests --- hypothesis-python/tests/cover/test_direct_strategies.py | 1 - hypothesis-python/tests/cover/test_slices.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hypothesis-python/tests/cover/test_direct_strategies.py b/hypothesis-python/tests/cover/test_direct_strategies.py index 82d33405bf..dd109262a4 100644 --- a/hypothesis-python/tests/cover/test_direct_strategies.py +++ b/hypothesis-python/tests/cover/test_direct_strategies.py @@ -170,7 +170,6 @@ def fn_ktest(*fnkwargs): (ds.characters, {"max_codepoint": "1"}), (ds.characters, {"whitelist_categories": []}), (ds.characters, {"whitelist_categories": ["Nd"], "blacklist_categories": ["Nd"]}), - (ds.slices, {"size": 0}), (ds.slices, {"size": None}), (ds.slices, {"size": "chips"}), (ds.slices, {"size": -1}), diff --git a/hypothesis-python/tests/cover/test_slices.py b/hypothesis-python/tests/cover/test_slices.py index 2fef0c3814..d471d3688d 100644 --- a/hypothesis-python/tests/cover/test_slices.py +++ b/hypothesis-python/tests/cover/test_slices.py @@ -49,7 +49,7 @@ def test_step_stays_within_bounds(size): x.indices(size)[0] + x.indices(size)[2] <= size and x.indices(size)[0] + x.indices(size)[2] >= -size ) - or x.start == x.stop, + or x.start % size == x.stop % size, )