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

4
from test import test_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 103 104 105 106 107
        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)
        self.assertRaises(TypeError, self.enum, 'abc', 2) # too many arguments

    def test_tuple_reuse(self):
        # Tests an implementation detail where tuple is reused
        # whenever nothing else holds a reference to it
108 109
        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)))
110 111 112 113 114 115 116 117

class MyEnum(enumerate):
    pass

class SubclassTestCase(EnumerateTestCase):

    enum = MyEnum

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

    seq, res = '', []

class TestBig(EnumerateTestCase):

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

127 128 129 130 131 132 133 134 135 136
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
137
        for data in 'abc', range(5), tuple(enumerate('abc')), A(), range(1,17,5):
138 139 140
            self.assertEqual(list(data)[::-1], list(reversed(data)))
        self.assertRaises(TypeError, reversed, {})

141 142
    def test_range_optimization(self):
        x = range(1)
143
        self.assertEqual(type(reversed(x)), type(iter(x)))
Raymond Hettinger's avatar
Raymond Hettinger committed
144

145
    def test_len(self):
146
        # This is an implementation detail, not an interface requirement
147
        from test.test_iterlen import len
148
        for s in ('hello', tuple('hello'), list('hello'), range(5)):
149
            self.assertEqual(len(reversed(s)), len(s))
150 151 152 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
            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')
179

Michael W. Hudson's avatar
Michael W. Hudson committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
    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
197

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

Raymond Hettinger's avatar
Raymond Hettinger committed
199
def test_main(verbose=None):
200 201
    testclasses = (EnumerateTestCase, SubclassTestCase, TestEmpty, TestBig,
                   TestReversed)
Raymond Hettinger's avatar
Raymond Hettinger committed
202 203 204 205 206 207
    test_support.run_unittest(*testclasses)

    # verify reference counting
    import sys
    if verbose and hasattr(sys, "gettotalrefcount"):
        counts = [None] * 5
208
        for i in range(len(counts)):
Raymond Hettinger's avatar
Raymond Hettinger committed
209 210
            test_support.run_unittest(*testclasses)
            counts[i] = sys.gettotalrefcount()
211
        print(counts)
212 213

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