blob: 41c9752e557fb36aef318e0a26bd069a4106eb49 [file] [log] [blame]
Raymond Hettinger7892b1c2004-04-12 18:10:01 +00001""" Test Iterator Length Transparency
2
3Some functions or methods which accept general iterable arguments have
4optional, more efficient code paths if they know how many items to expect.
5For instance, map(func, iterable), will pre-allocate the exact amount of
6space required whenever the iterable can report its length.
7
8The desired invariant is: len(it)==len(list(it)).
9
10A complication is that an iterable and iterator can be the same object. To
11maintain the invariant, an iterator needs to dynamically update its length.
Guido van Rossum805365e2007-05-07 22:24:25 +000012For instance, an iterable such as range(10) always reports its length as ten,
13but it=iter(range(10)) starts at ten, and then goes to nine after next(it).
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000014Having this capability means that map() can ignore the distinction between
15map(func, iterable) and map(func, iter(iterable)).
16
17When the iterable is immutable, the implementation can straight-forwardly
18report the original length minus the cumulative number of calls to next().
Guido van Rossum805365e2007-05-07 22:24:25 +000019This is the case for tuples, range objects, and itertools.repeat().
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000020
21Some containers become temporarily immutable during iteration. This includes
22dicts, sets, and collections.deque. Their implementation is equally simple
Ezio Melotti13925002011-03-16 11:05:33 +020023though they need to permanently set their length to zero whenever there is
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000024an attempt to iterate after a length mutation.
25
26The situation slightly more involved whenever an object allows length mutation
Ezio Melotti13925002011-03-16 11:05:33 +020027during iteration. Lists and sequence iterators are dynamically updatable.
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000028So, if a list is extended during iteration, the iterator will continue through
29the new items. If it shrinks to a point before the most recent iteration,
30then no further items are available and the length is reported at zero.
31
32Reversed objects can also be wrapped around mutable objects; however, any
33appends after the current position are ignored. Any other approach leads
34to confusion and possibly returning the same item more than once.
35
36The iterators not listed above, such as enumerate and the other itertools,
37are not length transparent because they have no way to distinguish between
38iterables that report static length and iterators whose length changes with
39each call (i.e. the difference between enumerate('abc') and
40enumerate(iter('abc')).
41
42"""
43
44import unittest
Raymond Hettinger6b27cda2005-09-24 21:23:05 +000045from itertools import repeat
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000046from collections import deque
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020047from operator import length_hint
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000048
49n = 10
50
Raymond Hettinger6b27cda2005-09-24 21:23:05 +000051
Ezio Melottid13c0082013-04-17 04:34:05 +030052class TestInvariantWithoutMutations:
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000053
54 def test_invariant(self):
Tim Peters27f88362004-07-08 04:22:35 +000055 it = self.it
Guido van Rossum805365e2007-05-07 22:24:25 +000056 for i in reversed(range(1, n+1)):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020057 self.assertEqual(length_hint(it), i)
Georg Brandla18af4e2007-04-21 15:47:16 +000058 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020059 self.assertEqual(length_hint(it), 0)
Georg Brandla18af4e2007-04-21 15:47:16 +000060 self.assertRaises(StopIteration, next, it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020061 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000062
63class TestTemporarilyImmutable(TestInvariantWithoutMutations):
64
65 def test_immutable_during_iteration(self):
66 # objects such as deques, sets, and dictionaries enforce
67 # length immutability during iteration
68
69 it = self.it
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020070 self.assertEqual(length_hint(it), n)
Georg Brandla18af4e2007-04-21 15:47:16 +000071 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020072 self.assertEqual(length_hint(it), n-1)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000073 self.mutate()
Georg Brandla18af4e2007-04-21 15:47:16 +000074 self.assertRaises(RuntimeError, next, it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020075 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000076
77## ------- Concrete Type Tests -------
78
Ezio Melottid13c0082013-04-17 04:34:05 +030079class TestRepeat(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000080
81 def setUp(self):
82 self.it = repeat(None, n)
83
Ezio Melottid13c0082013-04-17 04:34:05 +030084class TestXrange(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000085
86 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000087 self.it = iter(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000088
Ezio Melottid13c0082013-04-17 04:34:05 +030089class TestXrangeCustomReversed(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000090
91 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000092 self.it = reversed(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000093
Ezio Melottid13c0082013-04-17 04:34:05 +030094class TestTuple(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000095
96 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000097 self.it = iter(tuple(range(n)))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000098
99## ------- Types that should not be mutated during iteration -------
100
Ezio Melottid13c0082013-04-17 04:34:05 +0300101class TestDeque(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000102
103 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000104 d = deque(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000105 self.it = iter(d)
106 self.mutate = d.pop
107
Ezio Melottid13c0082013-04-17 04:34:05 +0300108class TestDequeReversed(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000109
110 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000111 d = deque(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000112 self.it = reversed(d)
113 self.mutate = d.pop
114
Ezio Melottid13c0082013-04-17 04:34:05 +0300115class TestDictKeys(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000116
117 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000118 d = dict.fromkeys(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000119 self.it = iter(d)
120 self.mutate = d.popitem
121
Ezio Melottid13c0082013-04-17 04:34:05 +0300122class TestDictItems(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000123
124 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000125 d = dict.fromkeys(range(n))
Brett Cannoneb6b0ee2007-02-22 04:45:13 +0000126 self.it = iter(d.items())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000127 self.mutate = d.popitem
128
Ezio Melottid13c0082013-04-17 04:34:05 +0300129class TestDictValues(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000130
131 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000132 d = dict.fromkeys(range(n))
Brett Cannoneb6b0ee2007-02-22 04:45:13 +0000133 self.it = iter(d.values())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000134 self.mutate = d.popitem
135
Ezio Melottid13c0082013-04-17 04:34:05 +0300136class TestSet(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000137
138 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000139 d = set(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000140 self.it = iter(d)
141 self.mutate = d.pop
142
143## ------- Types that can mutate during iteration -------
144
Ezio Melottid13c0082013-04-17 04:34:05 +0300145class TestList(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000146
147 def setUp(self):
148 self.it = iter(range(n))
149
150 def test_mutation(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000151 d = list(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000152 it = iter(d)
Georg Brandla18af4e2007-04-21 15:47:16 +0000153 next(it)
154 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200155 self.assertEqual(length_hint(it), n - 2)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000156 d.append(n)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200157 self.assertEqual(length_hint(it), n - 1) # grow with append
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000158 d[1:] = []
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200159 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000160 self.assertEqual(list(it), [])
Guido van Rossum805365e2007-05-07 22:24:25 +0000161 d.extend(range(20))
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200162 self.assertEqual(length_hint(it), 0)
163
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000164
Ezio Melottid13c0082013-04-17 04:34:05 +0300165class TestListReversed(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000166
167 def setUp(self):
168 self.it = reversed(range(n))
169
170 def test_mutation(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000171 d = list(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000172 it = reversed(d)
Georg Brandla18af4e2007-04-21 15:47:16 +0000173 next(it)
174 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200175 self.assertEqual(length_hint(it), n - 2)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000176 d.append(n)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200177 self.assertEqual(length_hint(it), n - 2) # ignore append
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000178 d[1:] = []
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200179 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000180 self.assertEqual(list(it), []) # confirm invariant
Guido van Rossum805365e2007-05-07 22:24:25 +0000181 d.extend(range(20))
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200182 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000183
Raymond Hettingere8364232009-02-02 22:55:09 +0000184## -- Check to make sure exceptions are not suppressed by __length_hint__()
185
186
187class BadLen(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200188 def __iter__(self):
189 return iter(range(10))
190
Raymond Hettingere8364232009-02-02 22:55:09 +0000191 def __len__(self):
192 raise RuntimeError('hello')
193
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200194
Raymond Hettingere8364232009-02-02 22:55:09 +0000195class BadLengthHint(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200196 def __iter__(self):
197 return iter(range(10))
198
Raymond Hettingere8364232009-02-02 22:55:09 +0000199 def __length_hint__(self):
200 raise RuntimeError('hello')
201
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200202
Raymond Hettinger5d654122009-02-03 02:12:10 +0000203class NoneLengthHint(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200204 def __iter__(self):
205 return iter(range(10))
206
Raymond Hettinger5d654122009-02-03 02:12:10 +0000207 def __length_hint__(self):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200208 return NotImplemented
209
Raymond Hettinger5d654122009-02-03 02:12:10 +0000210
Raymond Hettingere8364232009-02-02 22:55:09 +0000211class TestLengthHintExceptions(unittest.TestCase):
212
213 def test_issue1242657(self):
214 self.assertRaises(RuntimeError, list, BadLen())
215 self.assertRaises(RuntimeError, list, BadLengthHint())
216 self.assertRaises(RuntimeError, [].extend, BadLen())
217 self.assertRaises(RuntimeError, [].extend, BadLengthHint())
218 b = bytearray(range(10))
219 self.assertRaises(RuntimeError, b.extend, BadLen())
220 self.assertRaises(RuntimeError, b.extend, BadLengthHint())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000221
Raymond Hettinger5d654122009-02-03 02:12:10 +0000222 def test_invalid_hint(self):
223 # Make sure an invalid result doesn't muck-up the works
224 self.assertEqual(list(NoneLengthHint()), list(range(10)))
225
226
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000227if __name__ == "__main__":
Ezio Melottid13c0082013-04-17 04:34:05 +0300228 unittest.main()