blob: 152f5fc0cb63d6c2b1c120137a756103fb59efd1 [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
Benjamin Petersonee8712c2008-05-20 21:35:26 +000045from test import support
Raymond Hettinger6b27cda2005-09-24 21:23:05 +000046from itertools import repeat
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000047from collections import deque
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020048from operator import length_hint
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000049
50n = 10
51
Raymond Hettinger6b27cda2005-09-24 21:23:05 +000052
Ezio Melottid13c0082013-04-17 04:34:05 +030053class TestInvariantWithoutMutations:
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000054
55 def test_invariant(self):
Tim Peters27f88362004-07-08 04:22:35 +000056 it = self.it
Guido van Rossum805365e2007-05-07 22:24:25 +000057 for i in reversed(range(1, n+1)):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020058 self.assertEqual(length_hint(it), i)
Georg Brandla18af4e2007-04-21 15:47:16 +000059 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020060 self.assertEqual(length_hint(it), 0)
Georg Brandla18af4e2007-04-21 15:47:16 +000061 self.assertRaises(StopIteration, next, it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020062 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000063
64class TestTemporarilyImmutable(TestInvariantWithoutMutations):
65
66 def test_immutable_during_iteration(self):
67 # objects such as deques, sets, and dictionaries enforce
68 # length immutability during iteration
69
70 it = self.it
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020071 self.assertEqual(length_hint(it), n)
Georg Brandla18af4e2007-04-21 15:47:16 +000072 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020073 self.assertEqual(length_hint(it), n-1)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000074 self.mutate()
Georg Brandla18af4e2007-04-21 15:47:16 +000075 self.assertRaises(RuntimeError, next, it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +020076 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000077
78## ------- Concrete Type Tests -------
79
Ezio Melottid13c0082013-04-17 04:34:05 +030080class TestRepeat(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000081
82 def setUp(self):
83 self.it = repeat(None, n)
84
Ezio Melottid13c0082013-04-17 04:34:05 +030085class TestXrange(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000086
87 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000088 self.it = iter(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000089
Ezio Melottid13c0082013-04-17 04:34:05 +030090class TestXrangeCustomReversed(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000091
92 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000093 self.it = reversed(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000094
Ezio Melottid13c0082013-04-17 04:34:05 +030095class TestTuple(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000096
97 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +000098 self.it = iter(tuple(range(n)))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +000099
100## ------- Types that should not be mutated during iteration -------
101
Ezio Melottid13c0082013-04-17 04:34:05 +0300102class TestDeque(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000103
104 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000105 d = deque(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000106 self.it = iter(d)
107 self.mutate = d.pop
108
Ezio Melottid13c0082013-04-17 04:34:05 +0300109class TestDequeReversed(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000110
111 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000112 d = deque(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000113 self.it = reversed(d)
114 self.mutate = d.pop
115
Ezio Melottid13c0082013-04-17 04:34:05 +0300116class TestDictKeys(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000117
118 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000119 d = dict.fromkeys(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000120 self.it = iter(d)
121 self.mutate = d.popitem
122
Ezio Melottid13c0082013-04-17 04:34:05 +0300123class TestDictItems(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000124
125 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000126 d = dict.fromkeys(range(n))
Brett Cannoneb6b0ee2007-02-22 04:45:13 +0000127 self.it = iter(d.items())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000128 self.mutate = d.popitem
129
Ezio Melottid13c0082013-04-17 04:34:05 +0300130class TestDictValues(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000131
132 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000133 d = dict.fromkeys(range(n))
Brett Cannoneb6b0ee2007-02-22 04:45:13 +0000134 self.it = iter(d.values())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000135 self.mutate = d.popitem
136
Ezio Melottid13c0082013-04-17 04:34:05 +0300137class TestSet(TestTemporarilyImmutable, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000138
139 def setUp(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000140 d = set(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000141 self.it = iter(d)
142 self.mutate = d.pop
143
144## ------- Types that can mutate during iteration -------
145
Ezio Melottid13c0082013-04-17 04:34:05 +0300146class TestList(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000147
148 def setUp(self):
149 self.it = iter(range(n))
150
151 def test_mutation(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000152 d = list(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000153 it = iter(d)
Georg Brandla18af4e2007-04-21 15:47:16 +0000154 next(it)
155 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200156 self.assertEqual(length_hint(it), n - 2)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000157 d.append(n)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200158 self.assertEqual(length_hint(it), n - 1) # grow with append
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000159 d[1:] = []
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200160 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000161 self.assertEqual(list(it), [])
Guido van Rossum805365e2007-05-07 22:24:25 +0000162 d.extend(range(20))
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200163 self.assertEqual(length_hint(it), 0)
164
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000165
Ezio Melottid13c0082013-04-17 04:34:05 +0300166class TestListReversed(TestInvariantWithoutMutations, unittest.TestCase):
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000167
168 def setUp(self):
169 self.it = reversed(range(n))
170
171 def test_mutation(self):
Guido van Rossum805365e2007-05-07 22:24:25 +0000172 d = list(range(n))
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000173 it = reversed(d)
Georg Brandla18af4e2007-04-21 15:47:16 +0000174 next(it)
175 next(it)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200176 self.assertEqual(length_hint(it), n - 2)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000177 d.append(n)
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200178 self.assertEqual(length_hint(it), n - 2) # ignore append
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000179 d[1:] = []
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200180 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000181 self.assertEqual(list(it), []) # confirm invariant
Guido van Rossum805365e2007-05-07 22:24:25 +0000182 d.extend(range(20))
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200183 self.assertEqual(length_hint(it), 0)
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000184
Raymond Hettingere8364232009-02-02 22:55:09 +0000185## -- Check to make sure exceptions are not suppressed by __length_hint__()
186
187
188class BadLen(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200189 def __iter__(self):
190 return iter(range(10))
191
Raymond Hettingere8364232009-02-02 22:55:09 +0000192 def __len__(self):
193 raise RuntimeError('hello')
194
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200195
Raymond Hettingere8364232009-02-02 22:55:09 +0000196class BadLengthHint(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200197 def __iter__(self):
198 return iter(range(10))
199
Raymond Hettingere8364232009-02-02 22:55:09 +0000200 def __length_hint__(self):
201 raise RuntimeError('hello')
202
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200203
Raymond Hettinger5d654122009-02-03 02:12:10 +0000204class NoneLengthHint(object):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200205 def __iter__(self):
206 return iter(range(10))
207
Raymond Hettinger5d654122009-02-03 02:12:10 +0000208 def __length_hint__(self):
Armin Ronacheraa9a79d2012-10-06 14:03:24 +0200209 return NotImplemented
210
Raymond Hettinger5d654122009-02-03 02:12:10 +0000211
Raymond Hettingere8364232009-02-02 22:55:09 +0000212class TestLengthHintExceptions(unittest.TestCase):
213
214 def test_issue1242657(self):
215 self.assertRaises(RuntimeError, list, BadLen())
216 self.assertRaises(RuntimeError, list, BadLengthHint())
217 self.assertRaises(RuntimeError, [].extend, BadLen())
218 self.assertRaises(RuntimeError, [].extend, BadLengthHint())
219 b = bytearray(range(10))
220 self.assertRaises(RuntimeError, b.extend, BadLen())
221 self.assertRaises(RuntimeError, b.extend, BadLengthHint())
Raymond Hettinger7892b1c2004-04-12 18:10:01 +0000222
Raymond Hettinger5d654122009-02-03 02:12:10 +0000223 def test_invalid_hint(self):
224 # Make sure an invalid result doesn't muck-up the works
225 self.assertEqual(list(NoneLengthHint()), list(range(10)))
226
227
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000228if __name__ == "__main__":
Ezio Melottid13c0082013-04-17 04:34:05 +0300229 unittest.main()