summaryrefslogtreecommitdiff
path: root/tests/basics/class_setname_hazard_rand.py
blob: 4c9934c3bf068554da953e4f49ccaa09c75dc998 (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
# Test to make sure there's no sequence hazard even when a __set_name__ implementation
# mutates and reorders the namespace of its owner class.
# VERY hard bug to prove out except via a stochastic test.


try:
    from random import choice
    import re
except ImportError:
    print("SKIP")
    raise SystemExit


def skip_if_no_descriptors():
    class Descriptor:
        def __get__(self, obj, cls):
            return

    class TestClass:
        Forward = Descriptor()

    a = TestClass()
    try:
        a.__class__
    except AttributeError:
        # Target doesn't support __class__.
        print("SKIP")
        raise SystemExit

    b = a.Forward
    if "Descriptor" in repr(b.__class__):
        # Target doesn't support descriptors.
        print("SKIP")
        raise SystemExit


skip_if_no_descriptors()

letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

# Would be r"[A-Z]{5}", but not all ports support the {n} quantifier.
junk_re = re.compile(r"[A-Z][A-Z][A-Z][A-Z][A-Z]")


def junk_fill(obj, n=10):  # Add randomly-generated attributes to an object.
    for i in range(n):
        name = "".join(choice(letters) for j in range(5))
        setattr(obj, name, object())


def junk_clear(obj):  # Remove attributes added by junk_fill.
    to_del = [name for name in dir(obj) if junk_re.match(name)]
    for name in to_del:
        delattr(obj, name)


def junk_sequencer():
    global runs
    try:
        while True:
            owner, name = yield
            runs[name] = runs.get(name, 0) + 1
            junk_fill(owner)
    finally:
        junk_clear(owner)


class JunkMaker:
    def __set_name__(self, owner, name):
        global seq
        seq.send((owner, name))


runs = {}
seq = junk_sequencer()
next(seq)


class Main:
    a = JunkMaker()
    b = JunkMaker()
    c = JunkMaker()
    d = JunkMaker()
    e = JunkMaker()
    f = JunkMaker()
    g = JunkMaker()
    h = JunkMaker()
    i = JunkMaker()
    j = JunkMaker()
    k = JunkMaker()
    l = JunkMaker()
    m = JunkMaker()
    n = JunkMaker()
    o = JunkMaker()
    p = JunkMaker()
    q = JunkMaker()
    r = JunkMaker()
    s = JunkMaker()
    t = JunkMaker()
    u = JunkMaker()
    v = JunkMaker()
    w = JunkMaker()
    x = JunkMaker()
    y = JunkMaker()
    z = JunkMaker()


seq.close()

for k in letters.lower():
    print(k, runs.get(k, 0))