From 5aa075c801067648552fcadca486d2c3f2e4eaa8 Mon Sep 17 00:00:00 2001 From: ShaharNaveh <50263213+ShaharNaveh@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:15:08 +0200 Subject: [PATCH] Update `test_enumerate.py` from 3.13.5 --- Lib/test/test_enumerate.py | 31 +++++++++++++++++++++++++++++-- vm/src/builtins/enumerate.rs | 13 +++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_enumerate.py b/Lib/test/test_enumerate.py index 5785cb4649..5cb54cff9b 100644 --- a/Lib/test/test_enumerate.py +++ b/Lib/test/test_enumerate.py @@ -2,6 +2,7 @@ import operator import sys import pickle +import gc from test import support @@ -127,6 +128,18 @@ def test_argumentcheck(self): self.assertRaises(TypeError, self.enum, 'abc', 'a') # wrong type self.assertRaises(TypeError, self.enum, 'abc', 2, 3) # too many arguments + def test_kwargs(self): + self.assertEqual(list(self.enum(iterable=Ig(self.seq))), self.res) + expected = list(self.enum(Ig(self.seq), 0)) + self.assertEqual(list(self.enum(iterable=Ig(self.seq), start=0)), + expected) + self.assertEqual(list(self.enum(start=0, iterable=Ig(self.seq))), + expected) + self.assertRaises(TypeError, self.enum, iterable=[], x=3) + self.assertRaises(TypeError, self.enum, start=0, x=3) + self.assertRaises(TypeError, self.enum, x=0, y=3) + self.assertRaises(TypeError, self.enum, x=0) + @support.cpython_only def test_tuple_reuse(self): # Tests an implementation detail where tuple is reused @@ -134,6 +147,18 @@ def test_tuple_reuse(self): self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq)) self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq))) + @support.cpython_only + def test_enumerate_result_gc(self): + # bpo-42536: enumerate's tuple-reuse speed trick breaks the GC's + # assumptions about what can be untracked. Make sure we re-track result + # tuples whenever we reuse them. + it = self.enum([[]]) + gc.collect() + # That GC collection probably untracked the recycled internal result + # tuple, which is initialized to (None, None). Make sure it's re-tracked + # when it's mutated and returned from __next__: + self.assertTrue(gc.is_tracked(next(it))) + class MyEnum(enumerate): pass @@ -253,14 +278,16 @@ def test_basicfunction(self): class TestStart(EnumerateStartTestCase): + def enum(self, iterable, start=11): + return enumerate(iterable, start=start) - enum = lambda self, i: enumerate(i, start=11) seq, res = 'abc', [(11, 'a'), (12, 'b'), (13, 'c')] class TestLongStart(EnumerateStartTestCase): + def enum(self, iterable, start=sys.maxsize + 1): + return enumerate(iterable, start=start) - enum = lambda self, i: enumerate(i, start=sys.maxsize+1) seq, res = 'abc', [(sys.maxsize+1,'a'), (sys.maxsize+2,'b'), (sys.maxsize+3,'c')] diff --git a/vm/src/builtins/enumerate.rs b/vm/src/builtins/enumerate.rs index db3d45b248..a83404c734 100644 --- a/vm/src/builtins/enumerate.rs +++ b/vm/src/builtins/enumerate.rs @@ -19,7 +19,7 @@ use num_traits::Zero; pub struct PyEnumerate { #[pytraverse(skip)] counter: PyRwLock, - iterator: PyIter, + iterable: PyIter, } impl PyPayload for PyEnumerate { @@ -31,7 +31,8 @@ impl PyPayload for PyEnumerate { #[derive(FromArgs)] pub struct EnumerateArgs { - iterator: PyIter, + #[pyarg(any)] + iterable: PyIter, #[pyarg(any, optional)] start: OptionalArg, } @@ -41,13 +42,13 @@ impl Constructor for PyEnumerate { fn py_new( cls: PyTypeRef, - Self::Args { iterator, start }: Self::Args, + Self::Args { iterable, start }: Self::Args, vm: &VirtualMachine, ) -> PyResult { let counter = start.map_or_else(BigInt::zero, |start| start.as_bigint().clone()); Self { counter: PyRwLock::new(counter), - iterator, + iterable, } .into_ref_with_type(vm, cls) .map(Into::into) @@ -68,7 +69,7 @@ impl Py { fn __reduce__(&self) -> (PyTypeRef, (PyIter, BigInt)) { ( self.class().to_owned(), - (self.iterator.clone(), self.counter.read().clone()), + (self.iterable.clone(), self.counter.read().clone()), ) } } @@ -77,7 +78,7 @@ impl SelfIter for PyEnumerate {} impl IterNext for PyEnumerate { fn next(zelf: &Py, vm: &VirtualMachine) -> PyResult { - let next_obj = raise_if_stop!(zelf.iterator.next(vm)?); + let next_obj = raise_if_stop!(zelf.iterable.next(vm)?); let mut counter = zelf.counter.write(); let position = counter.clone(); *counter += 1;