Issue #14288: Serialization support for builtin iterators.

parent 283b96b6
...@@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *); ...@@ -18,6 +18,8 @@ PyAPI_FUNC(PyObject *) PySeqIter_New(PyObject *);
PyAPI_FUNC(PyObject *) PyCallIter_New(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyCallIter_New(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyIter_GetBuiltin(const char *iter);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
......
...@@ -4,6 +4,7 @@ Tests common to tuple, list and UserList.UserList ...@@ -4,6 +4,7 @@ Tests common to tuple, list and UserList.UserList
import unittest import unittest
import sys import sys
import pickle
# Various iterables # Various iterables
# This is used for checking the constructor (here and in test_deque.py) # This is used for checking the constructor (here and in test_deque.py)
...@@ -388,3 +389,9 @@ class CommonTest(unittest.TestCase): ...@@ -388,3 +389,9 @@ class CommonTest(unittest.TestCase):
self.assertEqual(a.index(0, -4*sys.maxsize, 4*sys.maxsize), 2) self.assertEqual(a.index(0, -4*sys.maxsize, 4*sys.maxsize), 2)
self.assertRaises(ValueError, a.index, 0, 4*sys.maxsize,-4*sys.maxsize) self.assertRaises(ValueError, a.index, 0, 4*sys.maxsize,-4*sys.maxsize)
self.assertRaises(ValueError, a.index, 2, 0, -10) self.assertRaises(ValueError, a.index, 2, 0, -10)
def test_pickle(self):
lst = self.type2test([4, 5, 6, 7])
lst2 = pickle.loads(pickle.dumps(lst))
self.assertEqual(lst2, lst)
self.assertNotEqual(id(lst2), id(lst))
...@@ -285,6 +285,20 @@ class BaseTest(unittest.TestCase): ...@@ -285,6 +285,20 @@ class BaseTest(unittest.TestCase):
self.assertEqual(a.x, b.x) self.assertEqual(a.x, b.x)
self.assertEqual(type(a), type(b)) self.assertEqual(type(a), type(b))
def test_iterator_pickle(self):
data = array.array(self.typecode, self.example)
orgit = iter(data)
d = pickle.dumps(orgit)
it = pickle.loads(d)
self.assertEqual(type(orgit), type(it))
self.assertEqual(list(it), list(data))
if len(data):
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(list(it), list(data)[1:])
def test_insert(self): def test_insert(self):
a = array.array(self.typecode, self.example) a = array.array(self.typecode, self.example)
a.insert(0, self.example[0]) a.insert(0, self.example[0])
......
...@@ -14,6 +14,7 @@ import random ...@@ -14,6 +14,7 @@ import random
import traceback import traceback
from test.support import TESTFN, unlink, run_unittest, check_warnings from test.support import TESTFN, unlink, run_unittest, check_warnings
from operator import neg from operator import neg
import pickle
try: try:
import pty, signal import pty, signal
except ImportError: except ImportError:
...@@ -110,7 +111,30 @@ class TestFailingIter: ...@@ -110,7 +111,30 @@ class TestFailingIter:
def __iter__(self): def __iter__(self):
raise RuntimeError raise RuntimeError
def filter_char(arg):
return ord(arg) > ord("d")
def map_char(arg):
return chr(ord(arg)+1)
class BuiltinTest(unittest.TestCase): class BuiltinTest(unittest.TestCase):
# Helper to check picklability
def check_iter_pickle(self, it, seq):
itorg = it
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(list(it), seq)
#test the iterator after dropping one from it
it = pickle.loads(d)
try:
next(it)
except StopIteration:
return
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(list(it), seq[1:])
def test_import(self): def test_import(self):
__import__('sys') __import__('sys')
...@@ -566,6 +590,11 @@ class BuiltinTest(unittest.TestCase): ...@@ -566,6 +590,11 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(list(filter(lambda x: x>=3, (1, 2, 3, 4))), [3, 4]) self.assertEqual(list(filter(lambda x: x>=3, (1, 2, 3, 4))), [3, 4])
self.assertRaises(TypeError, list, filter(42, (1, 2))) self.assertRaises(TypeError, list, filter(42, (1, 2)))
def test_filter_pickle(self):
f1 = filter(filter_char, "abcdeabcde")
f2 = filter(filter_char, "abcdeabcde")
self.check_iter_pickle(f1, list(f2))
def test_getattr(self): def test_getattr(self):
self.assertTrue(getattr(sys, 'stdout') is sys.stdout) self.assertTrue(getattr(sys, 'stdout') is sys.stdout)
self.assertRaises(TypeError, getattr, sys, 1) self.assertRaises(TypeError, getattr, sys, 1)
...@@ -759,6 +788,11 @@ class BuiltinTest(unittest.TestCase): ...@@ -759,6 +788,11 @@ class BuiltinTest(unittest.TestCase):
raise RuntimeError raise RuntimeError
self.assertRaises(RuntimeError, list, map(badfunc, range(5))) self.assertRaises(RuntimeError, list, map(badfunc, range(5)))
def test_map_pickle(self):
m1 = map(map_char, "Is this the real life?")
m2 = map(map_char, "Is this the real life?")
self.check_iter_pickle(m1, list(m2))
def test_max(self): def test_max(self):
self.assertEqual(max('123123'), '3') self.assertEqual(max('123123'), '3')
self.assertEqual(max(1, 2, 3), 3) self.assertEqual(max(1, 2, 3), 3)
...@@ -1300,6 +1334,13 @@ class BuiltinTest(unittest.TestCase): ...@@ -1300,6 +1334,13 @@ class BuiltinTest(unittest.TestCase):
return i return i
self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq())) self.assertRaises(ValueError, list, zip(BadSeq(), BadSeq()))
def test_zip_pickle(self):
a = (1, 2, 3)
b = (4, 5, 6)
t = [(1, 4), (2, 5), (3, 6)]
z1 = zip(a, b)
self.check_iter_pickle(z1, t)
def test_format(self): def test_format(self):
# Test the basic machinery of the format() builtin. Don't test # Test the basic machinery of the format() builtin. Don't test
# the specifics of the various formatters # the specifics of the various formatters
......
...@@ -518,6 +518,24 @@ class BaseBytesTest(unittest.TestCase): ...@@ -518,6 +518,24 @@ class BaseBytesTest(unittest.TestCase):
q = pickle.loads(ps) q = pickle.loads(ps)
self.assertEqual(b, q) self.assertEqual(b, q)
def test_iterator_pickling(self):
for b in b"", b"a", b"abc", b"\xffab\x80", b"\0\0\377\0\0":
it = itorg = iter(self.type2test(b))
data = list(self.type2test(b))
d = pickle.dumps(it)
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)
it = pickle.loads(d)
self.assertEqual(list(it), data[1:])
def test_strip(self): def test_strip(self):
b = self.type2test(b'mississippi') b = self.type2test(b'mississippi')
self.assertEqual(b.strip(b'i'), b'mississipp') self.assertEqual(b.strip(b'i'), b'mississipp')
......
...@@ -471,6 +471,19 @@ class TestBasic(unittest.TestCase): ...@@ -471,6 +471,19 @@ class TestBasic(unittest.TestCase):
## self.assertNotEqual(id(d), id(e)) ## self.assertNotEqual(id(d), id(e))
## self.assertEqual(id(e), id(e[-1])) ## self.assertEqual(id(e), id(e[-1]))
def test_iterator_pickle(self):
data = deque(range(200))
it = itorg = iter(data)
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(list(it), list(data))
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(list(it), list(data)[1:])
def test_deepcopy(self): def test_deepcopy(self):
mut = [10] mut = [10]
d = deque([mut]) d = deque([mut])
......
...@@ -2,7 +2,9 @@ import unittest ...@@ -2,7 +2,9 @@ import unittest
from test import support from test import support
import collections, random, string import collections, random, string
import collections.abc
import gc, weakref import gc, weakref
import pickle
class DictTest(unittest.TestCase): class DictTest(unittest.TestCase):
...@@ -803,6 +805,58 @@ class DictTest(unittest.TestCase): ...@@ -803,6 +805,58 @@ class DictTest(unittest.TestCase):
pass pass
self._tracked(MyDict()) self._tracked(MyDict())
def test_iterator_pickling(self):
data = {1:"a", 2:"b", 3:"c"}
it = iter(data)
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(sorted(it), sorted(data))
it = pickle.loads(d)
try:
drop = next(it)
except StopIteration:
return
d = pickle.dumps(it)
it = pickle.loads(d)
del data[drop]
self.assertEqual(sorted(it), sorted(data))
def test_itemiterator_pickling(self):
data = {1:"a", 2:"b", 3:"c"}
# dictviews aren't picklable, only their iterators
itorg = iter(data.items())
d = pickle.dumps(itorg)
it = pickle.loads(d)
# note that the type of type of the unpickled iterator
# is not necessarily the same as the original. It is
# merely an object supporting the iterator protocol, yielding
# the same objects as the original one.
# self.assertEqual(type(itorg), type(it))
self.assertTrue(isinstance(it, collections.abc.Iterator))
self.assertEqual(dict(it), data)
it = pickle.loads(d)
drop = next(it)
d = pickle.dumps(it)
it = pickle.loads(d)
del data[drop[0]]
self.assertEqual(dict(it), data)
def test_valuesiterator_pickling(self):
data = {1:"a", 2:"b", 3:"c"}
# data.values() isn't picklable, only its iterator
it = iter(data.values())
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(sorted(list(it)), sorted(list(data.values())))
it = pickle.loads(d)
drop = next(it)
d = pickle.dumps(it)
it = pickle.loads(d)
values = list(it) + [drop]
self.assertEqual(sorted(values), sorted(list(data.values())))
from test import mapping_tests from test import mapping_tests
......
import unittest import unittest
import sys import sys
import pickle
from test import support from test import support
...@@ -61,7 +62,25 @@ class N: ...@@ -61,7 +62,25 @@ class N:
def __iter__(self): def __iter__(self):
return self return self
class EnumerateTestCase(unittest.TestCase): class PickleTest:
# Helper to check picklability
def check_pickle(self, itorg, seq):
d = pickle.dumps(itorg)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(list(it), seq)
it = pickle.loads(d)
try:
next(it)
except StopIteration:
self.assertFalse(seq[1:])
return
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(list(it), seq[1:])
class EnumerateTestCase(unittest.TestCase, PickleTest):
enum = enumerate enum = enumerate
seq, res = 'abc', [(0,'a'), (1,'b'), (2,'c')] seq, res = 'abc', [(0,'a'), (1,'b'), (2,'c')]
...@@ -73,6 +92,9 @@ class EnumerateTestCase(unittest.TestCase): ...@@ -73,6 +92,9 @@ class EnumerateTestCase(unittest.TestCase):
self.assertEqual(list(self.enum(self.seq)), self.res) self.assertEqual(list(self.enum(self.seq)), self.res)
self.enum.__doc__ self.enum.__doc__
def test_pickle(self):
self.check_pickle(self.enum(self.seq), self.res)
def test_getitemseqn(self): def test_getitemseqn(self):
self.assertEqual(list(self.enum(G(self.seq))), self.res) self.assertEqual(list(self.enum(G(self.seq))), self.res)
e = self.enum(G('')) e = self.enum(G(''))
...@@ -126,7 +148,7 @@ class TestBig(EnumerateTestCase): ...@@ -126,7 +148,7 @@ class TestBig(EnumerateTestCase):
seq = range(10,20000,2) seq = range(10,20000,2)
res = list(zip(range(20000), seq)) res = list(zip(range(20000), seq))
class TestReversed(unittest.TestCase): class TestReversed(unittest.TestCase, PickleTest):
def test_simple(self): def test_simple(self):
class A: class A:
...@@ -212,6 +234,10 @@ class TestReversed(unittest.TestCase): ...@@ -212,6 +234,10 @@ class TestReversed(unittest.TestCase):
ngi = NoGetItem() ngi = NoGetItem()
self.assertRaises(TypeError, reversed, ngi) self.assertRaises(TypeError, reversed, ngi)
def test_pickle(self):
for data in 'abc', range(5), tuple(enumerate('abc')), range(1,17,5):
self.check_pickle(reversed(data), list(data)[::-1])
class EnumerateStartTestCase(EnumerateTestCase): class EnumerateStartTestCase(EnumerateTestCase):
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import unittest import unittest
from test.support import run_unittest, TESTFN, unlink, cpython_only from test.support import run_unittest, TESTFN, unlink, cpython_only
import pickle
import collections.abc
# Test result of triple loop (too big to inline) # Test result of triple loop (too big to inline)
TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2), TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2),
...@@ -28,6 +30,8 @@ class BasicIterClass: ...@@ -28,6 +30,8 @@ class BasicIterClass:
raise StopIteration raise StopIteration
self.i = res + 1 self.i = res + 1
return res return res
def __iter__(self):
return self
class IteratingSequenceClass: class IteratingSequenceClass:
def __init__(self, n): def __init__(self, n):
...@@ -49,7 +53,9 @@ class SequenceClass: ...@@ -49,7 +53,9 @@ class SequenceClass:
class TestCase(unittest.TestCase): class TestCase(unittest.TestCase):
# Helper to check that an iterator returns a given sequence # Helper to check that an iterator returns a given sequence
def check_iterator(self, it, seq): def check_iterator(self, it, seq, pickle=True):
if pickle:
self.check_pickle(it, seq)
res = [] res = []
while 1: while 1:
try: try:
...@@ -60,12 +66,33 @@ class TestCase(unittest.TestCase): ...@@ -60,12 +66,33 @@ class TestCase(unittest.TestCase):
self.assertEqual(res, seq) self.assertEqual(res, seq)
# Helper to check that a for loop generates a given sequence # Helper to check that a for loop generates a given sequence
def check_for_loop(self, expr, seq): def check_for_loop(self, expr, seq, pickle=True):
if pickle:
self.check_pickle(iter(expr), seq)
res = [] res = []
for val in expr: for val in expr:
res.append(val) res.append(val)
self.assertEqual(res, seq) self.assertEqual(res, seq)
# Helper to check picklability
def check_pickle(self, itorg, seq):
d = pickle.dumps(itorg)
it = pickle.loads(d)
# Cannot assert type equality because dict iterators unpickle as list
# iterators.
# self.assertEqual(type(itorg), type(it))
self.assertTrue(isinstance(it, collections.abc.Iterator))
self.assertEqual(list(it), seq)
it = pickle.loads(d)
try:
next(it)
except StopIteration:
return
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(list(it), seq[1:])
# Test basic use of iter() function # Test basic use of iter() function
def test_iter_basic(self): def test_iter_basic(self):
self.check_iterator(iter(range(10)), list(range(10))) self.check_iterator(iter(range(10)), list(range(10)))
...@@ -138,7 +165,7 @@ class TestCase(unittest.TestCase): ...@@ -138,7 +165,7 @@ class TestCase(unittest.TestCase):
if i > 100: if i > 100:
raise IndexError # Emergency stop raise IndexError # Emergency stop
return i return i
self.check_iterator(iter(C(), 10), list(range(10))) self.check_iterator(iter(C(), 10), list(range(10)), pickle=False)
# Test two-argument iter() with function # Test two-argument iter() with function
def test_iter_function(self): def test_iter_function(self):
...@@ -146,7 +173,7 @@ class TestCase(unittest.TestCase): ...@@ -146,7 +173,7 @@ class TestCase(unittest.TestCase):
i = state[0] i = state[0]
state[0] = i+1 state[0] = i+1
return i return i
self.check_iterator(iter(spam, 10), list(range(10))) self.check_iterator(iter(spam, 10), list(range(10)), pickle=False)
# Test two-argument iter() with function that raises StopIteration # Test two-argument iter() with function that raises StopIteration
def test_iter_function_stop(self): def test_iter_function_stop(self):
...@@ -156,7 +183,7 @@ class TestCase(unittest.TestCase): ...@@ -156,7 +183,7 @@ class TestCase(unittest.TestCase):
raise StopIteration raise StopIteration
state[0] = i+1 state[0] = i+1
return i return i
self.check_iterator(iter(spam, 20), list(range(10))) self.check_iterator(iter(spam, 20), list(range(10)), pickle=False)
# Test exception propagation through function iterator # Test exception propagation through function iterator
def test_exception_function(self): def test_exception_function(self):
...@@ -198,7 +225,7 @@ class TestCase(unittest.TestCase): ...@@ -198,7 +225,7 @@ class TestCase(unittest.TestCase):
if i == 10: if i == 10:
raise StopIteration raise StopIteration
return SequenceClass.__getitem__(self, i) return SequenceClass.__getitem__(self, i)
self.check_for_loop(MySequenceClass(20), list(range(10))) self.check_for_loop(MySequenceClass(20), list(range(10)), pickle=False)
# Test a big range # Test a big range
def test_iter_big_range(self): def test_iter_big_range(self):
...@@ -237,8 +264,8 @@ class TestCase(unittest.TestCase): ...@@ -237,8 +264,8 @@ class TestCase(unittest.TestCase):
f.close() f.close()
f = open(TESTFN, "r") f = open(TESTFN, "r")
try: try:
self.check_for_loop(f, ["0\n", "1\n", "2\n", "3\n", "4\n"]) self.check_for_loop(f, ["0\n", "1\n", "2\n", "3\n", "4\n"], pickle=False)
self.check_for_loop(f, []) self.check_for_loop(f, [], pickle=False)
finally: finally:
f.close() f.close()
try: try:
......
This diff is collapsed.
import sys import sys
from test import support, list_tests from test import support, list_tests
import pickle
class ListTest(list_tests.CommonTest): class ListTest(list_tests.CommonTest):
type2test = list type2test = list
...@@ -69,6 +70,33 @@ class ListTest(list_tests.CommonTest): ...@@ -69,6 +70,33 @@ class ListTest(list_tests.CommonTest):
check(10) # check our checking code check(10) # check our checking code
check(1000000) check(1000000)
def test_iterator_pickle(self):
# Userlist iterators don't support pickling yet since
# they are based on generators.
data = self.type2test([4, 5, 6, 7])
it = itorg = iter(data)
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(self.type2test(it), self.type2test(data))
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(self.type2test(it), self.type2test(data)[1:])
def test_reversed_pickle(self):
data = self.type2test([4, 5, 6, 7])
it = itorg = reversed(data)
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(self.type2test(it), self.type2test(reversed(data)))
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(self.type2test(it), self.type2test(reversed(data))[1:])
def test_main(verbose=None): def test_main(verbose=None):
support.run_unittest(ListTest) support.run_unittest(ListTest)
......
...@@ -341,13 +341,35 @@ class RangeTest(unittest.TestCase): ...@@ -341,13 +341,35 @@ class RangeTest(unittest.TestCase):
def test_pickling(self): def test_pickling(self):
testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1), testcases = [(13,), (0, 11), (-22, 10), (20, 3, -1),
(13, 21, 3), (-2, 2, 2)] (13, 21, 3), (-2, 2, 2), (2**65, 2**65+2)]
for proto in range(pickle.HIGHEST_PROTOCOL + 1): for proto in range(pickle.HIGHEST_PROTOCOL + 1):
for t in testcases: for t in testcases:
r = range(*t) r = range(*t)
self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))), self.assertEqual(list(pickle.loads(pickle.dumps(r, proto))),
list(r)) 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)]
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)
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)
it = pickle.loads(d)
self.assertEqual(list(it), data[1:])
def test_odd_bug(self): def test_odd_bug(self):
# This used to raise a "SystemError: NULL result without error" # This used to raise a "SystemError: NULL result without error"
# because the range validation step was eating the exception # because the range validation step was eating the exception
......
...@@ -9,6 +9,7 @@ from random import randrange, shuffle ...@@ -9,6 +9,7 @@ from random import randrange, shuffle
import sys import sys
import warnings import warnings
import collections import collections
import collections.abc
class PassThru(Exception): class PassThru(Exception):
pass pass
...@@ -234,6 +235,26 @@ class TestJointOps(unittest.TestCase): ...@@ -234,6 +235,26 @@ class TestJointOps(unittest.TestCase):
dup = pickle.loads(p) dup = pickle.loads(p)
self.assertEqual(self.s.x, dup.x) self.assertEqual(self.s.x, dup.x)
def test_iterator_pickling(self):
itorg = iter(self.s)
data = self.thetype(self.s)
d = pickle.dumps(itorg)
it = pickle.loads(d)
# Set iterators unpickle as list iterators due to the
# undefined order of set items.
# self.assertEqual(type(itorg), type(it))
self.assertTrue(isinstance(it, collections.abc.Iterator))
self.assertEqual(self.thetype(it), data)
it = pickle.loads(d)
try:
drop = next(it)
except StopIteration:
return
d = pickle.dumps(it)
it = pickle.loads(d)
self.assertEqual(self.thetype(it), data - self.thetype((drop,)))
def test_deepcopy(self): def test_deepcopy(self):
class Tracer: class Tracer:
def __init__(self, value): def __init__(self, value):
......
from test import support, seq_tests from test import support, seq_tests
import gc import gc
import pickle
class TupleTest(seq_tests.CommonTest): class TupleTest(seq_tests.CommonTest):
type2test = tuple type2test = tuple
...@@ -164,6 +165,34 @@ class TupleTest(seq_tests.CommonTest): ...@@ -164,6 +165,34 @@ class TupleTest(seq_tests.CommonTest):
check(10) # check our checking code check(10) # check our checking code
check(1000000) check(1000000)
def test_iterator_pickle(self):
# Userlist iterators don't support pickling yet since
# they are based on generators.
data = self.type2test([4, 5, 6, 7])
itorg = iter(data)
d = pickle.dumps(itorg)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(self.type2test(it), self.type2test(data))
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(self.type2test(it), self.type2test(data)[1:])
def test_reversed_pickle(self):
data = self.type2test([4, 5, 6, 7])
itorg = reversed(data)
d = pickle.dumps(itorg)
it = pickle.loads(d)
self.assertEqual(type(itorg), type(it))
self.assertEqual(self.type2test(it), self.type2test(reversed(data)))
it = pickle.loads(d)
next(it)
d = pickle.dumps(it)
self.assertEqual(self.type2test(it), self.type2test(reversed(data))[1:])
def test_main(): def test_main():
support.run_unittest(TupleTest) support.run_unittest(TupleTest)
......
...@@ -1121,6 +1121,35 @@ dequeiter_next(dequeiterobject *it) ...@@ -1121,6 +1121,35 @@ dequeiter_next(dequeiterobject *it)
return item; return item;
} }
static PyObject *
dequeiter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
Py_ssize_t i, index=0;
PyObject *deque;
dequeiterobject *it;
if (!PyArg_ParseTuple(args, "O!|n", &deque_type, &deque, &index))
return NULL;
assert(type == &dequeiter_type);
it = (dequeiterobject*)deque_iter((dequeobject *)deque);
if (!it)
return NULL;
/* consume items from the queue */
for(i=0; i<index; i++) {
PyObject *item = dequeiter_next(it);
if (item) {
Py_DECREF(item);
} else {
if (it->counter) {
Py_DECREF(it);
return NULL;
} else
break;
}
}
return (PyObject*)it;
}
static PyObject * static PyObject *
dequeiter_len(dequeiterobject *it) dequeiter_len(dequeiterobject *it)
{ {
...@@ -1129,14 +1158,21 @@ dequeiter_len(dequeiterobject *it) ...@@ -1129,14 +1158,21 @@ dequeiter_len(dequeiterobject *it)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *
dequeiter_reduce(dequeiterobject *it)
{
return Py_BuildValue("O(On)", Py_TYPE(it), it->deque, it->deque->len - it->counter);
}
static PyMethodDef dequeiter_methods[] = { static PyMethodDef dequeiter_methods[] = {
{"__length_hint__", (PyCFunction)dequeiter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)dequeiter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)dequeiter_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
static PyTypeObject dequeiter_type = { static PyTypeObject dequeiter_type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"deque_iterator", /* tp_name */ "_collections._deque_iterator", /* tp_name */
sizeof(dequeiterobject), /* tp_basicsize */ sizeof(dequeiterobject), /* tp_basicsize */
0, /* tp_itemsize */ 0, /* tp_itemsize */
/* methods */ /* methods */
...@@ -1164,6 +1200,16 @@ static PyTypeObject dequeiter_type = { ...@@ -1164,6 +1200,16 @@ static PyTypeObject dequeiter_type = {
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)dequeiter_next, /* tp_iternext */ (iternextfunc)dequeiter_next, /* tp_iternext */
dequeiter_methods, /* tp_methods */ dequeiter_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
dequeiter_new, /* tp_new */
0, 0,
}; };
...@@ -1217,9 +1263,38 @@ dequereviter_next(dequeiterobject *it) ...@@ -1217,9 +1263,38 @@ dequereviter_next(dequeiterobject *it)
return item; return item;
} }
static PyObject *
dequereviter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
Py_ssize_t i, index=0;
PyObject *deque;
dequeiterobject *it;
if (!PyArg_ParseTuple(args, "O!|n", &deque_type, &deque, &index))
return NULL;
assert(type == &dequereviter_type);
it = (dequeiterobject*)deque_reviter((dequeobject *)deque);
if (!it)
return NULL;
/* consume items from the queue */
for(i=0; i<index; i++) {
PyObject *item = dequereviter_next(it);
if (item) {
Py_DECREF(item);
} else {
if (it->counter) {
Py_DECREF(it);
return NULL;
} else
break;
}
}
return (PyObject*)it;
}
static PyTypeObject dequereviter_type = { static PyTypeObject dequereviter_type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"deque_reverse_iterator", /* tp_name */ "_collections._deque_reverse_iterator", /* tp_name */
sizeof(dequeiterobject), /* tp_basicsize */ sizeof(dequeiterobject), /* tp_basicsize */
0, /* tp_itemsize */ 0, /* tp_itemsize */
/* methods */ /* methods */
...@@ -1247,6 +1322,16 @@ static PyTypeObject dequereviter_type = { ...@@ -1247,6 +1322,16 @@ static PyTypeObject dequereviter_type = {
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)dequereviter_next, /* tp_iternext */ (iternextfunc)dequereviter_next, /* tp_iternext */
dequeiter_methods, /* tp_methods */ dequeiter_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
dequereviter_new, /* tp_new */
0, 0,
}; };
...@@ -1653,9 +1738,13 @@ PyInit__collections(void) ...@@ -1653,9 +1738,13 @@ PyInit__collections(void)
if (PyType_Ready(&dequeiter_type) < 0) if (PyType_Ready(&dequeiter_type) < 0)
return NULL; return NULL;
Py_INCREF(&dequeiter_type);
PyModule_AddObject(m, "_deque_iterator", (PyObject *)&dequeiter_type);
if (PyType_Ready(&dequereviter_type) < 0) if (PyType_Ready(&dequereviter_type) < 0)
return NULL; return NULL;
Py_INCREF(&dequereviter_type);
PyModule_AddObject(m, "_deque_reverse_iterator", (PyObject *)&dequereviter_type);
return m; return m;
} }
...@@ -2753,6 +2753,34 @@ arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg) ...@@ -2753,6 +2753,34 @@ arrayiter_traverse(arrayiterobject *it, visitproc visit, void *arg)
return 0; return 0;
} }
static PyObject *
arrayiter_reduce(arrayiterobject *it)
{
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("iter"),
it->ao, it->index);
}
static PyObject *
arrayiter_setstate(arrayiterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (index < 0)
index = 0;
it->index = index;
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef arrayiter_methods[] = {
{"__reduce__", (PyCFunction)arrayiter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)arrayiter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */
};
static PyTypeObject PyArrayIter_Type = { static PyTypeObject PyArrayIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
"arrayiterator", /* tp_name */ "arrayiterator", /* tp_name */
...@@ -2782,7 +2810,7 @@ static PyTypeObject PyArrayIter_Type = { ...@@ -2782,7 +2810,7 @@ static PyTypeObject PyArrayIter_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)arrayiter_next, /* tp_iternext */ (iternextfunc)arrayiter_next, /* tp_iternext */
0, /* tp_methods */ arrayiter_methods, /* tp_methods */
}; };
......
This diff is collapsed.
...@@ -3003,7 +3003,7 @@ bytearrayiter_next(bytesiterobject *it) ...@@ -3003,7 +3003,7 @@ bytearrayiter_next(bytesiterobject *it)
} }
static PyObject * static PyObject *
bytesarrayiter_length_hint(bytesiterobject *it) bytearrayiter_length_hint(bytesiterobject *it)
{ {
Py_ssize_t len = 0; Py_ssize_t len = 0;
if (it->it_seq) if (it->it_seq)
...@@ -3014,9 +3014,41 @@ bytesarrayiter_length_hint(bytesiterobject *it) ...@@ -3014,9 +3014,41 @@ bytesarrayiter_length_hint(bytesiterobject *it)
PyDoc_STRVAR(length_hint_doc, PyDoc_STRVAR(length_hint_doc,
"Private method returning an estimate of len(list(it))."); "Private method returning an estimate of len(list(it)).");
static PyObject *
bytearrayiter_reduce(bytesiterobject *it)
{
if (it->it_seq != NULL) {
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
} else {
PyObject *u = PyUnicode_FromUnicode(NULL, 0);
if (u == NULL)
return NULL;
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), u);
}
}
static PyObject *
bytearrayiter_setstate(bytesiterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (index < 0)
index = 0;
it->it_index = index;
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef bytearrayiter_methods[] = { static PyMethodDef bytearrayiter_methods[] = {
{"__length_hint__", (PyCFunction)bytesarrayiter_length_hint, METH_NOARGS, {"__length_hint__", (PyCFunction)bytearrayiter_length_hint, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)bytearrayiter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)bytearrayiter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -3074,9 +3074,43 @@ striter_len(striterobject *it) ...@@ -3074,9 +3074,43 @@ striter_len(striterobject *it)
PyDoc_STRVAR(length_hint_doc, PyDoc_STRVAR(length_hint_doc,
"Private method returning an estimate of len(list(it))."); "Private method returning an estimate of len(list(it)).");
static PyObject *
striter_reduce(striterobject *it)
{
if (it->it_seq != NULL) {
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
} else {
PyObject *u = PyUnicode_FromUnicode(NULL, 0);
if (u == NULL)
return NULL;
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), u);
}
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyObject *
striter_setstate(striterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (index < 0)
index = 0;
it->it_index = index;
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef striter_methods[] = { static PyMethodDef striter_methods[] = {
{"__length_hint__", (PyCFunction)striter_len, METH_NOARGS, {"__length_hint__", (PyCFunction)striter_len, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)striter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)striter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -2323,9 +2323,16 @@ dictiter_len(dictiterobject *di) ...@@ -2323,9 +2323,16 @@ dictiter_len(dictiterobject *di)
PyDoc_STRVAR(length_hint_doc, PyDoc_STRVAR(length_hint_doc,
"Private method returning an estimate of len(list(it))."); "Private method returning an estimate of len(list(it)).");
static PyObject *
dictiter_reduce(dictiterobject *di);
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyMethodDef dictiter_methods[] = { static PyMethodDef dictiter_methods[] = {
{"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, {"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)dictiter_reduce, METH_NOARGS,
reduce_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -2560,6 +2567,52 @@ PyTypeObject PyDictIterItem_Type = { ...@@ -2560,6 +2567,52 @@ PyTypeObject PyDictIterItem_Type = {
}; };
static PyObject *
dictiter_reduce(dictiterobject *di)
{
PyObject *list;
dictiterobject tmp;
list = PyList_New(0);
if (!list)
return NULL;
/* copy the itertor state */
tmp = *di;
Py_XINCREF(tmp.di_dict);
/* iterate the temporary into a list */
for(;;) {
PyObject *element = 0;
if (Py_TYPE(di) == &PyDictIterItem_Type)
element = dictiter_iternextitem(&tmp);
else if (Py_TYPE(di) == &PyDictIterKey_Type)
element = dictiter_iternextkey(&tmp);
else if (Py_TYPE(di) == &PyDictIterValue_Type)
element = dictiter_iternextvalue(&tmp);
else
assert(0);
if (element) {
if (PyList_Append(list, element)) {
Py_DECREF(element);
Py_DECREF(list);
Py_XDECREF(tmp.di_dict);
return NULL;
}
Py_DECREF(element);
} else
break;
}
Py_XDECREF(tmp.di_dict);
/* check for error */
if (tmp.di_dict != NULL) {
/* we have an error */
Py_DECREF(list);
return NULL;
}
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), list);
}
/***********************************************/ /***********************************************/
/* View objects for keys(), items(), values(). */ /* View objects for keys(), items(), values(). */
/***********************************************/ /***********************************************/
......
...@@ -158,6 +158,22 @@ enum_next(enumobject *en) ...@@ -158,6 +158,22 @@ enum_next(enumobject *en)
return result; return result;
} }
static PyObject *
enum_reduce(enumobject *en)
{
if (en->en_longindex != NULL)
return Py_BuildValue("O(OO)", Py_TYPE(en), en->en_sit, en->en_longindex);
else
return Py_BuildValue("O(On)", Py_TYPE(en), en->en_sit, en->en_index);
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyMethodDef enum_methods[] = {
{"__reduce__", (PyCFunction)enum_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
PyDoc_STRVAR(enum_doc, PyDoc_STRVAR(enum_doc,
"enumerate(iterable[, start]) -> iterator for index, value of iterable\n" "enumerate(iterable[, start]) -> iterator for index, value of iterable\n"
"\n" "\n"
...@@ -197,7 +213,7 @@ PyTypeObject PyEnum_Type = { ...@@ -197,7 +213,7 @@ PyTypeObject PyEnum_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)enum_next, /* tp_iternext */ (iternextfunc)enum_next, /* tp_iternext */
0, /* tp_methods */ enum_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
0, /* tp_getset */ 0, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
...@@ -319,8 +335,40 @@ reversed_len(reversedobject *ro) ...@@ -319,8 +335,40 @@ reversed_len(reversedobject *ro)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *
reversed_reduce(reversedobject *ro)
{
if (ro->seq)
return Py_BuildValue("O(O)n", Py_TYPE(ro), ro->seq, ro->index);
else
return Py_BuildValue("O(())", Py_TYPE(ro));
}
static PyObject *
reversed_setstate(reversedobject *ro, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (ro->seq != 0) {
Py_ssize_t n = PySequence_Size(ro->seq);
if (n < 0)
return NULL;
if (index < -1)
index = -1;
else if (index > n-1)
index = n-1;
ro->index = index;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef reversediter_methods[] = { static PyMethodDef reversediter_methods[] = {
{"__length_hint__", (PyCFunction)reversed_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)reversed_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)reversed_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)reversed_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -2,6 +2,19 @@ ...@@ -2,6 +2,19 @@
#include "Python.h" #include "Python.h"
/* Convenience function to get builtins.iter or builtins.reversed */
PyObject *
_PyIter_GetBuiltin(const char *iter)
{
PyObject *mod, *attr;
mod = PyImport_ImportModule("builtins");
if (mod == NULL)
return NULL;
attr = PyObject_GetAttrString(mod, iter);
Py_DECREF(mod);
return attr;
}
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
long it_index; long it_index;
...@@ -88,8 +101,38 @@ iter_len(seqiterobject *it) ...@@ -88,8 +101,38 @@ iter_len(seqiterobject *it)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *
iter_reduce(seqiterobject *it)
{
if (it->it_seq != NULL)
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
else
return Py_BuildValue("N(())", _PyIter_GetBuiltin("iter"));
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyObject *
iter_setstate(seqiterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (it->it_seq != NULL) {
if (index < 0)
index = 0;
it->it_index = index;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef seqiter_methods[] = { static PyMethodDef seqiter_methods[] = {
{"__length_hint__", (PyCFunction)iter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)iter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)iter_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)iter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -195,6 +238,21 @@ calliter_iternext(calliterobject *it) ...@@ -195,6 +238,21 @@ calliter_iternext(calliterobject *it)
return NULL; return NULL;
} }
static PyObject *
calliter_reduce(calliterobject *it)
{
if (it->it_callable != NULL && it->it_sentinel != NULL)
return Py_BuildValue("N(OO)", _PyIter_GetBuiltin("iter"),
it->it_callable, it->it_sentinel);
else
return Py_BuildValue("N(())", _PyIter_GetBuiltin("iter"));
}
static PyMethodDef calliter_methods[] = {
{"__reduce__", (PyCFunction)calliter_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
PyTypeObject PyCallIter_Type = { PyTypeObject PyCallIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0) PyVarObject_HEAD_INIT(&PyType_Type, 0)
"callable_iterator", /* tp_name */ "callable_iterator", /* tp_name */
...@@ -224,7 +282,7 @@ PyTypeObject PyCallIter_Type = { ...@@ -224,7 +282,7 @@ PyTypeObject PyCallIter_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)calliter_iternext, /* tp_iternext */ (iternextfunc)calliter_iternext, /* tp_iternext */
0, /* tp_methods */ calliter_methods, /* tp_methods */
}; };
...@@ -2660,11 +2660,18 @@ static void listiter_dealloc(listiterobject *); ...@@ -2660,11 +2660,18 @@ static void listiter_dealloc(listiterobject *);
static int listiter_traverse(listiterobject *, visitproc, void *); static int listiter_traverse(listiterobject *, visitproc, void *);
static PyObject *listiter_next(listiterobject *); static PyObject *listiter_next(listiterobject *);
static PyObject *listiter_len(listiterobject *); static PyObject *listiter_len(listiterobject *);
static PyObject *listiter_reduce_general(void *_it, int forward);
static PyObject *listiter_reduce(listiterobject *);
static PyObject *listiter_setstate(listiterobject *, PyObject *state);
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef listiter_methods[] = { static PyMethodDef listiter_methods[] = {
{"__length_hint__", (PyCFunction)listiter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)listiter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)listiter_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)listiter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -2771,6 +2778,27 @@ listiter_len(listiterobject *it) ...@@ -2771,6 +2778,27 @@ listiter_len(listiterobject *it)
} }
return PyLong_FromLong(0); return PyLong_FromLong(0);
} }
static PyObject *
listiter_reduce(listiterobject *it)
{
return listiter_reduce_general(it, 1);
}
static PyObject *
listiter_setstate(listiterobject *it, PyObject *state)
{
long index = PyLong_AsLong(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (it->it_seq != NULL) {
if (index < 0)
index = 0;
it->it_index = index;
}
Py_RETURN_NONE;
}
/*********************** List Reverse Iterator **************************/ /*********************** List Reverse Iterator **************************/
typedef struct { typedef struct {
...@@ -2784,9 +2812,13 @@ static void listreviter_dealloc(listreviterobject *); ...@@ -2784,9 +2812,13 @@ static void listreviter_dealloc(listreviterobject *);
static int listreviter_traverse(listreviterobject *, visitproc, void *); static int listreviter_traverse(listreviterobject *, visitproc, void *);
static PyObject *listreviter_next(listreviterobject *); static PyObject *listreviter_next(listreviterobject *);
static PyObject *listreviter_len(listreviterobject *); static PyObject *listreviter_len(listreviterobject *);
static PyObject *listreviter_reduce(listreviterobject *);
static PyObject *listreviter_setstate(listreviterobject *, PyObject *);
static PyMethodDef listreviter_methods[] = { static PyMethodDef listreviter_methods[] = {
{"__length_hint__", (PyCFunction)listreviter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)listreviter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)listreviter_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)listreviter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -2883,3 +2915,51 @@ listreviter_len(listreviterobject *it) ...@@ -2883,3 +2915,51 @@ listreviter_len(listreviterobject *it)
len = 0; len = 0;
return PyLong_FromSsize_t(len); return PyLong_FromSsize_t(len);
} }
static PyObject *
listreviter_reduce(listreviterobject *it)
{
return listiter_reduce_general(it, 0);
}
static PyObject *
listreviter_setstate(listreviterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (it->it_seq != NULL) {
if (index < -1)
index = -1;
else if (index > PyList_GET_SIZE(it->it_seq) - 1)
index = PyList_GET_SIZE(it->it_seq) - 1;
it->it_index = index;
}
Py_RETURN_NONE;
}
/* common pickling support */
static PyObject *
listiter_reduce_general(void *_it, int forward)
{
PyObject *list;
/* the objects are not the same, index is of different types! */
if (forward) {
listiterobject *it = (listiterobject *)_it;
if (it->it_seq)
return Py_BuildValue("N(O)l", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
} else {
listreviterobject *it = (listreviterobject *)_it;
if (it->it_seq)
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("reversed"),
it->it_seq, it->it_index);
}
/* empty iterator, create an empty list */
list = PyList_New(0);
if (list == NULL)
return NULL;
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), list);
}
...@@ -964,9 +964,59 @@ rangeiter_len(rangeiterobject *r) ...@@ -964,9 +964,59 @@ rangeiter_len(rangeiterobject *r)
PyDoc_STRVAR(length_hint_doc, PyDoc_STRVAR(length_hint_doc,
"Private method returning an estimate of len(list(it))."); "Private method returning an estimate of len(list(it)).");
static PyObject *
rangeiter_reduce(rangeiterobject *r)
{
PyObject *start=NULL, *stop=NULL, *step=NULL;
PyObject *range;
/* create a range object for pickling */
start = PyLong_FromLong(r->start);
if (start == NULL)
goto err;
stop = PyLong_FromLong(r->start + r->len * r->step);
if (stop == NULL)
goto err;
step = PyLong_FromLong(r->step);
if (step == NULL)
goto err;
range = (PyObject*)make_range_object(&PyRange_Type,
start, stop, step);
if (range == NULL)
goto err;
/* return the result */
return Py_BuildValue("N(N)i", _PyIter_GetBuiltin("iter"), range, r->index);
err:
Py_XDECREF(start);
Py_XDECREF(stop);
Py_XDECREF(step);
return NULL;
}
static PyObject *
rangeiter_setstate(rangeiterobject *r, PyObject *state)
{
long index = PyLong_AsLong(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (index < 0 || index >= r->len) {
PyErr_SetString(PyExc_ValueError, "index out of range");
return NULL;
}
r->index = index;
Py_RETURN_NONE;
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef rangeiter_methods[] = { static PyMethodDef rangeiter_methods[] = {
{"__length_hint__", (PyCFunction)rangeiter_len, METH_NOARGS, {"__length_hint__", (PyCFunction)rangeiter_len, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)rangeiter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)rangeiter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -1095,9 +1145,51 @@ longrangeiter_len(longrangeiterobject *r, PyObject *no_args) ...@@ -1095,9 +1145,51 @@ longrangeiter_len(longrangeiterobject *r, PyObject *no_args)
return PyNumber_Subtract(r->len, r->index); return PyNumber_Subtract(r->len, r->index);
} }
static PyObject *
longrangeiter_reduce(longrangeiterobject *r)
{
PyObject *product, *stop=NULL;
PyObject *range;
/* create a range object for pickling. Must calculate the "stop" value */
product = PyNumber_Multiply(r->len, r->step);
if (product == NULL)
return NULL;
stop = PyNumber_Add(r->start, product);
Py_DECREF(product);
if (stop == NULL)
return NULL;
Py_INCREF(r->start);
Py_INCREF(r->step);
range = (PyObject*)make_range_object(&PyRange_Type,
r->start, stop, r->step);
if (range == NULL) {
Py_DECREF(r->start);
Py_DECREF(stop);
Py_DECREF(r->step);
return NULL;
}
/* return the result */
return Py_BuildValue("N(N)O", _PyIter_GetBuiltin("iter"), range, r->index);
}
static PyObject *
longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
{
Py_CLEAR(r->index);
r->index = state;
Py_INCREF(r->index);
Py_RETURN_NONE;
}
static PyMethodDef longrangeiter_methods[] = { static PyMethodDef longrangeiter_methods[] = {
{"__length_hint__", (PyCFunction)longrangeiter_len, METH_NOARGS, {"__length_hint__", (PyCFunction)longrangeiter_len, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)longrangeiter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)longrangeiter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -819,8 +819,51 @@ setiter_len(setiterobject *si) ...@@ -819,8 +819,51 @@ setiter_len(setiterobject *si)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *setiter_iternext(setiterobject *si);
static PyObject *
setiter_reduce(setiterobject *si)
{
PyObject *list;
setiterobject tmp;
list = PyList_New(0);
if (!list)
return NULL;
/* copy the itertor state */
tmp = *si;
Py_XINCREF(tmp.si_set);
/* iterate the temporary into a list */
for(;;) {
PyObject *element = setiter_iternext(&tmp);
if (element) {
if (PyList_Append(list, element)) {
Py_DECREF(element);
Py_DECREF(list);
Py_XDECREF(tmp.si_set);
return NULL;
}
Py_DECREF(element);
} else
break;
}
Py_XDECREF(tmp.si_set);
/* check for error */
if (tmp.si_set != NULL) {
/* we have an error */
Py_DECREF(list);
return NULL;
}
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), list);
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyMethodDef setiter_methods[] = { static PyMethodDef setiter_methods[] = {
{"__length_hint__", (PyCFunction)setiter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)setiter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)setiter_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
...@@ -1964,8 +2007,6 @@ done: ...@@ -1964,8 +2007,6 @@ done:
return result; return result;
} }
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyObject * static PyObject *
set_sizeof(PySetObject *so) set_sizeof(PySetObject *so)
{ {
......
...@@ -967,8 +967,39 @@ tupleiter_len(tupleiterobject *it) ...@@ -967,8 +967,39 @@ tupleiter_len(tupleiterobject *it)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *
tupleiter_reduce(tupleiterobject *it)
{
if (it->it_seq)
return Py_BuildValue("N(O)l", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
else
return Py_BuildValue("N(())", _PyIter_GetBuiltin("iter"));
}
static PyObject *
tupleiter_setstate(tupleiterobject *it, PyObject *state)
{
long index = PyLong_AsLong(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (it->it_seq != NULL) {
if (index < 0)
index = 0;
else if (it->it_seq != NULL && index > PyTuple_GET_SIZE(it->it_seq))
index = PyTuple_GET_SIZE(it->it_seq);
it->it_index = index;
}
Py_RETURN_NONE;
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef tupleiter_methods[] = { static PyMethodDef tupleiter_methods[] = {
{"__length_hint__", (PyCFunction)tupleiter_len, METH_NOARGS, length_hint_doc}, {"__length_hint__", (PyCFunction)tupleiter_len, METH_NOARGS, length_hint_doc},
{"__reduce__", (PyCFunction)tupleiter_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)tupleiter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -14382,9 +14382,43 @@ unicodeiter_len(unicodeiterobject *it) ...@@ -14382,9 +14382,43 @@ unicodeiter_len(unicodeiterobject *it)
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
static PyObject *
unicodeiter_reduce(unicodeiterobject *it)
{
if (it->it_seq != NULL) {
return Py_BuildValue("N(O)n", _PyIter_GetBuiltin("iter"),
it->it_seq, it->it_index);
} else {
PyObject *u = PyUnicode_FromUnicode(NULL, 0);
if (u == NULL)
return NULL;
return Py_BuildValue("N(N)", _PyIter_GetBuiltin("iter"), u);
}
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyObject *
unicodeiter_setstate(unicodeiterobject *it, PyObject *state)
{
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (index < 0)
index = 0;
it->it_index = index;
Py_RETURN_NONE;
}
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef unicodeiter_methods[] = { static PyMethodDef unicodeiter_methods[] = {
{"__length_hint__", (PyCFunction)unicodeiter_len, METH_NOARGS, {"__length_hint__", (PyCFunction)unicodeiter_len, METH_NOARGS,
length_hint_doc}, length_hint_doc},
{"__reduce__", (PyCFunction)unicodeiter_reduce, METH_NOARGS,
reduce_doc},
{"__setstate__", (PyCFunction)unicodeiter_setstate, METH_O,
setstate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
......
...@@ -438,6 +438,19 @@ filter_next(filterobject *lz) ...@@ -438,6 +438,19 @@ filter_next(filterobject *lz)
} }
} }
static PyObject *
filter_reduce(filterobject *lz)
{
return Py_BuildValue("O(OO)", Py_TYPE(lz), lz->func, lz->it);
}
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyMethodDef filter_methods[] = {
{"__reduce__", (PyCFunction)filter_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
PyDoc_STRVAR(filter_doc, PyDoc_STRVAR(filter_doc,
"filter(function or None, iterable) --> filter object\n\ "filter(function or None, iterable) --> filter object\n\
\n\ \n\
...@@ -474,7 +487,7 @@ PyTypeObject PyFilter_Type = { ...@@ -474,7 +487,7 @@ PyTypeObject PyFilter_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)filter_next, /* tp_iternext */ (iternextfunc)filter_next, /* tp_iternext */
0, /* tp_methods */ filter_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
0, /* tp_getset */ 0, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
...@@ -1054,6 +1067,31 @@ map_next(mapobject *lz) ...@@ -1054,6 +1067,31 @@ map_next(mapobject *lz)
return result; return result;
} }
static PyObject *
map_reduce(mapobject *lz)
{
Py_ssize_t numargs = PyTuple_GET_SIZE(lz->iters);
PyObject *args = PyTuple_New(numargs+1);
Py_ssize_t i;
if (args == NULL)
return NULL;
Py_INCREF(lz->func);
PyTuple_SET_ITEM(args, 0, lz->func);
for (i = 0; i<numargs; i++){
PyObject *it = PyTuple_GET_ITEM(lz->iters, i);
Py_INCREF(it);
PyTuple_SET_ITEM(args, i+1, it);
}
return Py_BuildValue("ON", Py_TYPE(lz), args);
}
static PyMethodDef map_methods[] = {
{"__reduce__", (PyCFunction)map_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
PyDoc_STRVAR(map_doc, PyDoc_STRVAR(map_doc,
"map(func, *iterables) --> map object\n\ "map(func, *iterables) --> map object\n\
\n\ \n\
...@@ -1090,7 +1128,7 @@ PyTypeObject PyMap_Type = { ...@@ -1090,7 +1128,7 @@ PyTypeObject PyMap_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)map_next, /* tp_iternext */ (iternextfunc)map_next, /* tp_iternext */
0, /* tp_methods */ map_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
0, /* tp_getset */ 0, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
...@@ -2238,6 +2276,18 @@ zip_next(zipobject *lz) ...@@ -2238,6 +2276,18 @@ zip_next(zipobject *lz)
return result; return result;
} }
static PyObject *
zip_reduce(zipobject *lz)
{
/* Just recreate the zip with the internal iterator tuple */
return Py_BuildValue("OO", Py_TYPE(lz), lz->ittuple);
}
static PyMethodDef zip_methods[] = {
{"__reduce__", (PyCFunction)zip_reduce, METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};
PyDoc_STRVAR(zip_doc, PyDoc_STRVAR(zip_doc,
"zip(iter1 [,iter2 [...]]) --> zip object\n\ "zip(iter1 [,iter2 [...]]) --> zip object\n\
\n\ \n\
...@@ -2276,7 +2326,7 @@ PyTypeObject PyZip_Type = { ...@@ -2276,7 +2326,7 @@ PyTypeObject PyZip_Type = {
0, /* tp_weaklistoffset */ 0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */ PyObject_SelfIter, /* tp_iter */
(iternextfunc)zip_next, /* tp_iternext */ (iternextfunc)zip_next, /* tp_iternext */
0, /* tp_methods */ zip_methods, /* tp_methods */
0, /* tp_members */ 0, /* tp_members */
0, /* tp_getset */ 0, /* tp_getset */
0, /* tp_base */ 0, /* tp_base */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment