test_enumerate.py 6.62 KB
Newer Older
1
import unittest
Michael W. Hudson's avatar
Michael W. Hudson committed
2
import sys
3

4
from test import support
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

class G:
    'Sequence using __getitem__'
    def __init__(self, seqn):
        self.seqn = seqn
    def __getitem__(self, i):
        return self.seqn[i]

class I:
    'Sequence using iterator protocol'
    def __init__(self, seqn):
        self.seqn = seqn
        self.i = 0
    def __iter__(self):
        return self
20
    def __next__(self):
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
        if self.i >= len(self.seqn): raise StopIteration
        v = self.seqn[self.i]
        self.i += 1
        return v

class Ig:
    'Sequence using iterator protocol defined with a generator'
    def __init__(self, seqn):
        self.seqn = seqn
        self.i = 0
    def __iter__(self):
        for val in self.seqn:
            yield val

class X:
    'Missing __getitem__ and __iter__'
    def __init__(self, seqn):
        self.seqn = seqn
        self.i = 0
40
    def __next__(self):
41 42 43 44 45 46 47 48 49 50 51 52
        if self.i >= len(self.seqn): raise StopIteration
        v = self.seqn[self.i]
        self.i += 1
        return v

class E:
    'Test propagation of exceptions'
    def __init__(self, seqn):
        self.seqn = seqn
        self.i = 0
    def __iter__(self):
        return self
53
    def __next__(self):
54
        3 // 0
55 56

class N:
57
    'Iterator missing __next__()'
58 59 60 61 62 63 64 65 66
    def __init__(self, seqn):
        self.seqn = seqn
        self.i = 0
    def __iter__(self):
        return self

class EnumerateTestCase(unittest.TestCase):

    enum = enumerate
Raymond Hettinger's avatar
Raymond Hettinger committed
67
    seq, res = 'abc', [(0,'a'), (1,'b'), (2,'c')]
