commit:     07b4f2180a86354c212c11f38e80d408e353ebd9
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Mon Oct 27 16:52:50 2025 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Mon Oct 27 21:28:21 2025 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=07b4f218

fix: don't suppress TypeError in iter_stable_unique edge case.

If the iterator itself throws the TypeError before we get a value
out of it, 'x' would be unbound, which would then explode for when
the code falls back to the linear list search for unhashable items.

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>

 src/snakeoil/sequences.py | 22 +++++++++++++---------
 tests/test_sequences.py   | 10 +++++++++-
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/src/snakeoil/sequences.py b/src/snakeoil/sequences.py
index b80b101..b7e0ba7 100644
--- a/src/snakeoil/sequences.py
+++ b/src/snakeoil/sequences.py
@@ -71,25 +71,29 @@ def iter_stable_unique(iterable):
     For performance reasons, only use this if you really do need to preserve
     the ordering.
     """
-    s = set()
-    sadd = s.add
-    sl = []
-    slappend = sl.append
+    seen = set()
+    unhashable_seen = []
     iterable = iter(iterable)
     # the reason for this structuring is purely speed- entering try/except
     # repeatedly is costly, thus structure it to penalize the unhashables
     # instead of penalizing the hashables.
+    singleton = object()
+
     while True:
+        x = singleton
         try:
             for x in iterable:
-                if x not in s:
+                if x not in seen:
                     yield x
-                    sadd(x)
+                    seen.add(x)
         except TypeError:
-            # unhashable item...
-            if x not in sl:
+            # unhashable item pathway
+            if x is singleton:
+                # the iterable itself threw the TypeError
+                raise
+            if x not in unhashable_seen:
                 yield x
-                slappend(x)
+                unhashable_seen.append(x)
             continue
         break
 

diff --git a/tests/test_sequences.py b/tests/test_sequences.py
index 0d8c5a6..b558647 100644
--- a/tests/test_sequences.py
+++ b/tests/test_sequences.py
@@ -3,8 +3,9 @@ from itertools import chain
 from operator import itemgetter
 
 import pytest
+
 from snakeoil import sequences
-from snakeoil.sequences import split_elements, split_negations
+from snakeoil.sequences import iter_stable_unique, split_elements, 
split_negations
 
 
 class UnhashableComplex(complex):
@@ -33,6 +34,13 @@ class TestStableUnique:
         l = [1, 2, 3, o, UnhashableComplex(), 4, 3, UnhashableComplex()]
         assert list(sequences.iter_stable_unique(l)) == [1, 2, 3, o, 4]
 
+    def test_TypeError_propagation(self):
+        def iterator():
+            raise TypeError()
+            yield
+
+        pytest.raises(TypeError, lambda: list(iter_stable_unique(iterator())))
+
     def _generator(self):
         for x in range(5, -1, -1):
             yield x

Reply via email to