diff --git a/Lib/test/test_list.py b/Lib/test/test_list.py index 642b54d34849da..bce2ee8f92f375 100644 --- a/Lib/test/test_list.py +++ b/Lib/test/test_list.py @@ -213,6 +213,20 @@ def test_reversed_pickle(self): a[:] = data self.assertEqual(list(it), []) + def test_exhausted_iterator_setstate(self): + # gh-129139: __setstate__ on an exhausted iterator must not revive it + it = iter([1, 2, 3]) + list(it) # exhaust the iterator + it.__setstate__(0) + self.assertRaises(StopIteration, next, it) + + def test_exhausted_reversed_iterator_setstate(self): + # gh-129139: __setstate__ on an exhausted reversed iterator must not revive it + it = reversed([1, 2, 3]) + list(it) # exhaust the iterator + it.__setstate__(0) + self.assertRaises(StopIteration, next, it) + def test_step_overflow(self): a = [0, 1, 2, 3, 4] a[1::sys.maxsize] = [0] diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-22-00-00-00.gh-issue-129139.8hyhJC.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-22-00-00-00.gh-issue-129139.8hyhJC.rst new file mode 100644 index 00000000000000..6d3ea87bdaa011 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-22-00-00-00.gh-issue-129139.8hyhJC.rst @@ -0,0 +1 @@ +In free-threaded build, fix ``__setstate__`` on exhausted list iterators and reversed list iterators reviving the iterator instead of being a no-op. diff --git a/Objects/listobject.c b/Objects/listobject.c index 654b8130e70840..31fc33a9d15039 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -4108,7 +4108,7 @@ listiter_setstate(PyObject *self, PyObject *state) Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; - if (it->it_seq != NULL) { + if (it->it_seq != NULL && FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index) >= 0) { if (index < -1) index = -1; else if (index > PyList_GET_SIZE(it->it_seq)) @@ -4260,7 +4260,7 @@ listreviter_setstate(PyObject *self, PyObject *state) Py_ssize_t index = PyLong_AsSsize_t(state); if (index == -1 && PyErr_Occurred()) return NULL; - if (it->it_seq != NULL) { + if (it->it_seq != NULL && FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index) >= 0) { if (index < -1) index = -1; else if (index > PyList_GET_SIZE(it->it_seq) - 1)