68 69

    def test_basicfunction(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
70 71
        self.assertEqual(type(self.enum(self.seq)), self.enum)
        e = self.enum(self.seq)
72
        self.assertEqual(iter(e), e)
Raymond Hettinger's avatar
Raymond Hettinger committed
73
        self.assertEqual(list(self.enum(self.seq)), self.res)
74 75 76
        self.enum.__doc__

    def test_getitemseqn(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
77
        self.assertEqual(list(self.enum(G(self.seq))), self.res)
78
        e = self.enum(G(''))
79
        self.assertRaises(StopIteration, next, e)
80 81

    def test_iteratorseqn(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
82
        self.assertEqual(list(self.enum(I(self.seq))), self.res)
83
        e = self.enum(I(''))
84
        self.assertRaises(StopIteration, next, e)
85 86

    def test_iteratorgenerator(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
87
        self.assertEqual(list(self.enum(Ig(self.seq))), self.res)
88
        e = self.enum(Ig(''))
89
        self.assertRaises(StopIteration, next, e)
90 91

    def test_noniterable(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
92
        self.assertRaises(TypeError, self.enum, X(self.seq))
93 94

    def test_illformediterable(self):
95
        self.assertRaises(TypeError, self.enum, N(self.seq))
96 97

    def test_exception_propagation(self):
Raymond Hettinger's avatar
Raymond Hettinger committed
98 99 100 101 102
        self.assertRaises(ZeroDivisionError, list, self.enum(E(self.seq)))

    def test_argumentcheck(self):
        self.assertRaises(TypeError, self.enum) # no arguments
        self.assertRaises(TypeError, self.enum, 1) # wrong type (not iterable)
103 104
        self.assertRaises(TypeError, self.enum, 'abc', 'a') # wrong type
        self.assertRaises(TypeError, self.enum, 'abc', 2, 3) # too many arguments
Raymond Hettinger's avatar
Raymond Hettinger committed
105 106 107 108

    def test_tuple_reuse(self):
        # Tests an implementation detail where tuple is reused
        # whenever nothing else holds a reference to it
109 110
        self.assertEqual(len(set(map(id, list(enumerate(self.seq))))), len(self.seq))
        self.assertEqual(len(set(map(id, enumerate(self.seq)))), min(1,len(self.seq)))
111 112 113 114 115 116 117 118

class MyEnum(enumerate):
    pass

class SubclassTestCase(EnumerateTestCase):

    enum = MyEnum

Raymond Hettinger's avatar
Raymond Hettinger committed
119 120 121 122 123 124 125
class TestEmpty(EnumerateTestCase):

    seq, res = '', []

class TestBig(EnumerateTestCase):

    seq = range(10,20000,2)
126
    res = list(zip(range(20000), seq))
Raymond Hettinger's avatar
Raymond Hettinger committed
127

128 129 130 131 132 133 134 135 136 137
class TestReversed(unittest.TestCase):

    def test_simple(self):
        class A:
            def __getitem__(self, i):
                if i < 5:
                    return str(i)
                raise StopIteration
            def __len__(self):
                return 5
138
        for data in 'abc', range(5), tuple(enumerate('abc')), A(), range(1,17,5):
139 140
            self.assertEqual(list(data)[::-1], list(reversed(data)))
        self.assertRaises(TypeError, reversed, {})
141 142
        # don't allow keyword arguments
        self.assertRaises(TypeError, reversed, [], a=1)
143

144 145
    def test_range_optimization(self):
        x = range(1)
146
        self.assertEqual(type(reversed(x)), type(iter(x)))
Raymond Hettinger's avatar
Raymond Hettinger committed
147

148
    def test_len(self):
149
        # This is an implementation detail, not an interface requirement
150
        from test.test_iterlen import len
151
        for s in ('hello', tuple('hello'), list('hello'), range(5)):
152
            self.assertEqual(len(reversed(s)), len(s))
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
            r = reversed(s)
            list(r)
            self.assertEqual(len(r), 0)
        class SeqWithWeirdLen:
            called = False
            def __len__(self):
                if not self.called:
                    self.called = True
                    return 10
                raise ZeroDivisionError
            def __getitem__(self, index):
                return index
        r = reversed(SeqWithWeirdLen())
        self.assertRaises(ZeroDivisionError, len, r)


    def test_gc(self):
        class Seq:
            def __len__(self):
                return 10
            def __getitem__(self, index):
                return index
        s = Seq()
        r = reversed(s)
        s.r = r

    def test_args(self):
        self.assertRaises(TypeError, reversed)
        self.assertRaises(TypeError, reversed, [], 'extra')
182

Michael W. Hudson's avatar
Michael W. Hudson committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
    def test_bug1229429(self):
        # this bug was never in reversed, it was in
        # PyObject_CallMethod, and reversed_new calls that sometimes.
        if not hasattr(sys, "getrefcount"):
            return
        def f():
            pass
        r = f.__reversed__ = object()
        rc = sys.getrefcount(r)
        for i in range(10):
            try:
                reversed(f)
            except TypeError:
                pass
            else:
                self.fail("non-callable __reversed__ didn't raise!")
        self.assertEqual(rc, sys.getrefcount(r))
Tim Peters's avatar
Tim Peters committed
200

Michael W. Hudson's avatar
Michael W. Hudson committed
201

202 203 204 205 206 207 208 209 210 211 212 213 214
class TestStart(EnumerateTestCase):

    enum = lambda i: enumerate(i, start=11)
    seq, res = 'abc', [(1, 'a'), (2, 'b'), (3, 'c')]


class TestLongStart(EnumerateTestCase):

    enum = lambda i: enumerate(i, start=sys.maxsize+1)
    seq, res = 'abc', [(sys.maxsize+1,'a'), (sys.maxsize+2,'b'),
                       (sys.maxsize+3,'c')]


Raymond Hettinger's avatar
Raymond Hettinger committed
215
def test_main(verbose=None):
216 217
    testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig,
                   TestReversed)
218
    support.run_unittest(*testclasses)
Raymond Hettinger's avatar
Raymond Hettinger committed
219 220 221 222 223

    # verify reference counting
    import sys
    if verbose and hasattr(sys, "gettotalrefcount"):
        counts = [None] * 5
224
        for i in range(len(counts)):
225
            support.run_unittest(*testclasses)
Raymond Hettinger's avatar
Raymond Hettinger committed
226
            counts[i] = sys.gettotalrefcount()
227
        print(counts)
228 229

if __name__ == "__main__":
Raymond Hettinger's avatar
Raymond Hettinger committed
230
    test_main(verbose=True)