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