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