Skip to content

Commit c5abb14

Browse files
committed
Add iter reduce tests for issue python#101765
1 parent efa0540 commit c5abb14

File tree

1 file changed

+52
-0
lines changed

1 file changed

+52
-0
lines changed

Lib/test/test_iter.py

+52
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ def __call__(self):
9191
raise IndexError # Emergency stop
9292
return i
9393

94+
class EmptyIterClass:
95+
def __len__(self):
96+
return 0
97+
def __getitem__(self, i):
98+
raise StopIteration
99+
94100
# Main test suite
95101

96102
class TestCase(unittest.TestCase):
@@ -238,6 +244,52 @@ def test_mutating_seq_class_exhausted_iter(self):
238244
self.assertEqual(list(empit), [5, 6])
239245
self.assertEqual(list(a), [0, 1, 2, 3, 4, 5, 6])
240246

247+
def test_reduce_mutating_builtins_iter(self):
248+
# This is a reproducer of issue #101765
249+
# where iter `__reduce__` calls could lead to a segfault or SystemError
250+
# depending on the order of C argument evaluation, which is undefined
251+
252+
# Backup builtins.iter
253+
builtins = __builtins__.__dict__ if hasattr(__builtins__, "__dict__") else __builtins__
254+
orig_iter = builtins["iter"]
255+
256+
def run(item):
257+
(fn, *flag), *initializer = item
258+
it = iter(fn(*initializer), *flag)
259+
260+
class CustomStr:
261+
def __hash__(self):
262+
return hash("iter")
263+
def __eq__(self, other):
264+
# Here we exhaust `it`, possibly changing our `it_seq` pointer to NULL
265+
# The `__reduce__` call should correctly get the pointers after this call
266+
list(it)
267+
return other == "iter"
268+
269+
_iter = builtins["iter"]
270+
del builtins["iter"]
271+
builtins[CustomStr()] = _iter
272+
273+
return it.__reduce__()
274+
275+
types = [
276+
([EmptyIterClass],),
277+
([bytes], 8),
278+
([bytearray], 8),
279+
([tuple], range(8)),
280+
([lambda: (lambda: 0), 0],)
281+
]
282+
283+
try:
284+
self.assertEqual(run(([str], "xyz")), (orig_iter, ("xyz",), 0))
285+
self.assertEqual(run(([list], range(8))), (orig_iter, ([],)))
286+
for case in types:
287+
self.assertEqual(run(case), (orig_iter, ((),)))
288+
finally:
289+
# Restore original iter
290+
del builtins["iter"]
291+
builtins["iter"] = orig_iter
292+
241293
# Test a new_style class with __iter__ but no next() method
242294
def test_new_style_iter_class(self):
243295
class IterClass(object):

0 commit comments

Comments
 (0)