| """ Test Iterator Length Transparency | 
 |  | 
 | Some functions or methods which accept general iterable arguments have | 
 | optional, more efficient code paths if they know how many items to expect. | 
 | For instance, map(func, iterable), will pre-allocate the exact amount of | 
 | space required whenever the iterable can report its length. | 
 |  | 
 | The desired invariant is:  len(it)==len(list(it)). | 
 |  | 
 | A complication is that an iterable and iterator can be the same object. To | 
 | maintain the invariant, an iterator needs to dynamically update its length. | 
 | For instance, an iterable such as range(10) always reports its length as ten, | 
 | but it=iter(range(10)) starts at ten, and then goes to nine after next(it). | 
 | Having this capability means that map() can ignore the distinction between | 
 | map(func, iterable) and map(func, iter(iterable)). | 
 |  | 
 | When the iterable is immutable, the implementation can straight-forwardly | 
 | report the original length minus the cumulative number of calls to next(). | 
 | This is the case for tuples, range objects, and itertools.repeat(). | 
 |  | 
 | Some containers become temporarily immutable during iteration.  This includes | 
 | dicts, sets, and collections.deque.  Their implementation is equally simple | 
 | though they need to permanently set their length to zero whenever there is | 
 | an attempt to iterate after a length mutation. | 
 |  | 
 | The situation slightly more involved whenever an object allows length mutation | 
 | during iteration.  Lists and sequence iterators are dynamically updatable. | 
 | So, if a list is extended during iteration, the iterator will continue through | 
 | the new items.  If it shrinks to a point before the most recent iteration, | 
 | then no further items are available and the length is reported at zero. | 
 |  | 
 | Reversed objects can also be wrapped around mutable objects; however, any | 
 | appends after the current position are ignored.  Any other approach leads | 
 | to confusion and possibly returning the same item more than once. | 
 |  | 
 | The iterators not listed above, such as enumerate and the other itertools, | 
 | are not length transparent because they have no way to distinguish between | 
 | iterables that report static length and iterators whose length changes with | 
 | each call (i.e. the difference between enumerate('abc') and | 
 | enumerate(iter('abc')). | 
 |  | 
 | """ | 
 |  | 
 | import unittest | 
 | from test import support | 
 | from itertools import repeat | 
 | from collections import deque | 
 | from builtins import len as _len | 
 |  | 
 | n = 10 | 
 |  | 
 | def len(obj): | 
 |     try: | 
 |         return _len(obj) | 
 |     except TypeError: | 
 |         try: | 
 |             # note: this is an internal undocumented API, | 
 |             # don't rely on it in your own programs | 
 |             return obj.__length_hint__() | 
 |         except AttributeError: | 
 |             raise TypeError | 
 |  | 
 | class TestInvariantWithoutMutations: | 
 |  | 
 |     def test_invariant(self): | 
 |         it = self.it | 
 |         for i in reversed(range(1, n+1)): | 
 |             self.assertEqual(len(it), i) | 
 |             next(it) | 
 |         self.assertEqual(len(it), 0) | 
 |         self.assertRaises(StopIteration, next, it) | 
 |         self.assertEqual(len(it), 0) | 
 |  | 
 | class TestTemporarilyImmutable(TestInvariantWithoutMutations): | 
 |  | 
 |     def test_immutable_during_iteration(self): | 
 |         # objects such as deques, sets, and dictionaries enforce | 
 |         # length immutability  during iteration | 
 |  | 
 |         it = self.it | 
 |         self.assertEqual(len(it), n) | 
 |         next(it) | 
 |         self.assertEqual(len(it), n-1) | 
 |         self.mutate() | 
 |         self.assertRaises(RuntimeError, next, it) | 
 |         self.assertEqual(len(it), 0) | 
 |  | 
 | ## ------- Concrete Type Tests ------- | 
 |  | 
 | class TestRepeat(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = repeat(None, n) | 
 |  | 
 |     def test_no_len_for_infinite_repeat(self): | 
 |         # The repeat() object can also be infinite | 
 |         self.assertRaises(TypeError, len, repeat(None)) | 
 |  | 
 | class TestXrange(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = iter(range(n)) | 
 |  | 
 | class TestXrangeCustomReversed(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = reversed(range(n)) | 
 |  | 
 | class TestTuple(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = iter(tuple(range(n))) | 
 |  | 
 | ## ------- Types that should not be mutated during iteration ------- | 
 |  | 
 | class TestDeque(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = deque(range(n)) | 
 |         self.it = iter(d) | 
 |         self.mutate = d.pop | 
 |  | 
 | class TestDequeReversed(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = deque(range(n)) | 
 |         self.it = reversed(d) | 
 |         self.mutate = d.pop | 
 |  | 
 | class TestDictKeys(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = dict.fromkeys(range(n)) | 
 |         self.it = iter(d) | 
 |         self.mutate = d.popitem | 
 |  | 
 | class TestDictItems(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = dict.fromkeys(range(n)) | 
 |         self.it = iter(d.items()) | 
 |         self.mutate = d.popitem | 
 |  | 
 | class TestDictValues(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = dict.fromkeys(range(n)) | 
 |         self.it = iter(d.values()) | 
 |         self.mutate = d.popitem | 
 |  | 
 | class TestSet(TestTemporarilyImmutable, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         d = set(range(n)) | 
 |         self.it = iter(d) | 
 |         self.mutate = d.pop | 
 |  | 
 | ## ------- Types that can mutate during iteration ------- | 
 |  | 
 | class TestList(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = iter(range(n)) | 
 |  | 
 |     def test_mutation(self): | 
 |         d = list(range(n)) | 
 |         it = iter(d) | 
 |         next(it) | 
 |         next(it) | 
 |         self.assertEqual(len(it), n-2) | 
 |         d.append(n) | 
 |         self.assertEqual(len(it), n-1)  # grow with append | 
 |         d[1:] = [] | 
 |         self.assertEqual(len(it), 0) | 
 |         self.assertEqual(list(it), []) | 
 |         d.extend(range(20)) | 
 |         self.assertEqual(len(it), 0) | 
 |  | 
 | class TestListReversed(TestInvariantWithoutMutations, unittest.TestCase): | 
 |  | 
 |     def setUp(self): | 
 |         self.it = reversed(range(n)) | 
 |  | 
 |     def test_mutation(self): | 
 |         d = list(range(n)) | 
 |         it = reversed(d) | 
 |         next(it) | 
 |         next(it) | 
 |         self.assertEqual(len(it), n-2) | 
 |         d.append(n) | 
 |         self.assertEqual(len(it), n-2)  # ignore append | 
 |         d[1:] = [] | 
 |         self.assertEqual(len(it), 0) | 
 |         self.assertEqual(list(it), [])  # confirm invariant | 
 |         d.extend(range(20)) | 
 |         self.assertEqual(len(it), 0) | 
 |  | 
 | ## -- Check to make sure exceptions are not suppressed by __length_hint__() | 
 |  | 
 |  | 
 | class BadLen(object): | 
 |     def __iter__(self): return iter(range(10)) | 
 |     def __len__(self): | 
 |         raise RuntimeError('hello') | 
 |  | 
 | class BadLengthHint(object): | 
 |     def __iter__(self): return iter(range(10)) | 
 |     def __length_hint__(self): | 
 |         raise RuntimeError('hello') | 
 |  | 
 | class NoneLengthHint(object): | 
 |     def __iter__(self): return iter(range(10)) | 
 |     def __length_hint__(self): | 
 |         return None | 
 |  | 
 | class TestLengthHintExceptions(unittest.TestCase): | 
 |  | 
 |     def test_issue1242657(self): | 
 |         self.assertRaises(RuntimeError, list, BadLen()) | 
 |         self.assertRaises(RuntimeError, list, BadLengthHint()) | 
 |         self.assertRaises(RuntimeError, [].extend, BadLen()) | 
 |         self.assertRaises(RuntimeError, [].extend, BadLengthHint()) | 
 |         b = bytearray(range(10)) | 
 |         self.assertRaises(RuntimeError, b.extend, BadLen()) | 
 |         self.assertRaises(RuntimeError, b.extend, BadLengthHint()) | 
 |  | 
 |     def test_invalid_hint(self): | 
 |         # Make sure an invalid result doesn't muck-up the works | 
 |         self.assertEqual(list(NoneLengthHint()), list(range(10))) | 
 |  | 
 |  | 
 | if __name__ == "__main__": | 
 |     unittest.main() |