blob: 0ad7d17fbd4ddd98bd1cc57ac403c9f91638152e [file] [log] [blame]
Nick Coghland78448e2016-07-30 16:26:03 +10001import types
Berker Peksag01d17192016-07-30 14:06:15 +03002import unittest
Nick Coghland78448e2016-07-30 16:26:03 +10003
4
Berker Peksag01d17192016-07-30 14:06:15 +03005class Test(unittest.TestCase):
Nick Coghland78448e2016-07-30 16:26:03 +10006 def test_init_subclass(self):
Berker Peksag01d17192016-07-30 14:06:15 +03007 class A:
Nick Coghland78448e2016-07-30 16:26:03 +10008 initialized = False
9
10 def __init_subclass__(cls):
11 super().__init_subclass__()
12 cls.initialized = True
13
14 class B(A):
15 pass
16
17 self.assertFalse(A.initialized)
18 self.assertTrue(B.initialized)
19
20 def test_init_subclass_dict(self):
Berker Peksag01d17192016-07-30 14:06:15 +030021 class A(dict):
Nick Coghland78448e2016-07-30 16:26:03 +100022 initialized = False
23
24 def __init_subclass__(cls):
25 super().__init_subclass__()
26 cls.initialized = True
27
28 class B(A):
29 pass
30
31 self.assertFalse(A.initialized)
32 self.assertTrue(B.initialized)
33
34 def test_init_subclass_kwargs(self):
Berker Peksag01d17192016-07-30 14:06:15 +030035 class A:
Nick Coghland78448e2016-07-30 16:26:03 +100036 def __init_subclass__(cls, **kwargs):
37 cls.kwargs = kwargs
38
39 class B(A, x=3):
40 pass
41
42 self.assertEqual(B.kwargs, dict(x=3))
43
44 def test_init_subclass_error(self):
Berker Peksag01d17192016-07-30 14:06:15 +030045 class A:
Nick Coghland78448e2016-07-30 16:26:03 +100046 def __init_subclass__(cls):
47 raise RuntimeError
48
49 with self.assertRaises(RuntimeError):
50 class B(A):
51 pass
52
53 def test_init_subclass_wrong(self):
Berker Peksag01d17192016-07-30 14:06:15 +030054 class A:
Nick Coghland78448e2016-07-30 16:26:03 +100055 def __init_subclass__(cls, whatever):
56 pass
57
58 with self.assertRaises(TypeError):
59 class B(A):
60 pass
61
62 def test_init_subclass_skipped(self):
Berker Peksag01d17192016-07-30 14:06:15 +030063 class BaseWithInit:
Nick Coghland78448e2016-07-30 16:26:03 +100064 def __init_subclass__(cls, **kwargs):
65 super().__init_subclass__(**kwargs)
66 cls.initialized = cls
67
68 class BaseWithoutInit(BaseWithInit):
69 pass
70
71 class A(BaseWithoutInit):
72 pass
73
74 self.assertIs(A.initialized, A)
75 self.assertIs(BaseWithoutInit.initialized, BaseWithoutInit)
76
77 def test_init_subclass_diamond(self):
Berker Peksag01d17192016-07-30 14:06:15 +030078 class Base:
Nick Coghland78448e2016-07-30 16:26:03 +100079 def __init_subclass__(cls, **kwargs):
80 super().__init_subclass__(**kwargs)
81 cls.calls = []
82
83 class Left(Base):
84 pass
85
Berker Peksag01d17192016-07-30 14:06:15 +030086 class Middle:
Nick Coghland78448e2016-07-30 16:26:03 +100087 def __init_subclass__(cls, middle, **kwargs):
88 super().__init_subclass__(**kwargs)
89 cls.calls += [middle]
90
91 class Right(Base):
92 def __init_subclass__(cls, right="right", **kwargs):
93 super().__init_subclass__(**kwargs)
94 cls.calls += [right]
95
96 class A(Left, Middle, Right, middle="middle"):
97 pass
98
99 self.assertEqual(A.calls, ["right", "middle"])
100 self.assertEqual(Left.calls, [])
101 self.assertEqual(Right.calls, [])
102
103 def test_set_name(self):
104 class Descriptor:
105 def __set_name__(self, owner, name):
106 self.owner = owner
107 self.name = name
108
Berker Peksag01d17192016-07-30 14:06:15 +0300109 class A:
Nick Coghland78448e2016-07-30 16:26:03 +1000110 d = Descriptor()
111
112 self.assertEqual(A.d.name, "d")
113 self.assertIs(A.d.owner, A)
114
115 def test_set_name_metaclass(self):
116 class Meta(type):
117 def __new__(cls, name, bases, ns):
118 ret = super().__new__(cls, name, bases, ns)
119 self.assertEqual(ret.d.name, "d")
120 self.assertIs(ret.d.owner, ret)
121 return 0
122
Berker Peksag01d17192016-07-30 14:06:15 +0300123 class Descriptor:
Nick Coghland78448e2016-07-30 16:26:03 +1000124 def __set_name__(self, owner, name):
125 self.owner = owner
126 self.name = name
127
Berker Peksag01d17192016-07-30 14:06:15 +0300128 class A(metaclass=Meta):
Nick Coghland78448e2016-07-30 16:26:03 +1000129 d = Descriptor()
130 self.assertEqual(A, 0)
131
132 def test_set_name_error(self):
133 class Descriptor:
134 def __set_name__(self, owner, name):
Serhiy Storchakad5d32d22016-10-21 17:13:31 +0300135 1/0
Nick Coghland78448e2016-07-30 16:26:03 +1000136
Serhiy Storchakad5d32d22016-10-21 17:13:31 +0300137 with self.assertRaises(RuntimeError) as cm:
138 class NotGoingToWork:
139 attr = Descriptor()
140
141 exc = cm.exception
142 self.assertRegex(str(exc), r'\bNotGoingToWork\b')
143 self.assertRegex(str(exc), r'\battr\b')
144 self.assertRegex(str(exc), r'\bDescriptor\b')
145 self.assertIsInstance(exc.__cause__, ZeroDivisionError)
Nick Coghland78448e2016-07-30 16:26:03 +1000146
147 def test_set_name_wrong(self):
148 class Descriptor:
149 def __set_name__(self):
150 pass
151
Serhiy Storchakad5d32d22016-10-21 17:13:31 +0300152 with self.assertRaises(RuntimeError) as cm:
153 class NotGoingToWork:
154 attr = Descriptor()
155
156 exc = cm.exception
157 self.assertRegex(str(exc), r'\bNotGoingToWork\b')
158 self.assertRegex(str(exc), r'\battr\b')
159 self.assertRegex(str(exc), r'\bDescriptor\b')
160 self.assertIsInstance(exc.__cause__, TypeError)
Nick Coghland78448e2016-07-30 16:26:03 +1000161
Serhiy Storchakaafd02a42016-09-21 15:54:59 +0300162 def test_set_name_lookup(self):
163 resolved = []
164 class NonDescriptor:
165 def __getattr__(self, name):
166 resolved.append(name)
167
168 class A:
169 d = NonDescriptor()
170
171 self.assertNotIn('__set_name__', resolved,
172 '__set_name__ is looked up in instance dict')
173
Nick Coghland78448e2016-07-30 16:26:03 +1000174 def test_set_name_init_subclass(self):
175 class Descriptor:
176 def __set_name__(self, owner, name):
177 self.owner = owner
178 self.name = name
179
180 class Meta(type):
181 def __new__(cls, name, bases, ns):
182 self = super().__new__(cls, name, bases, ns)
183 self.meta_owner = self.owner
184 self.meta_name = self.name
185 return self
186
Berker Peksag01d17192016-07-30 14:06:15 +0300187 class A:
Nick Coghland78448e2016-07-30 16:26:03 +1000188 def __init_subclass__(cls):
189 cls.owner = cls.d.owner
190 cls.name = cls.d.name
191
192 class B(A, metaclass=Meta):
193 d = Descriptor()
194
195 self.assertIs(B.owner, B)
196 self.assertEqual(B.name, 'd')
197 self.assertIs(B.meta_owner, B)
198 self.assertEqual(B.name, 'd')
199
Serhiy Storchaka9ec07722016-11-29 09:54:17 +0200200 def test_set_name_modifying_dict(self):
201 notified = []
202 class Descriptor:
203 def __set_name__(self, owner, name):
204 setattr(owner, name + 'x', None)
205 notified.append(name)
206
207 class A:
208 a = Descriptor()
209 b = Descriptor()
210 c = Descriptor()
211 d = Descriptor()
212 e = Descriptor()
213
214 self.assertCountEqual(notified, ['a', 'b', 'c', 'd', 'e'])
215
Nick Coghland78448e2016-07-30 16:26:03 +1000216 def test_errors(self):
217 class MyMeta(type):
218 pass
219
220 with self.assertRaises(TypeError):
Berker Peksag01d17192016-07-30 14:06:15 +0300221 class MyClass(metaclass=MyMeta, otherarg=1):
Nick Coghland78448e2016-07-30 16:26:03 +1000222 pass
223
224 with self.assertRaises(TypeError):
225 types.new_class("MyClass", (object,),
226 dict(metaclass=MyMeta, otherarg=1))
227 types.prepare_class("MyClass", (object,),
228 dict(metaclass=MyMeta, otherarg=1))
229
230 class MyMeta(type):
231 def __init__(self, name, bases, namespace, otherarg):
232 super().__init__(name, bases, namespace)
233
234 with self.assertRaises(TypeError):
Berker Peksag01d17192016-07-30 14:06:15 +0300235 class MyClass(metaclass=MyMeta, otherarg=1):
Nick Coghland78448e2016-07-30 16:26:03 +1000236 pass
237
238 class MyMeta(type):
239 def __new__(cls, name, bases, namespace, otherarg):
240 return super().__new__(cls, name, bases, namespace)
241
242 def __init__(self, name, bases, namespace, otherarg):
243 super().__init__(name, bases, namespace)
244 self.otherarg = otherarg
245
Berker Peksag01d17192016-07-30 14:06:15 +0300246 class MyClass(metaclass=MyMeta, otherarg=1):
Nick Coghland78448e2016-07-30 16:26:03 +1000247 pass
248
249 self.assertEqual(MyClass.otherarg, 1)
250
251 def test_errors_changed_pep487(self):
252 # These tests failed before Python 3.6, PEP 487
253 class MyMeta(type):
254 def __new__(cls, name, bases, namespace):
255 return super().__new__(cls, name=name, bases=bases,
256 dict=namespace)
257
258 with self.assertRaises(TypeError):
Berker Peksag01d17192016-07-30 14:06:15 +0300259 class MyClass(metaclass=MyMeta):
Nick Coghland78448e2016-07-30 16:26:03 +1000260 pass
261
262 class MyMeta(type):
263 def __new__(cls, name, bases, namespace, otherarg):
264 self = super().__new__(cls, name, bases, namespace)
265 self.otherarg = otherarg
266 return self
267
Berker Peksag01d17192016-07-30 14:06:15 +0300268 class MyClass(metaclass=MyMeta, otherarg=1):
Nick Coghland78448e2016-07-30 16:26:03 +1000269 pass
270
271 self.assertEqual(MyClass.otherarg, 1)
272
273 def test_type(self):
274 t = type('NewClass', (object,), {})
275 self.assertIsInstance(t, type)
276 self.assertEqual(t.__name__, 'NewClass')
277
278 with self.assertRaises(TypeError):
279 type(name='NewClass', bases=(object,), dict={})
280
281
282if __name__ == "__main__":
Berker Peksag01d17192016-07-30 14:06:15 +0300283 unittest.main()