]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-107208: Fix iter_index() recipe to not swallow exceptions (gh-108835) ...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Mon, 4 Sep 2023 11:46:35 +0000 (04:46 -0700)
committerGitHub <noreply@github.com>
Mon, 4 Sep 2023 11:46:35 +0000 (13:46 +0200)
gh-107208: Fix iter_index() recipe to not swallow exceptions (gh-108835)
(cherry picked from commit f373c6b9483e12d7f6e03a631601149ed60ab883)

gh-107208: iter_index now supports "stop" and no longer swallows ValueError

Co-authored-by: Raymond Hettinger <rhettinger@users.noreply.github.com>
Doc/library/itertools.rst

index 730736bbb59ed9665e6832d97c8ae657340eff50..72c68519b456e2cac10f6ab011c68de62017fd52 100644 (file)
@@ -865,26 +865,22 @@ which incur interpreter overhead.
        # first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
        return next(filter(pred, iterable), default)
 
-   def iter_index(iterable, value, start=0):
+   def iter_index(iterable, value, start=0, stop=None):
        "Return indices where a value occurs in a sequence or iterable."
        # iter_index('AABCADEAF', 'A') --> 0 1 4 7
-       try:
-           seq_index = iterable.index
-       except AttributeError:
+       seq_index = getattr(iterable, 'index', None)
+       if seq_index is None:
            # Slow path for general iterables
-           it = islice(iterable, start, None)
-           i = start - 1
-           try:
-               while True:
-                   yield (i := i + operator.indexOf(it, value) + 1)
-           except ValueError:
-               pass
+           it = islice(iterable, start, stop)
+           for i, element in enumerate(it, start):
+               if element is value or element == value:
+                   yield i
        else:
            # Fast path for sequences
            i = start - 1
            try:
                while True:
-                   yield (i := seq_index(value, i+1))
+                   yield (i := seq_index(value, i+1, stop))
            except ValueError:
                pass
 
@@ -1333,6 +1329,21 @@ The following recipes have a more mathematical flavor:
     []
     >>> list(iter_index(iter('AABCADEAF'), 'A', 10))
     []
+    >>> list(iter_index('AABCADEAF', 'A', 1, 7))
+    [1, 4]
+    >>> list(iter_index(iter('AABCADEAF'), 'A', 1, 7))
+    [1, 4]
+    >>> # Verify that ValueErrors not swallowed (gh-107208)
+    >>> def assert_no_value(iterable, forbidden_value):
+    ...     for item in iterable:
+    ...         if item == forbidden_value:
+    ...             raise ValueError
+    ...         yield item
+    ...
+    >>> list(iter_index(assert_no_value('AABCADEAF', 'B'), 'A'))
+    Traceback (most recent call last):
+    ...
+    ValueError
 
     >>> list(sieve(30))
     [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]