ยปCore Development>Code coverage>Lib/test/test_iterlen.py

Python code coverage for Lib/test/test_iterlen.py

#countcontent
1n/a""" Test Iterator Length Transparency
2n/a
3n/aSome functions or methods which accept general iterable arguments have
4n/aoptional, more efficient code paths if they know how many items to expect.
5n/aFor instance, map(func, iterable), will pre-allocate the exact amount of
6n/aspace required whenever the iterable can report its length.
7n/a
8n/aThe desired invariant is: len(it)==len(list(it)).
9n/a
10n/aA complication is that an iterable and iterator can be the same object. To
11n/amaintain the invariant, an iterator needs to dynamically update its length.
12n/aFor instance, an iterable such as range(10) always reports its length as ten,
13n/abut it=iter(range(10)) starts at ten, and then goes to nine after next(it).
14n/aHaving this capability means that map() can ignore the distinction between
15n/amap(func, iterable) and map(func, iter(iterable)).
16n/a
17n/aWhen the iterable is immutable, the implementation can straight-forwardly
18n/areport the original length minus the cumulative number of calls to next().
19n/aThis is the case for tuples, range objects, and itertools.repeat().
20n/a
21n/aSome containers become temporarily immutable during iteration. This includes
22n/adicts, sets, and collections.deque. Their implementation is equally simple
23n/athough they need to permanently set their length to zero whenever there is
24n/aan attempt to iterate after a length mutation.
25n/a
26n/aThe situation slightly more involved whenever an object allows length mutation
27n/aduring iteration. Lists and sequence iterators are dynamically updatable.
28n/aSo, if a list is extended during iteration, the iterator will continue through
29n/athe new items. If it shrinks to a point before the most recent iteration,
30n/athen no further items are available and the length is reported at zero.
31n/a
32n/aReversed objects can also be wrapped around mutable objects; however, any
33n/aappends after the current position are ignored. Any other approach leads
34n/ato confusion and possibly returning the same item more than once.
35n/a
36n/aThe iterators not listed above, such as enumerate and the other itertools,
37n/aare not length transparent because they have no way to distinguish between
38n/aiterables that report static length and iterators whose length changes with
39n/aeach call (i.e. the difference between enumerate('abc') and
40n/aenumerate(iter('abc')).
41n/a
42n/a"""
43n/a
44n/aimport unittest
45n/afrom itertools import repeat
46n/afrom collections import deque
47n/afrom operator import length_hint
48n/a
49n/an = 10
50n/a
51n/a
52n/aclass TestInvariantWithoutMutations:
53n/a
54n/a def test_invariant(self):
55n/a it = self.it
56n/a for i in reversed(range(1, n+1)):
57n/a self.assertEqual(length_hint(it), i)
58n/a next(it)
59n/a self.assertEqual(length_hint(it), 0)
60n/a self.assertRaises(StopIteration, next, it)
61n/a self.assertEqual(length_hint(it), 0)
62n/a
63n/aclass TestTemporarilyImmutable(TestInvariantWithoutMutations):
64n/a
65n/a def test_immutable_during_iteration(self):
66n/a # objects such as deques, sets, and dictionaries enforce
67n/a # length immutability during iteration
68n/a
69n/a it = self.it
70n/a self.assertEqual(length_hint(it), n)
71n/a next(it)
72n/a self.assertEqual(length_hint(it), n-1)
73n/a self.mutate()
74n/a self.assertRaises(RuntimeError, next, it)
75n/a self.assertEqual(length_hint(it), 0)
76n/a
77n/a## ------- Concrete Type Tests -------
78n/a
79n/aclass TestRepeat(TestInvariantWithoutMutations, unittest.TestCase):
80n/a
81n/a def setUp(self):
82n/a self.it = repeat(None, n)
83n/a
84n/aclass TestXrange(TestInvariantWithoutMutations, unittest.TestCase):
85n/a
86n/a def setUp(self):
87n/a self.it = iter(range(n))
88n/a
89n/aclass TestXrangeCustomReversed(TestInvariantWithoutMutations, unittest.TestCase):
90n/a
91n/a def setUp(self):
92n/a self.it = reversed(range(n))
93n/a
94n/aclass TestTuple(TestInvariantWithoutMutations, unittest.TestCase):
95n/a
96n/a def setUp(self):
97n/a self.it = iter(tuple(range(n)))
98n/a
99n/a## ------- Types that should not be mutated during iteration -------
100n/a
101n/aclass TestDeque(TestTemporarilyImmutable, unittest.TestCase):
102n/a
103n/a def setUp(self):
104n/a d = deque(range(n))
105n/a self.it = iter(d)
106n/a self.mutate = d.pop
107n/a
108n/aclass TestDequeReversed(TestTemporarilyImmutable, unittest.TestCase):
109n/a
110n/a def setUp(self):
111n/a d = deque(range(n))
112n/a self.it = reversed(d)
113n/a self.mutate = d.pop
114n/a
115n/aclass TestDictKeys(TestTemporarilyImmutable, unittest.TestCase):
116n/a
117n/a def setUp(self):
118n/a d = dict.fromkeys(range(n))
119n/a self.it = iter(d)
120n/a self.mutate = d.popitem
121n/a
122n/aclass TestDictItems(TestTemporarilyImmutable, unittest.TestCase):
123n/a
124n/a def setUp(self):
125n/a d = dict.fromkeys(range(n))
126n/a self.it = iter(d.items())
127n/a self.mutate = d.popitem
128n/a
129n/aclass TestDictValues(TestTemporarilyImmutable, unittest.TestCase):
130n/a
131n/a def setUp(self):
132n/a d = dict.fromkeys(range(n))
133n/a self.it = iter(d.values())
134n/a self.mutate = d.popitem
135n/a
136n/aclass TestSet(TestTemporarilyImmutable, unittest.TestCase):
137n/a
138n/a def setUp(self):
139n/a d = set(range(n))
140n/a self.it = iter(d)
141n/a self.mutate = d.pop
142n/a
143n/a## ------- Types that can mutate during iteration -------
144n/a
145n/aclass TestList(TestInvariantWithoutMutations, unittest.TestCase):
146n/a
147n/a def setUp(self):
148n/a self.it = iter(range(n))
149n/a
150n/a def test_mutation(self):
151n/a d = list(range(n))
152n/a it = iter(d)
153n/a next(it)
154n/a next(it)
155n/a self.assertEqual(length_hint(it), n - 2)
156n/a d.append(n)
157n/a self.assertEqual(length_hint(it), n - 1) # grow with append
158n/a d[1:] = []
159n/a self.assertEqual(length_hint(it), 0)
160n/a self.assertEqual(list(it), [])
161n/a d.extend(range(20))
162n/a self.assertEqual(length_hint(it), 0)
163n/a
164n/a
165n/aclass TestListReversed(TestInvariantWithoutMutations, unittest.TestCase):
166n/a
167n/a def setUp(self):
168n/a self.it = reversed(range(n))
169n/a
170n/a def test_mutation(self):
171n/a d = list(range(n))
172n/a it = reversed(d)
173n/a next(it)
174n/a next(it)
175n/a self.assertEqual(length_hint(it), n - 2)
176n/a d.append(n)
177n/a self.assertEqual(length_hint(it), n - 2) # ignore append
178n/a d[1:] = []
179n/a self.assertEqual(length_hint(it), 0)
180n/a self.assertEqual(list(it), []) # confirm invariant
181n/a d.extend(range(20))
182n/a self.assertEqual(length_hint(it), 0)
183n/a
184n/a## -- Check to make sure exceptions are not suppressed by __length_hint__()
185n/a
186n/a
187n/aclass BadLen(object):
188n/a def __iter__(self):
189n/a return iter(range(10))
190n/a
191n/a def __len__(self):
192n/a raise RuntimeError('hello')
193n/a
194n/a
195n/aclass BadLengthHint(object):
196n/a def __iter__(self):
197n/a return iter(range(10))
198n/a
199n/a def __length_hint__(self):
200n/a raise RuntimeError('hello')
201n/a
202n/a
203n/aclass NoneLengthHint(object):
204n/a def __iter__(self):
205n/a return iter(range(10))
206n/a
207n/a def __length_hint__(self):
208n/a return NotImplemented
209n/a
210n/a
211n/aclass TestLengthHintExceptions(unittest.TestCase):
212n/a
213n/a def test_issue1242657(self):
214n/a self.assertRaises(RuntimeError, list, BadLen())
215n/a self.assertRaises(RuntimeError, list, BadLengthHint())
216n/a self.assertRaises(RuntimeError, [].extend, BadLen())
217n/a self.assertRaises(RuntimeError, [].extend, BadLengthHint())
218n/a b = bytearray(range(10))
219n/a self.assertRaises(RuntimeError, b.extend, BadLen())
220n/a self.assertRaises(RuntimeError, b.extend, BadLengthHint())
221n/a
222n/a def test_invalid_hint(self):
223n/a # Make sure an invalid result doesn't muck-up the works
224n/a self.assertEqual(list(NoneLengthHint()), list(range(10)))
225n/a
226n/a
227n/aif __name__ == "__main__":
228n/a unittest.main()