8000 bpo-45018: Fix rangeiter_reduce in rangeobject.c by chilaxan · Pull Request #27938 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions Lib/test/test_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,26 +374,41 @@ def test_pickling(self):
list(r))

def test_iterator_pickling(self):
testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1),
(13, 21, 3), (-2, 2, 2), (2**65, 2**65+2)]
testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), (13, 21, 3),
(-2, 2, 2), (2**31-3, 2**31-1), (2**33, 2**33+2),
(2**63-3, 2**63-1), (2**65, 2**65+2)]
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
for t in testcases:
it = itorg = iter(range(*t))
data = list(range(*t))

d = pickle.dumps(it, proto)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(list(it), data)

it = pickle.loads(d)
try:
next(it)
except StopIteration:
continue
with self.subTest(proto=proto, t=t):
it = itorg = iter(range(*t))
data = list(range(*t))

d = pickle.dumps(it, proto)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(list(it), data)

it = pickle.loads(d)
try:
next(it)
except StopIteration:
continue
d = pickle.dumps(it, proto)
it = pickle.loads(d)
self.assertEqual(list(it), data[1:])

def test_iterator_pickling_overflowing_index(self):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.subTest(proto=proto):
it = iter(range(2**32 + 2))
_, _, idx = it.__reduce__()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test with __reduce__ is fragile. I have an idea of making the range iterator more compact and faster. __reduce__() will return different results, although pickle compatibility will be preserved.

Other Python implementations can use such range iterator implementation from start.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we include this test for now and adapt it once your proposed PR is up? I'm happy to review it then.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then other Python implementations will be limited in implementation of the range iterator.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the __reduce__ part is fragile then probably so is __setstate__. I think we should keep this so if an al 8000 ternative implementation actually sees this failing, they have a chance of speaking up on BPO or realizing what this test is even doing.

As I said, I fully support changing this test when/if we modify __reduce__ behavior in CPython in the future. But for now I don't see a downside of including it. Calls to __reduce__ are already used in tests for descriptors, datetime, OrderedDict, and structseq.c

self.assertEqual(idx, 0)
it.__setstate__(2**32 + 1) # undocumented way to set r->index
_, _, idx = it.__reduce__()
self.assertEqual(idx, 2**32 + 1)
d = pickle.dumps(it, proto)
it = pickle.loads(d)
self.assertEqual(list(it), data[1:])
self.assertEqual(next(it), 2**32 + 1)

def test_exhausted_iterator_pickling(self):
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed pickling of range iterators that iterated for over 2**32 times.
2 changes: 1 addition & 1 deletion Objects/rangeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ rangeiter_reduce(rangeiterobject *r, PyObject *Py_UNUSED(ignored))
if (range == NULL)
goto err;
/* return the result */
return Py_BuildValue("N(N)i", _PyEval_GetBuiltinId(&PyId_iter),
return Py_BuildValue("N(N)l", _PyEval_GetBuiltinId(&PyId_iter),
range, r->index);
err:
Py_XDECREF(start);
Expand Down
0