From aea82e89e8169a855b84cd20be720e9e6e961c9d Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 00:14:30 +0500 Subject: [PATCH 01/15] Allow only integers to longrangeiter_setstate state --- Lib/test/test_range.py | 9 +++++++++ Objects/rangeobject.c | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 3870b153688b25..de8eb76f8025ad 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,6 +470,15 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) + msg = "'float' object cannot be interpreted as an integer" + with self.assertRaisesRegex(TypeError, msg): + it = iter(range(10, 100, 2)) + it.__setstate__(1.0) + + with self.assertRaisesRegex(TypeError, msg): + it = iter(range(10, 2**65, 2)) + it.__setstate__(1.0) + def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" # because the range validation step was eating the exception diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index f8cdfe68a6435e..35fbb0ec9274ab 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -1063,6 +1063,12 @@ longrangeiter_setstate(PyObject *op, PyObject *state) PyObject *product = PyNumber_Multiply(state, r->step); if (product == NULL) return NULL; + if (!PyLong_Check(product)) { + Py_DECREF(product); + PyErr_Format(PyExc_TypeError, + "'%T' object cannot be interpreted as an integer", state); + return NULL; + } PyObject *new_start = PyNumber_Add(r->start, product); Py_DECREF(product); if (new_start == NULL) From 4eaee49b8903edcbb4daec7dcf0e32441c459375 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 01:06:13 +0500 Subject: [PATCH 02/15] Check state instead of product --- Objects/rangeobject.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 35fbb0ec9274ab..92129f35ee29a3 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -1042,6 +1042,12 @@ longrangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) static PyObject * longrangeiter_setstate(PyObject *op, PyObject *state) { + if (!PyLong_CheckExact(state)) { + PyErr_Format(PyExc_TypeError, + "'%T' object cannot be interpreted as an integer", state); + return NULL; + } + longrangeiterobject *r = (longrangeiterobject*)op; PyObject *zero = _PyLong_GetZero(); // borrowed reference int cmp; @@ -1063,12 +1069,6 @@ longrangeiter_setstate(PyObject *op, PyObject *state) PyObject *product = PyNumber_Multiply(state, r->step); if (product == NULL) return NULL; - if (!PyLong_Check(product)) { - Py_DECREF(product); - PyErr_Format(PyExc_TypeError, - "'%T' object cannot be interpreted as an integer", state); - return NULL; - } PyObject *new_start = PyNumber_Add(r->start, product); Py_DECREF(product); if (new_start == NULL) From c11596e218532813869615be9849072064a76cb6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 22:55:19 +0500 Subject: [PATCH 03/15] Update tests --- Lib/test/test_range.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index de8eb76f8025ad..fbcecba1fbec77 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,14 +470,24 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) - msg = "'float' object cannot be interpreted as an integer" - with self.assertRaisesRegex(TypeError, msg): - it = iter(range(10, 100, 2)) - it.__setstate__(1.0) - - with self.assertRaisesRegex(TypeError, msg): - it = iter(range(10, 2**65, 2)) - it.__setstate__(1.0) + def test_iterator_invalid_setstate(self): + + class I: + def __int__(self): return 1 + def __index__(self): return 1 + def __repr__(self): return "I()" + + invalid_values = (1.0, I(), "") + + for invalid_value in invalid_values: + invalid_msg = F"state must be an int, not " + ranges = (("range_iter", range(10, 100, 2)), + ("longrange_iter", range(10, 2**65, 2))) + for name, rng in ranges: + with self.subTest(invalid_value=invalid_value, range_name=name): + it = iter(rng) + with self.assertRaisesRegex(TypeError, invalid_msg): + it.__setstate__(invalid_value) def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" From 33190fed6d0119c1a7f9aa33add5f7fd71599ec6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 22:55:39 +0500 Subject: [PATCH 04/15] Add extra check to rangeiter_setstate --- Objects/rangeobject.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 92129f35ee29a3..80f46686d253d3 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -884,6 +884,11 @@ rangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) static PyObject * rangeiter_setstate(PyObject *op, PyObject *state) { + if (!PyLong_CheckExact(state)) { + PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state); + return NULL; + } + _PyRangeIterObject *r = (_PyRangeIterObject*)op; long index = PyLong_AsLong(state); if (index == -1 && PyErr_Occurred()) From 8d08e5a4f232942c84cc73d4034310536d3dcec5 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 22:56:04 +0500 Subject: [PATCH 05/15] Update error message for longrangeiter_setstate --- Objects/rangeobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 80f46686d253d3..fb6f8a6896d55a 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -1048,8 +1048,7 @@ static PyObject * longrangeiter_setstate(PyObject *op, PyObject *state) { if (!PyLong_CheckExact(state)) { - PyErr_Format(PyExc_TypeError, - "'%T' object cannot be interpreted as an integer", state); + PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state); return NULL; } From 4011aa231c48b5973dc2065bd53eecb1c09feced Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 23:07:15 +0500 Subject: [PATCH 06/15] Add news --- .../2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst new file mode 100644 index 00000000000000..8b9085f5d765f1 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst @@ -0,0 +1,2 @@ +Restict passing of non-integer object to the :meth:`~iterator.__setstate__` +for ranges. Patch by Sergey Miryanov. From 7e3c5afb876c83849238b3b252777c3656ec73d6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Mon, 10 Nov 2025 23:31:40 +0500 Subject: [PATCH 07/15] Fix news --- .../2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst index 8b9085f5d765f1..4fc9c452ecc5ac 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst @@ -1,2 +1,2 @@ -Restict passing of non-integer object to the :meth:`~iterator.__setstate__` -for ranges. Patch by Sergey Miryanov. +Restict passing of non-integer object to the ``rangeiter.__setstate__`` +and ``longrangeiter.__setstate__``. Patch by Sergey Miryanov. From ac8bda884083e79d7c851ab808db8f3ee8c0f6a6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Thu, 13 Nov 2025 17:51:09 +0500 Subject: [PATCH 08/15] Revert changes for rangeiter --- Lib/test/test_range.py | 37 +++++++++++++------ ...-11-10-23-07-06.gh-issue-141312.H-58GB.rst | 4 +- Objects/rangeobject.c | 5 --- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index fbcecba1fbec77..f7e7d097984205 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,24 +470,37 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) - def test_iterator_invalid_setstate(self): + class I: + def __int__(self): return 2 + def __index__(self): return 2 + def __repr__(self): return "I()" + + it = iter(range(10, 20, 2)) + it.__setstate__(I()) + self.assertEqual(list(it), [14, 16, 18]) + + def test_rangeiter_invalid_setstate(self): + + for invalid_value in (1.0, ""): + invalid_msg = "object cannot be interpreted as an integer" + with self.subTest(invalid_value=invalid_value): + it = iter(range(10, 100, 2)) + with self.assertRaisesRegex(TypeError, invalid_msg): + it.__setstate__(invalid_value) + + def test_longrangeiter_invalid_setstate(self): class I: def __int__(self): return 1 def __index__(self): return 1 def __repr__(self): return "I()" - invalid_values = (1.0, I(), "") - - for invalid_value in invalid_values: - invalid_msg = F"state must be an int, not " - ranges = (("range_iter", range(10, 100, 2)), - ("longrange_iter", range(10, 2**65, 2))) - for name, rng in ranges: - with self.subTest(invalid_value=invalid_value, range_name=name): - it = iter(rng) - with self.assertRaisesRegex(TypeError, invalid_msg): - it.__setstate__(invalid_value) + for invalid_value in (1.0, "", I()): + invalid_msg = "state must be an int, not " + with self.subTest(invalid_value=invalid_value): + it = iter(range(10, 2**65, 2)) + with self.assertRaisesRegex(TypeError, invalid_msg): + it.__setstate__(invalid_value) def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst index 4fc9c452ecc5ac..b323de7c5e3899 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst @@ -1,2 +1,2 @@ -Restict passing of non-integer object to the ``rangeiter.__setstate__`` -and ``longrangeiter.__setstate__``. Patch by Sergey Miryanov. +Restict passing of non-integer object to the ``longrangeiter.__setstate__``. +Patch by Sergey Miryanov. diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index fb6f8a6896d55a..e93346fb27703f 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -884,11 +884,6 @@ rangeiter_reduce(PyObject *op, PyObject *Py_UNUSED(ignored)) static PyObject * rangeiter_setstate(PyObject *op, PyObject *state) { - if (!PyLong_CheckExact(state)) { - PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state); - return NULL; - } - _PyRangeIterObject *r = (_PyRangeIterObject*)op; long index = PyLong_AsLong(state); if (index == -1 && PyErr_Occurred()) From a58d009d30055ad05c7a2585d38237aee8a4111b Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Thu, 13 Nov 2025 17:52:08 +0500 Subject: [PATCH 09/15] Remove empty lines --- Lib/test/test_range.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index f7e7d097984205..498b3e5f64c3c0 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -480,7 +480,6 @@ def __repr__(self): return "I()" self.assertEqual(list(it), [14, 16, 18]) def test_rangeiter_invalid_setstate(self): - for invalid_value in (1.0, ""): invalid_msg = "object cannot be interpreted as an integer" with self.subTest(invalid_value=invalid_value): @@ -489,7 +488,6 @@ def test_rangeiter_invalid_setstate(self): it.__setstate__(invalid_value) def test_longrangeiter_invalid_setstate(self): - class I: def __int__(self): return 1 def __index__(self): return 1 From 1b172df231355fc16de40df9284bdfb96a174ac9 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 10:47:21 +0500 Subject: [PATCH 10/15] Remove int dunder --- Lib/test/test_range.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 498b3e5f64c3c0..7a82de36186b11 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -471,7 +471,6 @@ def test_iterator_setstate(self): self.assertEqual(list(it), [12, 10]) class I: - def __int__(self): return 2 def __index__(self): return 2 def __repr__(self): return "I()" @@ -489,7 +488,6 @@ def test_rangeiter_invalid_setstate(self): def test_longrangeiter_invalid_setstate(self): class I: - def __int__(self): return 1 def __index__(self): return 1 def __repr__(self): return "I()" From 92d3ff2b8410662083958f6cb9ba67ecc915e62a Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 10:47:51 +0500 Subject: [PATCH 11/15] Update Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst Co-authored-by: Mikhail Efimov --- .../2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst index b323de7c5e3899..1b2eede72da6bd 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst @@ -1,2 +1,2 @@ -Restict passing of non-integer object to the ``longrangeiter.__setstate__``. +Restrict passing of non-integer object to the ``longrangeiter.__setstate__``. Patch by Sergey Miryanov. From 3db7418bf28f0a6866c2d9da18d5b2b5d7a639b6 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 13:11:34 +0500 Subject: [PATCH 12/15] Just check error type not a message --- Lib/test/test_range.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 7a82de36186b11..385dd7d2cf2603 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -480,10 +480,9 @@ def __repr__(self): return "I()" def test_rangeiter_invalid_setstate(self): for invalid_value in (1.0, ""): - invalid_msg = "object cannot be interpreted as an integer" with self.subTest(invalid_value=invalid_value): it = iter(range(10, 100, 2)) - with self.assertRaisesRegex(TypeError, invalid_msg): + with self.assertRaises(TypeError): it.__setstate__(invalid_value) def test_longrangeiter_invalid_setstate(self): @@ -492,10 +491,9 @@ def __index__(self): return 1 def __repr__(self): return "I()" for invalid_value in (1.0, "", I()): - invalid_msg = "state must be an int, not " with self.subTest(invalid_value=invalid_value): it = iter(range(10, 2**65, 2)) - with self.assertRaisesRegex(TypeError, invalid_msg): + with self.assertRaises(TypeError): it.__setstate__(invalid_value) def test_odd_bug(self): From 6f4c1f5272388eff658de7cc60e0ecdbab69ee93 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 14:37:01 +0500 Subject: [PATCH 13/15] Remove tests for integer-like objects --- Lib/test/test_range.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 385dd7d2cf2603..04acaaef04fec4 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,14 +470,6 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) - class I: - def __index__(self): return 2 - def __repr__(self): return "I()" - - it = iter(range(10, 20, 2)) - it.__setstate__(I()) - self.assertEqual(list(it), [14, 16, 18]) - def test_rangeiter_invalid_setstate(self): for invalid_value in (1.0, ""): with self.subTest(invalid_value=invalid_value): @@ -486,11 +478,7 @@ def test_rangeiter_invalid_setstate(self): it.__setstate__(invalid_value) def test_longrangeiter_invalid_setstate(self): - class I: - def __index__(self): return 1 - def __repr__(self): return "I()" - - for invalid_value in (1.0, "", I()): + for invalid_value in (1.0, ""): with self.subTest(invalid_value=invalid_value): it = iter(range(10, 2**65, 2)) with self.assertRaises(TypeError): From 0876224a5743e7b88d7155f824e7e6418dc0bcd1 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 15:41:02 +0500 Subject: [PATCH 14/15] Merge tests --- Lib/test/test_range.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_range.py b/Lib/test/test_range.py index 04acaaef04fec4..2c9c290e8906b7 100644 --- a/Lib/test/test_range.py +++ b/Lib/test/test_range.py @@ -470,19 +470,15 @@ def test_iterator_setstate(self): it.__setstate__(2**64 - 7) self.assertEqual(list(it), [12, 10]) - def test_rangeiter_invalid_setstate(self): + def test_iterator_invalid_setstate(self): for invalid_value in (1.0, ""): - with self.subTest(invalid_value=invalid_value): - it = iter(range(10, 100, 2)) - with self.assertRaises(TypeError): - it.__setstate__(invalid_value) - - def test_longrangeiter_invalid_setstate(self): - for invalid_value in (1.0, ""): - with self.subTest(invalid_value=invalid_value): - it = iter(range(10, 2**65, 2)) - with self.assertRaises(TypeError): - it.__setstate__(invalid_value) + ranges = (('rangeiter', range(10, 100, 2)), + ('longrangeiter', range(10, 2**65, 2))) + for rng_name, rng in ranges: + with self.subTest(invalid_value=invalid_value, range=rng_name): + it = iter(rng) + with self.assertRaises(TypeError): + it.__setstate__(invalid_value) def test_odd_bug(self): # This used to raise a "SystemError: NULL result without error" From 57ce2693ff7e58b55776cf78508c31466cf012b4 Mon Sep 17 00:00:00 2001 From: Sergey Miryanov Date: Fri, 14 Nov 2025 15:41:09 +0500 Subject: [PATCH 15/15] Update news --- .../2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst index 1b2eede72da6bd..fdb136cef3f33c 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-11-10-23-07-06.gh-issue-141312.H-58GB.rst @@ -1,2 +1,2 @@ -Restrict passing of non-integer object to the ``longrangeiter.__setstate__``. -Patch by Sergey Miryanov. +Fix the assertion failure in the ``__setstate__`` method of the range iterator +when a non-integer argument is passed. Patch by Sergey Miryanov.