• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Unit tests for zero-argument super() & related machinery."""
2
3import unittest
4
5
6class A:
7    def f(self):
8        return 'A'
9    @classmethod
10    def cm(cls):
11        return (cls, 'A')
12
13class B(A):
14    def f(self):
15        return super().f() + 'B'
16    @classmethod
17    def cm(cls):
18        return (cls, super().cm(), 'B')
19
20class C(A):
21    def f(self):
22        return super().f() + 'C'
23    @classmethod
24    def cm(cls):
25        return (cls, super().cm(), 'C')
26
27class D(C, B):
28    def f(self):
29        return super().f() + 'D'
30    def cm(cls):
31        return (cls, super().cm(), 'D')
32
33class E(D):
34    pass
35
36class F(E):
37    f = E.f
38
39class G(A):
40    pass
41
42
43class TestSuper(unittest.TestCase):
44
45    def tearDown(self):
46        # This fixes the damage that test_various___class___pathologies does.
47        nonlocal __class__
48        __class__ = TestSuper
49
50    def test_basics_working(self):
51        self.assertEqual(D().f(), 'ABCD')
52
53    def test_class_getattr_working(self):
54        self.assertEqual(D.f(D()), 'ABCD')
55
56    def test_subclass_no_override_working(self):
57        self.assertEqual(E().f(), 'ABCD')
58        self.assertEqual(E.f(E()), 'ABCD')
59
60    def test_unbound_method_transfer_working(self):
61        self.assertEqual(F().f(), 'ABCD')
62        self.assertEqual(F.f(F()), 'ABCD')
63
64    def test_class_methods_still_working(self):
65        self.assertEqual(A.cm(), (A, 'A'))
66        self.assertEqual(A().cm(), (A, 'A'))
67        self.assertEqual(G.cm(), (G, 'A'))
68        self.assertEqual(G().cm(), (G, 'A'))
69
70    def test_super_in_class_methods_working(self):
71        d = D()
72        self.assertEqual(d.cm(), (d, (D, (D, (D, 'A'), 'B'), 'C'), 'D'))
73        e = E()
74        self.assertEqual(e.cm(), (e, (E, (E, (E, 'A'), 'B'), 'C'), 'D'))
75
76    def test_super_with_closure(self):
77        # Issue4360: super() did not work in a function that
78        # contains a closure
79        class E(A):
80            def f(self):
81                def nested():
82                    self
83                return super().f() + 'E'
84
85        self.assertEqual(E().f(), 'AE')
86
87    def test_various___class___pathologies(self):
88        # See issue #12370
89        class X(A):
90            def f(self):
91                return super().f()
92            __class__ = 413
93        x = X()
94        self.assertEqual(x.f(), 'A')
95        self.assertEqual(x.__class__, 413)
96        class X:
97            x = __class__
98            def f():
99                __class__
100        self.assertIs(X.x, type(self))
101        with self.assertRaises(NameError) as e:
102            exec("""class X:
103                __class__
104                def f():
105                    __class__""", globals(), {})
106        self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
107        class X:
108            global __class__
109            __class__ = 42
110            def f():
111                __class__
112        self.assertEqual(globals()["__class__"], 42)
113        del globals()["__class__"]
114        self.assertNotIn("__class__", X.__dict__)
115        class X:
116            nonlocal __class__
117            __class__ = 42
118            def f():
119                __class__
120        self.assertEqual(__class__, 42)
121
122    def test___class___instancemethod(self):
123        # See issue #14857
124        class X:
125            def f(self):
126                return __class__
127        self.assertIs(X().f(), X)
128
129    def test___class___classmethod(self):
130        # See issue #14857
131        class X:
132            @classmethod
133            def f(cls):
134                return __class__
135        self.assertIs(X.f(), X)
136
137    def test___class___staticmethod(self):
138        # See issue #14857
139        class X:
140            @staticmethod
141            def f():
142                return __class__
143        self.assertIs(X.f(), X)
144
145    def test___class___new(self):
146        # See issue #23722
147        # Ensure zero-arg super() works as soon as type.__new__() is completed
148        test_class = None
149
150        class Meta(type):
151            def __new__(cls, name, bases, namespace):
152                nonlocal test_class
153                self = super().__new__(cls, name, bases, namespace)
154                test_class = self.f()
155                return self
156
157        class A(metaclass=Meta):
158            @staticmethod
159            def f():
160                return __class__
161
162        self.assertIs(test_class, A)
163
164    def test___class___delayed(self):
165        # See issue #23722
166        test_namespace = None
167
168        class Meta(type):
169            def __new__(cls, name, bases, namespace):
170                nonlocal test_namespace
171                test_namespace = namespace
172                return None
173
174        class A(metaclass=Meta):
175            @staticmethod
176            def f():
177                return __class__
178
179        self.assertIs(A, None)
180
181        B = type("B", (), test_namespace)
182        self.assertIs(B.f(), B)
183
184    def test___class___mro(self):
185        # See issue #23722
186        test_class = None
187
188        class Meta(type):
189            def mro(self):
190                # self.f() doesn't work yet...
191                self.__dict__["f"]()
192                return super().mro()
193
194        class A(metaclass=Meta):
195            def f():
196                nonlocal test_class
197                test_class = __class__
198
199        self.assertIs(test_class, A)
200
201    def test___classcell___expected_behaviour(self):
202        # See issue #23722
203        class Meta(type):
204            def __new__(cls, name, bases, namespace):
205                nonlocal namespace_snapshot
206                namespace_snapshot = namespace.copy()
207                return super().__new__(cls, name, bases, namespace)
208
209        # __classcell__ is injected into the class namespace by the compiler
210        # when at least one method needs it, and should be omitted otherwise
211        namespace_snapshot = None
212        class WithoutClassRef(metaclass=Meta):
213            pass
214        self.assertNotIn("__classcell__", namespace_snapshot)
215
216        # With zero-arg super() or an explicit __class__ reference,
217        # __classcell__ is the exact cell reference to be populated by
218        # type.__new__
219        namespace_snapshot = None
220        class WithClassRef(metaclass=Meta):
221            def f(self):
222                return __class__
223
224        class_cell = namespace_snapshot["__classcell__"]
225        method_closure = WithClassRef.f.__closure__
226        self.assertEqual(len(method_closure), 1)
227        self.assertIs(class_cell, method_closure[0])
228        # Ensure the cell reference *doesn't* get turned into an attribute
229        with self.assertRaises(AttributeError):
230            WithClassRef.__classcell__
231
232    def test___classcell___missing(self):
233        # See issue #23722
234        # Some metaclasses may not pass the original namespace to type.__new__
235        # We test that case here by forcibly deleting __classcell__
236        class Meta(type):
237            def __new__(cls, name, bases, namespace):
238                namespace.pop('__classcell__', None)
239                return super().__new__(cls, name, bases, namespace)
240
241        # The default case should continue to work without any errors
242        class WithoutClassRef(metaclass=Meta):
243            pass
244
245        # With zero-arg super() or an explicit __class__ reference, we expect
246        # __build_class__ to raise a RuntimeError complaining that
247        # __class__ was not set, and asking if __classcell__ was propagated
248        # to type.__new__.
249        expected_error = '__class__ not set.*__classcell__ propagated'
250        with self.assertRaisesRegex(RuntimeError, expected_error):
251            class WithClassRef(metaclass=Meta):
252                def f(self):
253                    return __class__
254
255    def test___classcell___overwrite(self):
256        # See issue #23722
257        # Overwriting __classcell__ with nonsense is explicitly prohibited
258        class Meta(type):
259            def __new__(cls, name, bases, namespace, cell):
260                namespace['__classcell__'] = cell
261                return super().__new__(cls, name, bases, namespace)
262
263        for bad_cell in (None, 0, "", object()):
264            with self.subTest(bad_cell=bad_cell):
265                with self.assertRaises(TypeError):
266                    class A(metaclass=Meta, cell=bad_cell):
267                        pass
268
269    def test___classcell___wrong_cell(self):
270        # See issue #23722
271        # Pointing the cell reference at the wrong class is also prohibited
272        class Meta(type):
273            def __new__(cls, name, bases, namespace):
274                cls = super().__new__(cls, name, bases, namespace)
275                B = type("B", (), namespace)
276                return cls
277
278        with self.assertRaises(TypeError):
279            class A(metaclass=Meta):
280                def f(self):
281                    return __class__
282
283    def test_obscure_super_errors(self):
284        def f():
285            super()
286        self.assertRaises(RuntimeError, f)
287        def f(x):
288            del x
289            super()
290        self.assertRaises(RuntimeError, f, None)
291        class X:
292            def f(x):
293                nonlocal __class__
294                del __class__
295                super()
296        self.assertRaises(RuntimeError, X().f)
297
298    def test_cell_as_self(self):
299        class X:
300            def meth(self):
301                super()
302
303        def f():
304            k = X()
305            def g():
306                return k
307            return g
308        c = f().__closure__[0]
309        self.assertRaises(TypeError, X.meth, c)
310
311    def test_super_init_leaks(self):
312        # Issue #26718: super.__init__ leaked memory if called multiple times.
313        # This will be caught by regrtest.py -R if this leak.
314        # NOTE: Despite the use in the test a direct call of super.__init__
315        # is not endorsed.
316        sp = super(float, 1.0)
317        for i in range(1000):
318            super.__init__(sp, int, i)
319
320
321if __name__ == "__main__":
322    unittest.main()
323