From 99a9faa8a41922c1c0b93a099774a8266bcb9cea Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 03:46:51 +0400 Subject: [PATCH 1/6] Allow passing before_and_after remainders into before_and_after again --- more_itertools/recipes.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/more_itertools/recipes.py b/more_itertools/recipes.py index 76c6815d..3fb49b53 100644 --- a/more_itertools/recipes.py +++ b/more_itertools/recipes.py @@ -744,11 +744,12 @@ def true_iterator(): transition.append(elem) return - def remainder_iterator(): - yield from transition - yield from it + # Note: this is different from itertools recipes to allow nesting + # before_and_after remainders into before_and_after again. See tests + # for an example. + remainder_iterator = chain(transition, it) - return true_iterator(), remainder_iterator() + return true_iterator(), remainder_iterator def triplewise(iterable): From 6afc74cdebd2423ae431514575d628d8b9928fe2 Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 03:56:46 +0400 Subject: [PATCH 2/6] Test passing before_and_after remainder into before_and_after again --- tests/test_recipes.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 6c3d9779..64948316 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -782,6 +782,35 @@ def test_some_true(self): before, after = mi.before_and_after(bool, [1, True, 0, False]) self.assertEqual(list(before), [1, True]) self.assertEqual(list(after), [0, False]) + + @staticmethod + def _group_events(events): + events = iter(events) + + while True: + try: + operation = next(events) + except StopIteration: + break + assert operation in ["SUM", "MULTIPLY"] + + # Here, the remainder `events` is passed into `before_and_after` + # again, which would be problematic if the remainder is a + # generator function (as in Python 3.10 itertools recipes), since + # that creates recursion. `itertools.chain` solves this problem. + numbers, events = before_and_after(lambda e: isinstance(e, int), events) + + yield (operation, numbers) + + def test_nested_remainder(self): + events = ["SUM", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000 + events += ["MULTIPLY", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000 + + for operation, numbers in self._group_events(events): + if operation == "SUM": + res = sum(numbers) + elif operation == "MULTIPLY": + res = reduce(lambda a, b: a * b, numbers) class TriplewiseTests(TestCase): From fcfaf98f95f2e16af088c52e12a265c139fe7f6d Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 04:02:17 +0400 Subject: [PATCH 3/6] Test passing before_and_after remainder into before_and_after again --- tests/test_recipes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 64948316..cd19bdde 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -1,6 +1,7 @@ import warnings from doctest import DocTestSuite +from functools import reduce from itertools import combinations, count, permutations from math import factorial from unittest import TestCase From e0c19d1cbc691e052de1c010551bed1bd793286a Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 04:28:43 +0400 Subject: [PATCH 4/6] Update tests/test_recipes.py Co-authored-by: Bo Bayles --- tests/test_recipes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index cd19bdde..6c2e6408 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -799,7 +799,7 @@ def _group_events(events): # again, which would be problematic if the remainder is a # generator function (as in Python 3.10 itertools recipes), since # that creates recursion. `itertools.chain` solves this problem. - numbers, events = before_and_after(lambda e: isinstance(e, int), events) + numbers, events = mi.before_and_after(lambda e: isinstance(e, int), events) yield (operation, numbers) From 5ef7562e97c2e603a2ee9b8ed9f58cefccaae9ea Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 04:56:24 +0400 Subject: [PATCH 5/6] Make flake8 happy --- tests/test_recipes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 6c2e6408..90f1627f 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -783,7 +783,7 @@ def test_some_true(self): before, after = mi.before_and_after(bool, [1, True, 0, False]) self.assertEqual(list(before), [1, True]) self.assertEqual(list(after), [0, False]) - + @staticmethod def _group_events(events): events = iter(events) @@ -802,7 +802,7 @@ def _group_events(events): numbers, events = mi.before_and_after(lambda e: isinstance(e, int), events) yield (operation, numbers) - + def test_nested_remainder(self): events = ["SUM", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000 events += ["MULTIPLY", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] * 1000 @@ -810,8 +810,10 @@ def test_nested_remainder(self): for operation, numbers in self._group_events(events): if operation == "SUM": res = sum(numbers) + self.assertEqual(res, 55) elif operation == "MULTIPLY": res = reduce(lambda a, b: a * b, numbers) + self.assertEqual(res, 3628800) class TriplewiseTests(TestCase): From bda68db765c88f8eff8b6596afa7c71b6918b35c Mon Sep 17 00:00:00 2001 From: Gleb Akhmerov <69253059+gleb-akhmerov@users.noreply.github.com> Date: Sat, 15 Oct 2022 05:00:35 +0400 Subject: [PATCH 6/6] Make flake8 happier! --- tests/test_recipes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_recipes.py b/tests/test_recipes.py index 90f1627f..44e35d10 100644 --- a/tests/test_recipes.py +++ b/tests/test_recipes.py @@ -799,7 +799,9 @@ def _group_events(events): # again, which would be problematic if the remainder is a # generator function (as in Python 3.10 itertools recipes), since # that creates recursion. `itertools.chain` solves this problem. - numbers, events = mi.before_and_after(lambda e: isinstance(e, int), events) + numbers, events = mi.before_and_after( + lambda e: isinstance(e, int), events + ) yield (operation, numbers)