mirror of
https://github.com/python/cpython.git
synced 2025-07-23 03:05:38 +00:00

The __class__ cell used by zero-argument super() is now initialized from type.__new__ rather than __build_class__, so class methods relying on that will now work correctly when called from metaclass methods during class creation. Patch by Martin Teichmann.
266 lines
6.9 KiB
Python
266 lines
6.9 KiB
Python
"""Unit tests for new super() implementation."""
|
|
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
class A:
|
|
def f(self):
|
|
return 'A'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, 'A')
|
|
|
|
class B(A):
|
|
def f(self):
|
|
return super().f() + 'B'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'B')
|
|
|
|
class C(A):
|
|
def f(self):
|
|
return super().f() + 'C'
|
|
@classmethod
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'C')
|
|
|
|
class D(C, B):
|
|
def f(self):
|
|
return super().f() + 'D'
|
|
def cm(cls):
|
|
return (cls, super().cm(), 'D')
|
|
|
|
class E(D):
|
|
pass
|
|
|
|
class F(E):
|
|
f = E.f
|
|
|
|
class G(A):
|
|
pass
|
|
|
|
|
|
class TestSuper(unittest.TestCase):
|
|
|
|
def tearDown(self):
|
|
# This fixes the damage that test_various___class___pathologies does.
|
|
nonlocal __class__
|
|
__class__ = TestSuper
|
|
|
|
def test_basics_working(self):
|
|
self.assertEqual(D().f(), 'ABCD')
|
|
|
|
def test_class_getattr_working(self):
|
|
self.assertEqual(D.f(D()), 'ABCD')
|
|
|
|
def test_subclass_no_override_working(self):
|
|
self.assertEqual(E().f(), 'ABCD')
|
|
self.assertEqual(E.f(E()), 'ABCD')
|
|
|
|
def test_unbound_method_transfer_working(self):
|
|
self.assertEqual(F().f(), 'ABCD')
|
|
self.assertEqual(F.f(F()), 'ABCD')
|
|
|
|
def test_class_methods_still_working(self):
|
|
self.assertEqual(A.cm(), (A, 'A'))
|
|
self.assertEqual(A().cm(), (A, 'A'))
|
|
self.assertEqual(G.cm(), (G, 'A'))
|
|
self.assertEqual(G().cm(), (G, 'A'))
|
|
|
|
def test_super_in_class_methods_working(self):
|
|
d = D()
|
|
self.assertEqual(d.cm(), (d, (D, (D, (D, 'A'), 'B'), 'C'), 'D'))
|
|
e = E()
|
|
self.assertEqual(e.cm(), (e, (E, (E, (E, 'A'), 'B'), 'C'), 'D'))
|
|
|
|
def test_super_with_closure(self):
|
|
# Issue4360: super() did not work in a function that
|
|
# contains a closure
|
|
class E(A):
|
|
def f(self):
|
|
def nested():
|
|
self
|
|
return super().f() + 'E'
|
|
|
|
self.assertEqual(E().f(), 'AE')
|
|
|
|
def test_various___class___pathologies(self):
|
|
# See issue #12370
|
|
class X(A):
|
|
def f(self):
|
|
return super().f()
|
|
__class__ = 413
|
|
x = X()
|
|
self.assertEqual(x.f(), 'A')
|
|
self.assertEqual(x.__class__, 413)
|
|
class X:
|
|
x = __class__
|
|
def f():
|
|
__class__
|
|
self.assertIs(X.x, type(self))
|
|
with self.assertRaises(NameError) as e:
|
|
exec("""class X:
|
|
__class__
|
|
def f():
|
|
__class__""", globals(), {})
|
|
self.assertIs(type(e.exception), NameError) # Not UnboundLocalError
|
|
class X:
|
|
global __class__
|
|
__class__ = 42
|
|
def f():
|
|
__class__
|
|
self.assertEqual(globals()["__class__"], 42)
|
|
del globals()["__class__"]
|
|
self.assertNotIn("__class__", X.__dict__)
|
|
class X:
|
|
nonlocal __class__
|
|
__class__ = 42
|
|
def f():
|
|
__class__
|
|
self.assertEqual(__class__, 42)
|
|
|
|
def test___class___instancemethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
def f(self):
|
|
return __class__
|
|
self.assertIs(X().f(), X)
|
|
|
|
def test___class___classmethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
@classmethod
|
|
def f(cls):
|
|
return __class__
|
|
self.assertIs(X.f(), X)
|
|
|
|
def test___class___staticmethod(self):
|
|
# See issue #14857
|
|
class X:
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
self.assertIs(X.f(), X)
|
|
|
|
def test___class___new(self):
|
|
test_class = None
|
|
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
nonlocal test_class
|
|
self = super().__new__(cls, name, bases, namespace)
|
|
test_class = self.f()
|
|
return self
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
|
|
self.assertIs(test_class, A)
|
|
|
|
def test___class___delayed(self):
|
|
test_namespace = None
|
|
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
nonlocal test_namespace
|
|
test_namespace = namespace
|
|
return None
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
return __class__
|
|
|
|
self.assertIs(A, None)
|
|
|
|
B = type("B", (), test_namespace)
|
|
self.assertIs(B.f(), B)
|
|
|
|
def test___class___mro(self):
|
|
test_class = None
|
|
|
|
class Meta(type):
|
|
def mro(self):
|
|
# self.f() doesn't work yet...
|
|
self.__dict__["f"]()
|
|
return super().mro()
|
|
|
|
class A(metaclass=Meta):
|
|
def f():
|
|
nonlocal test_class
|
|
test_class = __class__
|
|
|
|
self.assertIs(test_class, A)
|
|
|
|
def test___classcell___deleted(self):
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
del namespace['__classcell__']
|
|
return super().__new__(cls, name, bases, namespace)
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
__class__
|
|
|
|
with self.assertRaises(NameError):
|
|
A.f()
|
|
|
|
def test___classcell___reset(self):
|
|
class Meta(type):
|
|
def __new__(cls, name, bases, namespace):
|
|
namespace['__classcell__'] = 0
|
|
return super().__new__(cls, name, bases, namespace)
|
|
|
|
class A(metaclass=Meta):
|
|
@staticmethod
|
|
def f():
|
|
__class__
|
|
|
|
with self.assertRaises(NameError):
|
|
A.f()
|
|
self.assertEqual(A.__classcell__, 0)
|
|
|
|
def test_obscure_super_errors(self):
|
|
def f():
|
|
super()
|
|
self.assertRaises(RuntimeError, f)
|
|
def f(x):
|
|
del x
|
|
super()
|
|
self.assertRaises(RuntimeError, f, None)
|
|
class X:
|
|
def f(x):
|
|
nonlocal __class__
|
|
del __class__
|
|
super()
|
|
self.assertRaises(RuntimeError, X().f)
|
|
|
|
def test_cell_as_self(self):
|
|
class X:
|
|
def meth(self):
|
|
super()
|
|
|
|
def f():
|
|
k = X()
|
|
def g():
|
|
return k
|
|
return g
|
|
c = f().__closure__[0]
|
|
self.assertRaises(TypeError, X.meth, c)
|
|
|
|
def test_super_init_leaks(self):
|
|
# Issue #26718: super.__init__ leaked memory if called multiple times.
|
|
# This will be caught by regrtest.py -R if this leak.
|
|
# NOTE: Despite the use in the test a direct call of super.__init__
|
|
# is not endorsed.
|
|
sp = super(float, 1.0)
|
|
for i in range(1000):
|
|
super.__init__(sp, int, i)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|