summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2016-07-30 06:26:03 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2016-07-30 06:26:03 (GMT)
commitd78448e912126410117723c7d240bbdfff06df16 (patch)
tree73490ec91133beb78ecacda68510e3552b80e3b1 /Lib/test
parentf6daa690e4ee8581e0e5aa87764c0562a16c9330 (diff)
downloadcpython-d78448e912126410117723c7d240bbdfff06df16.zip
cpython-d78448e912126410117723c7d240bbdfff06df16.tar.gz
cpython-d78448e912126410117723c7d240bbdfff06df16.tar.bz2
Issue #27366: Implement PEP 487
- __init_subclass__ called when new subclasses defined - __set_name__ called when descriptors are part of a class definition
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/test_builtin.py20
-rw-r--r--Lib/test/test_descrtut.py1
-rw-r--r--Lib/test/test_pydoc.py3
-rw-r--r--Lib/test/test_subclassinit.py244
4 files changed, 252 insertions, 16 deletions
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 8cc1b00..acc4f9c 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -1699,21 +1699,11 @@ class TestType(unittest.TestCase):
self.assertEqual(x.spam(), 'spam42')
self.assertEqual(x.to_bytes(2, 'little'), b'\x2a\x00')
- def test_type_new_keywords(self):
- class B:
- def ham(self):
- return 'ham%d' % self
- C = type.__new__(type,
- name='C',
- bases=(B, int),
- dict={'spam': lambda self: 'spam%s' % self})
- self.assertEqual(C.__name__, 'C')
- self.assertEqual(C.__qualname__, 'C')
- self.assertEqual(C.__module__, __name__)
- self.assertEqual(C.__bases__, (B, int))
- self.assertIs(C.__base__, int)
- self.assertIn('spam', C.__dict__)
- self.assertNotIn('ham', C.__dict__)
+ def test_type_nokwargs(self):
+ with self.assertRaises(TypeError):
+ type('a', (), {}, x=5)
+ with self.assertRaises(TypeError):
+ type('a', (), dict={})
def test_type_name(self):
for name in 'A', '\xc4', '\U0001f40d', 'B.A', '42', '':
diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py
index 506d1ab..b84d644 100644
--- a/Lib/test/test_descrtut.py
+++ b/Lib/test/test_descrtut.py
@@ -182,6 +182,7 @@ You can get the information from the list type:
'__iadd__',
'__imul__',
'__init__',
+ '__init_subclass__',
'__iter__',
'__le__',
'__len__',
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index 889ce59..4998597 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -638,8 +638,9 @@ class PydocDocTest(unittest.TestCase):
del expected['__doc__']
del expected['__class__']
# inspect resolves descriptors on type into methods, but vars doesn't,
- # so we need to update __subclasshook__.
+ # so we need to update __subclasshook__ and __init_subclass__.
expected['__subclasshook__'] = TestClass.__subclasshook__
+ expected['__init_subclass__'] = TestClass.__init_subclass__
methods = pydoc.allmethods(TestClass)
self.assertDictEqual(methods, expected)
diff --git a/Lib/test/test_subclassinit.py b/Lib/test/test_subclassinit.py
new file mode 100644
index 0000000..eb5ed70
--- /dev/null
+++ b/Lib/test/test_subclassinit.py
@@ -0,0 +1,244 @@
+from unittest import TestCase, main
+import sys
+import types
+
+
+class Test(TestCase):
+ def test_init_subclass(self):
+ class A(object):
+ initialized = False
+
+ def __init_subclass__(cls):
+ super().__init_subclass__()
+ cls.initialized = True
+
+ class B(A):
+ pass
+
+ self.assertFalse(A.initialized)
+ self.assertTrue(B.initialized)
+
+ def test_init_subclass_dict(self):
+ class A(dict, object):
+ initialized = False
+
+ def __init_subclass__(cls):
+ super().__init_subclass__()
+ cls.initialized = True
+
+ class B(A):
+ pass
+
+ self.assertFalse(A.initialized)
+ self.assertTrue(B.initialized)
+
+ def test_init_subclass_kwargs(self):
+ class A(object):
+ def __init_subclass__(cls, **kwargs):
+ cls.kwargs = kwargs
+
+ class B(A, x=3):
+ pass
+
+ self.assertEqual(B.kwargs, dict(x=3))
+
+ def test_init_subclass_error(self):
+ class A(object):
+ def __init_subclass__(cls):
+ raise RuntimeError
+
+ with self.assertRaises(RuntimeError):
+ class B(A):
+ pass
+
+ def test_init_subclass_wrong(self):
+ class A(object):
+ def __init_subclass__(cls, whatever):
+ pass
+
+ with self.assertRaises(TypeError):
+ class B(A):
+ pass
+
+ def test_init_subclass_skipped(self):
+ class BaseWithInit(object):
+ def __init_subclass__(cls, **kwargs):
+ super().__init_subclass__(**kwargs)
+ cls.initialized = cls
+
+ class BaseWithoutInit(BaseWithInit):
+ pass
+
+ class A(BaseWithoutInit):
+ pass
+
+ self.assertIs(A.initialized, A)
+ self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit)
+
+ def test_init_subclass_diamond(self):
+ class Base(object):
+ def __init_subclass__(cls, **kwargs):
+ super().__init_subclass__(**kwargs)
+ cls.calls = []
+
+ class Left(Base):
+ pass
+
+ class Middle(object):
+ def __init_subclass__(cls, middle, **kwargs):
+ super().__init_subclass__(**kwargs)
+ cls.calls += [middle]
+
+ class Right(Base):
+ def __init_subclass__(cls, right="right", **kwargs):
+ super().__init_subclass__(**kwargs)
+ cls.calls += [right]
+
+ class A(Left, Middle, Right, middle="middle"):
+ pass
+
+ self.assertEqual(A.calls, ["right", "middle"])
+ self.assertEqual(Left.calls, [])
+ self.assertEqual(Right.calls, [])
+
+ def test_set_name(self):
+ class Descriptor:
+ def __set_name__(self, owner, name):
+ self.owner = owner
+ self.name = name
+
+ class A(object):
+ d = Descriptor()
+
+ self.assertEqual(A.d.name, "d")
+ self.assertIs(A.d.owner, A)
+
+ def test_set_name_metaclass(self):
+ class Meta(type):
+ def __new__(cls, name, bases, ns):
+ ret = super().__new__(cls, name, bases, ns)
+ self.assertEqual(ret.d.name, "d")
+ self.assertIs(ret.d.owner, ret)
+ return 0
+
+ class Descriptor(object):
+ def __set_name__(self, owner, name):
+ self.owner = owner
+ self.name = name
+
+ class A(object, metaclass=Meta):
+ d = Descriptor()
+ self.assertEqual(A, 0)
+
+ def test_set_name_error(self):
+ class Descriptor:
+ def __set_name__(self, owner, name):
+ raise RuntimeError
+
+ with self.assertRaises(RuntimeError):
+ class A(object):
+ d = Descriptor()
+
+ def test_set_name_wrong(self):
+ class Descriptor:
+ def __set_name__(self):
+ pass
+
+ with self.assertRaises(TypeError):
+ class A(object):
+ d = Descriptor()
+
+ def test_set_name_init_subclass(self):
+ class Descriptor:
+ def __set_name__(self, owner, name):
+ self.owner = owner
+ self.name = name
+
+ class Meta(type):
+ def __new__(cls, name, bases, ns):
+ self = super().__new__(cls, name, bases, ns)
+ self.meta_owner = self.owner
+ self.meta_name = self.name
+ return self
+
+ class A(object):
+ def __init_subclass__(cls):
+ cls.owner = cls.d.owner
+ cls.name = cls.d.name
+
+ class B(A, metaclass=Meta):
+ d = Descriptor()
+
+ self.assertIs(B.owner, B)
+ self.assertEqual(B.name, 'd')
+ self.assertIs(B.meta_owner, B)
+ self.assertEqual(B.name, 'd')
+
+ def test_errors(self):
+ class MyMeta(type):
+ pass
+
+ with self.assertRaises(TypeError):
+ class MyClass(object, metaclass=MyMeta, otherarg=1):
+ pass
+
+ with self.assertRaises(TypeError):
+ types.new_class("MyClass", (object,),
+ dict(metaclass=MyMeta, otherarg=1))
+ types.prepare_class("MyClass", (object,),
+ dict(metaclass=MyMeta, otherarg=1))
+
+ class MyMeta(type):
+ def __init__(self, name, bases, namespace, otherarg):
+ super().__init__(name, bases, namespace)
+
+ with self.assertRaises(TypeError):
+ class MyClass(object, metaclass=MyMeta, otherarg=1):
+ pass
+
+ class MyMeta(type):
+ def __new__(cls, name, bases, namespace, otherarg):
+ return super().__new__(cls, name, bases, namespace)
+
+ def __init__(self, name, bases, namespace, otherarg):
+ super().__init__(name, bases, namespace)
+ self.otherarg = otherarg
+
+ class MyClass(object, metaclass=MyMeta, otherarg=1):
+ pass
+
+ self.assertEqual(MyClass.otherarg, 1)
+
+ def test_errors_changed_pep487(self):
+ # These tests failed before Python 3.6, PEP 487
+ class MyMeta(type):
+ def __new__(cls, name, bases, namespace):
+ return super().__new__(cls, name=name, bases=bases,
+ dict=namespace)
+
+ with self.assertRaises(TypeError):
+ class MyClass(object, metaclass=MyMeta):
+ pass
+
+ class MyMeta(type):
+ def __new__(cls, name, bases, namespace, otherarg):
+ self = super().__new__(cls, name, bases, namespace)
+ self.otherarg = otherarg
+ return self
+
+ class MyClass(object, metaclass=MyMeta, otherarg=1):
+ pass
+
+ self.assertEqual(MyClass.otherarg, 1)
+
+ def test_type(self):
+ t = type('NewClass', (object,), {})
+ self.assertIsInstance(t, type)
+ self.assertEqual(t.__name__, 'NewClass')
+
+ with self.assertRaises(TypeError):
+ type(name='NewClass', bases=(object,), dict={})
+
+
+if __name__ == "__main__":
+ main()