aboutsummaryrefslogtreecommitdiff
blob: eb2ce9f9deb91bf2df106a085aa92d0c618d9613 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# test about the binop operation rule, see issue 412

class Base(object):
    def __init__(self, name):
        self.name = name

def lookup_where(obj, name):
    mro = type(obj).__mro__
    for t in mro:
        if name in t.__dict__:
            return t.__dict__[name], t
    return None, None

def refop(x, y, opname, ropname):
    # this has been validated by running the tests on top of cpython
    # so for the space of possibilities that the tests touch it is known
    # to behave like cpython as long as the latter doesn't change its own
    # algorithm
    t1 = type(x)
    t2 = type(y)
    op, where1 = lookup_where(x, opname)
    rop, where2 = lookup_where(y, ropname)
    if op is None and rop is not None:
        return rop(y, x)
    if rop and where1 is not where2:
        if (issubclass(t2, t1) and not issubclass(where1, where2)
            and not issubclass(t1, where2)
            ):
            return rop(y, x)
    if op is None:
        return "TypeError"
    return op(x,y)

def do_test(X, Y, name, impl):
    x = X('x')
    y = Y('y')
    opname = '__%s__' % name
    ropname = '__r%s__' % name

    count = [0]
    fail = []

    def check(z1, z2):
        ref = refop(z1, z2, opname, ropname)
        try:
            v = impl(z1, z2)
        except TypeError:
            v = "TypeError"
        if v != ref:
            fail.append(count[0])

    def override_in_hier(n=6):
        if n == 0:
            count[0] += 1
            check(x, y)
            check(y, x)
            return

        f = lambda self, other: (n, self.name, other.name)
        if n%2 == 0:
            name = opname
        else:
            name = ropname

        for C in Y.__mro__:
            if name in C.__dict__:
                continue
            if C is not object:
                setattr(C, name, f)
            override_in_hier(n-1)
            if C is not object:
                delattr(C, name)

    override_in_hier()
    #print count[0]
    return fail

def test_binop_combinations_mul():
    class X(Base):
        pass
    class Y(X):
        pass

    fail = do_test(X, Y, 'mul', lambda x,y: x*y)
    #print len(fail)
    assert not fail



def test_binop_combinations_sub():
    class X(Base):
        pass
    class Y(X):
        pass

    fail = do_test(X, Y, 'sub', lambda x,y: x-y)
    #print len(fail)
    assert not fail


def test_binop_combinations_pow():
    class X(Base):
        pass
    class Y(X):
        pass

    fail = do_test(X, Y, 'pow', lambda x,y: x**y)
    #print len(fail)
    assert not fail

def test_binop_combinations_more_exhaustive():
    class X(Base):
        pass

    class B1(object):
        pass

    class B2(object):
        pass

    class X1(B1, X, B2):
        pass

    class C1(object):
        pass

    class C2(object):
        pass

    class Y(C1, X1, C2):
        pass

    fail = do_test(X, Y, 'sub', lambda x,y: x-y)
    #print len(fail)
    assert not fail