1
|
|
|
from abc import abstractmethod |
2
|
|
|
import abc as abc_main |
3
|
|
|
from inspect import isabstract |
4
|
|
|
import sys |
5
|
|
|
|
6
|
|
|
import pytest |
7
|
|
|
|
8
|
|
|
|
9
|
|
|
def test_import_compat(compat): |
10
|
|
|
"""Test the module is importable. See conftest.py for the actual import.""" |
11
|
|
|
assert compat |
12
|
|
|
|
13
|
|
|
|
14
|
|
|
def test_import_abc(abc): |
15
|
|
|
"""Test the module is importable. See conftest.py for the actual import.""" |
16
|
|
|
assert abc |
17
|
|
|
|
18
|
|
|
|
19
|
|
|
def test_has_class(abc): |
20
|
|
|
"""Test the convenience class exists.""" |
21
|
|
|
assert abc.NamespaceableABC |
22
|
|
|
|
23
|
|
|
|
24
|
|
|
def test_ABC_helper(abc): |
25
|
|
|
"""Test the convenience class works as expected.""" |
26
|
|
|
# create an ABC using the helper class and perform basic checks |
27
|
|
|
class C(abc.NamespaceableABC): |
28
|
|
|
"""A throwaway test class.""" |
29
|
|
|
@classmethod |
30
|
|
|
@abstractmethod |
31
|
|
|
def footer(cls): |
32
|
|
|
return cls.__name__ |
33
|
|
|
assert isinstance(C, abc.NamespaceableABCMeta) |
34
|
|
|
with pytest.raises(TypeError): |
35
|
|
|
print(C()) |
36
|
|
|
|
37
|
|
|
class D(C): |
38
|
|
|
"""A throwaway test class.""" |
39
|
|
|
@classmethod |
40
|
|
|
def footer(cls): |
41
|
|
|
return super().footer() |
42
|
|
|
assert D.footer() == 'D' |
43
|
|
|
|
44
|
|
|
|
45
|
|
|
def test_abstractmethod_basics(abc): |
46
|
|
|
"""Test abstractmethod works as expected. |
47
|
|
|
|
48
|
|
|
Adapted from Python's test suite. |
49
|
|
|
""" |
50
|
|
|
@abstractmethod |
51
|
|
|
def footer(self): |
52
|
|
|
"""Return nothing. Abstract.""" |
53
|
|
|
assert footer.__isabstractmethod__ |
54
|
|
|
|
55
|
|
|
def barter(self): |
56
|
|
|
"""Return nothing. Concrete.""" |
57
|
|
|
assert not hasattr(barter, "__isabstractmethod__") |
58
|
|
|
|
59
|
|
|
|
60
|
|
|
def test_abstractproperty_basics(abc): |
61
|
|
|
"""Test abstract property works as expected. |
62
|
|
|
|
63
|
|
|
Adapted from Python's test suite. |
64
|
|
|
""" |
65
|
|
|
@property |
66
|
|
|
@abstractmethod |
67
|
|
|
def footer(self): |
68
|
|
|
"""Return nothing. Abstract.""" |
69
|
|
|
assert footer.__isabstractmethod__ |
70
|
|
|
|
71
|
|
|
def barter(self): |
72
|
|
|
"""Return nothing. Concrete.""" |
73
|
|
|
assert not getattr(barter, "__isabstractmethod__", False) |
74
|
|
|
|
75
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
76
|
|
|
"""A throwaway test class.""" |
77
|
|
|
@property |
78
|
|
|
@abstractmethod |
79
|
|
|
def footer(self): |
80
|
|
|
"""Return 3. Abstract.""" |
81
|
|
|
return 3 |
82
|
|
|
with pytest.raises(TypeError): |
83
|
|
|
print(C()) |
84
|
|
|
|
85
|
|
|
class D(C): |
86
|
|
|
"""A throwaway test class.""" |
87
|
|
|
@C.footer.getter |
88
|
|
|
def footer(self): |
89
|
|
|
"""Return 3. Concrete.""" |
90
|
|
|
return super().footer |
91
|
|
|
assert D().footer == 3 |
92
|
|
|
|
93
|
|
|
|
94
|
|
|
def test_abstractproperty_namespaced(abc, namespace): |
95
|
|
|
"""Test interaction between namespaces and abstract properties.""" |
96
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
97
|
|
|
"""A throwaway test class.""" |
98
|
|
|
with namespace() as ns: |
99
|
|
|
@property |
100
|
|
|
@abstractmethod |
101
|
|
|
def footer(self): |
102
|
|
|
"""Return 3. Abstract.""" |
103
|
|
|
return 3 |
104
|
|
|
with pytest.raises(TypeError): |
105
|
|
|
print(C()) |
106
|
|
|
|
107
|
|
|
class D(C): |
108
|
|
|
"""A throwaway test class.""" |
109
|
|
|
with namespace() as ns: |
110
|
|
|
@C.ns.footer.getter |
111
|
|
|
def footer(self): |
112
|
|
|
"""Return 3. Concrete.""" |
113
|
|
|
return super().ns.footer |
114
|
|
|
assert D().ns.footer == 3 |
115
|
|
|
|
116
|
|
|
|
117
|
|
View Code Duplication |
def test_abstractclassmethod_basics(abc): |
|
|
|
|
118
|
|
|
"""Test abstract classmethod works as expected. |
119
|
|
|
|
120
|
|
|
Adapted from Python's test suite. |
121
|
|
|
""" |
122
|
|
|
@classmethod |
123
|
|
|
@abstractmethod |
124
|
|
|
def footer(cls): |
125
|
|
|
pass |
126
|
|
|
assert footer.__isabstractmethod__ |
127
|
|
|
|
128
|
|
|
@classmethod |
129
|
|
|
def barter(cls): |
130
|
|
|
pass |
131
|
|
|
assert not getattr(barter, "__isabstractmethod__", False) |
132
|
|
|
|
133
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
134
|
|
|
"""A throwaway test class.""" |
135
|
|
|
@classmethod |
136
|
|
|
@abstractmethod |
137
|
|
|
def footer(cls): |
138
|
|
|
return cls.__name__ |
139
|
|
|
with pytest.raises(TypeError): |
140
|
|
|
print(C()) |
141
|
|
|
|
142
|
|
|
class D(C): |
143
|
|
|
"""A throwaway test class.""" |
144
|
|
|
@classmethod |
145
|
|
|
def footer(cls): |
146
|
|
|
return super().footer() |
147
|
|
|
assert D.footer() == 'D' |
148
|
|
|
assert D().footer() == 'D' |
149
|
|
|
|
150
|
|
|
|
151
|
|
View Code Duplication |
def test_abstractclassmethod_namespaced(abc, namespace): |
|
|
|
|
152
|
|
|
"""Test interaction between namespaces and abstract classmethods.""" |
153
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
154
|
|
|
"""A throwaway test class.""" |
155
|
|
|
with namespace() as ns: |
156
|
|
|
@classmethod |
157
|
|
|
@abstractmethod |
158
|
|
|
def footer(cls): |
159
|
|
|
return cls.__name__ |
160
|
|
|
with pytest.raises(TypeError): |
161
|
|
|
print(C()) |
162
|
|
|
|
163
|
|
|
class D(C): |
164
|
|
|
"""A throwaway test class.""" |
165
|
|
|
with namespace() as ns: |
166
|
|
|
@classmethod |
167
|
|
|
def footer(cls): |
168
|
|
|
return super().ns.footer() |
169
|
|
|
assert D.ns.footer() == 'D' |
170
|
|
|
assert D().ns.footer() == 'D' |
171
|
|
|
|
172
|
|
|
|
173
|
|
View Code Duplication |
def test_abstractstaticmethod_basics(abc): |
|
|
|
|
174
|
|
|
"""Test abstract staticmethod works as expected. |
175
|
|
|
|
176
|
|
|
Adapted from Python's test suite. |
177
|
|
|
""" |
178
|
|
|
@staticmethod |
179
|
|
|
@abstractmethod |
180
|
|
|
def footer(): |
181
|
|
|
pass |
182
|
|
|
assert footer.__isabstractmethod__ |
183
|
|
|
|
184
|
|
|
@staticmethod |
185
|
|
|
def barter(): |
186
|
|
|
pass |
187
|
|
|
assert not (getattr(barter, "__isabstractmethod__", False)) |
188
|
|
|
|
189
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
190
|
|
|
"""A throwaway test class.""" |
191
|
|
|
@staticmethod |
192
|
|
|
@abstractmethod |
193
|
|
|
def footer(): |
194
|
|
|
return 3 |
195
|
|
|
with pytest.raises(TypeError): |
196
|
|
|
print(C()) |
197
|
|
|
|
198
|
|
|
class D(C): |
199
|
|
|
"""A throwaway test class.""" |
200
|
|
|
@staticmethod |
201
|
|
|
def footer(): |
202
|
|
|
return 4 |
203
|
|
|
assert D.footer() == 4 |
204
|
|
|
assert D().footer() == 4 |
205
|
|
|
|
206
|
|
|
|
207
|
|
View Code Duplication |
def test_abstractstaticmethod_namespaced(abc, namespace): |
|
|
|
|
208
|
|
|
"""Test interaction between namespaces and abstract staticmethods.""" |
209
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
210
|
|
|
"""A throwaway test class.""" |
211
|
|
|
with namespace() as ns: |
212
|
|
|
@staticmethod |
213
|
|
|
@abstractmethod |
214
|
|
|
def footer(): |
215
|
|
|
return 3 |
216
|
|
|
with pytest.raises(TypeError): |
217
|
|
|
print(C()) |
218
|
|
|
|
219
|
|
|
class D(C): |
220
|
|
|
"""A throwaway test class.""" |
221
|
|
|
with namespace() as ns: |
222
|
|
|
@staticmethod |
223
|
|
|
def footer(): |
224
|
|
|
return 4 |
225
|
|
|
assert D.ns.footer() == 4 |
226
|
|
|
assert D().ns.footer() == 4 |
227
|
|
|
|
228
|
|
|
|
229
|
|
|
def test_abstractmethod_integration(abc): |
230
|
|
|
"""Test abstract shortcut decorators work as expected. |
231
|
|
|
|
232
|
|
|
Adapted from Python's test suite. |
233
|
|
|
""" |
234
|
|
|
for abstractthing in [abstractmethod, abc_main.abstractproperty, |
235
|
|
|
abc_main.abstractclassmethod, |
236
|
|
|
abc_main.abstractstaticmethod]: |
237
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
238
|
|
|
"""A throwaway test class.""" |
239
|
|
|
@abstractthing |
240
|
|
|
def footer(self): |
241
|
|
|
pass # abstract |
242
|
|
|
|
243
|
|
|
def barter(self): |
244
|
|
|
pass # concrete |
245
|
|
|
assert C.__abstractmethods__ == {"footer"} |
246
|
|
|
with pytest.raises(TypeError): |
247
|
|
|
print(C()) # because footer is abstract |
248
|
|
|
assert isabstract(C) |
249
|
|
|
|
250
|
|
|
class D(C): |
251
|
|
|
"""A throwaway test class.""" |
252
|
|
|
|
253
|
|
|
def barter(self): |
254
|
|
|
pass # concrete override of concrete |
255
|
|
|
assert D.__abstractmethods__ == {"footer"} |
256
|
|
|
with pytest.raises(TypeError): |
257
|
|
|
print(D()) # because footer is still abstract |
258
|
|
|
assert isabstract(D) |
259
|
|
|
|
260
|
|
|
class E(D): |
261
|
|
|
"""A throwaway test class.""" |
262
|
|
|
|
263
|
|
|
def footer(self): |
264
|
|
|
pass |
265
|
|
|
assert E.__abstractmethods__ == set() |
266
|
|
|
E() # now footer is concrete, too |
267
|
|
|
assert not isabstract(E) |
268
|
|
|
|
269
|
|
|
class F(E): |
270
|
|
|
"""A throwaway test class.""" |
271
|
|
|
@abstractthing |
272
|
|
|
def barter(self): |
273
|
|
|
pass # abstract override of concrete |
274
|
|
|
assert F.__abstractmethods__ == {"barter"} |
275
|
|
|
with pytest.raises(TypeError): |
276
|
|
|
print(F()) # because barter is abstract now |
277
|
|
|
assert isabstract(F) |
278
|
|
|
|
279
|
|
|
|
280
|
|
|
def test_abstractmethod_integration_namespaced(abc, namespace): |
281
|
|
|
"""Test abstract shortcut decorators work as expected, under a namespace. |
282
|
|
|
|
283
|
|
|
Adapted from Python's test suite. |
284
|
|
|
""" |
285
|
|
|
for abstractthing in [abstractmethod, abc_main.abstractproperty, |
286
|
|
|
abc_main.abstractclassmethod, |
287
|
|
|
abc_main.abstractstaticmethod]: |
288
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
289
|
|
|
"""A throwaway test class.""" |
290
|
|
|
with namespace() as ns: |
291
|
|
|
@abstractthing |
292
|
|
|
def footer(self): |
293
|
|
|
pass # abstract |
294
|
|
|
|
295
|
|
|
def barter(self): |
296
|
|
|
pass # concrete |
297
|
|
|
assert C.__abstractmethods__ == {"ns.footer"} |
298
|
|
|
with pytest.raises(TypeError): |
299
|
|
|
print(C()) # because footer is abstract |
300
|
|
|
assert isabstract(C) |
301
|
|
|
|
302
|
|
|
class D(C): |
303
|
|
|
"""A throwaway test class.""" |
304
|
|
|
with namespace() as ns: |
305
|
|
|
def barter(self): |
306
|
|
|
pass # concrete override of concrete |
307
|
|
|
assert D.__abstractmethods__ == {"ns.footer"} |
308
|
|
|
with pytest.raises(TypeError): |
309
|
|
|
print(D()) # because footer is still abstract |
310
|
|
|
assert isabstract(D) |
311
|
|
|
|
312
|
|
|
class E(D): |
313
|
|
|
"""A throwaway test class.""" |
314
|
|
|
with namespace() as ns: |
315
|
|
|
def footer(self): |
316
|
|
|
pass |
317
|
|
|
assert E.__abstractmethods__ == set() |
318
|
|
|
E() # now footer is concrete, too |
319
|
|
|
assert not isabstract(E) |
320
|
|
|
|
321
|
|
|
class F(E): |
322
|
|
|
"""A throwaway test class.""" |
323
|
|
|
with namespace() as ns: |
324
|
|
|
@abstractthing |
325
|
|
|
def barter(self): |
326
|
|
|
pass # abstract override of concrete |
327
|
|
|
assert F.__abstractmethods__ == {"ns.barter"} |
328
|
|
|
with pytest.raises(TypeError): |
329
|
|
|
print(F()) # because barter is abstract now |
330
|
|
|
assert isabstract(F) |
331
|
|
|
|
332
|
|
|
|
333
|
|
|
def test_descriptors_with_abstractmethod(abc): |
334
|
|
|
"""Test abstract property methods work as expected. |
335
|
|
|
|
336
|
|
|
Adapted from Python's test suite. |
337
|
|
|
""" |
338
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
339
|
|
|
"""A throwaway test class.""" |
340
|
|
|
@property |
341
|
|
|
@abstractmethod |
342
|
|
|
def footer(self): |
343
|
|
|
return 3 |
344
|
|
|
|
345
|
|
|
@footer.setter |
346
|
|
|
@abstractmethod |
347
|
|
|
def footer(self, val): |
348
|
|
|
pass |
349
|
|
|
with pytest.raises(TypeError): |
350
|
|
|
print(C()) |
351
|
|
|
|
352
|
|
|
class D(C): |
353
|
|
|
"""A throwaway test class.""" |
354
|
|
|
@C.footer.getter |
355
|
|
|
def footer(self): |
356
|
|
|
return super().footer |
357
|
|
|
with pytest.raises(TypeError): |
358
|
|
|
print(D()) |
359
|
|
|
|
360
|
|
|
class E(D): |
361
|
|
|
"""A throwaway test class.""" |
362
|
|
|
@D.footer.setter |
363
|
|
|
def footer(self, val): |
364
|
|
|
pass |
365
|
|
|
assert E().footer == 3 |
366
|
|
|
# check that the property's __isabstractmethod__ descriptor does the |
367
|
|
|
# right thing when presented with a value that fails truth testing: |
368
|
|
|
|
369
|
|
|
class NotBool(object): |
370
|
|
|
"""A pathological class for test purposes.""" |
371
|
|
|
|
372
|
|
|
def __bool__(self): |
373
|
|
|
raise ValueError() |
374
|
|
|
__len__ = __bool__ |
375
|
|
|
with pytest.raises(ValueError): |
376
|
|
|
class F(C): |
377
|
|
|
"""A throwaway test class.""" |
378
|
|
|
|
379
|
|
|
def barter(self): |
380
|
|
|
pass |
381
|
|
|
barter.__isabstractmethod__ = NotBool() |
382
|
|
|
footer = property(barter) |
383
|
|
|
|
384
|
|
|
|
385
|
|
|
def test_descriptors_with_abstractmethod_namespaced(abc, namespace): |
386
|
|
|
"""Test abstract property methods work as expected under a namespace. |
387
|
|
|
|
388
|
|
|
Adapted from Python's test suite. |
389
|
|
|
""" |
390
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
391
|
|
|
"""A throwaway test class.""" |
392
|
|
|
with namespace() as ns: |
393
|
|
|
@property |
394
|
|
|
@abstractmethod |
395
|
|
|
def footer(self): |
396
|
|
|
return 3 |
397
|
|
|
|
398
|
|
|
@footer.setter |
399
|
|
|
@abstractmethod |
400
|
|
|
def footer(self, val): |
401
|
|
|
pass |
402
|
|
|
with pytest.raises(TypeError): |
403
|
|
|
print(C()) |
404
|
|
|
|
405
|
|
|
class D(C): |
406
|
|
|
"""A throwaway test class.""" |
407
|
|
|
with namespace() as ns: |
408
|
|
|
@C.ns.footer.getter |
409
|
|
|
def footer(self): |
410
|
|
|
return super().ns.footer |
411
|
|
|
with pytest.raises(TypeError): |
412
|
|
|
print(D()) |
413
|
|
|
|
414
|
|
|
class E(D): |
415
|
|
|
"""A throwaway test class.""" |
416
|
|
|
with namespace() as ns: |
417
|
|
|
@D.ns.footer.setter |
418
|
|
|
def footer(self, val): |
419
|
|
|
pass |
420
|
|
|
assert E().ns.footer == 3 |
421
|
|
|
# check that the property's __isabstractmethod__ descriptor does the |
422
|
|
|
# right thing when presented with a value that fails truth testing: |
423
|
|
|
|
424
|
|
|
class NotBool(object): |
425
|
|
|
"""A pathological class for test purposes.""" |
426
|
|
|
|
427
|
|
|
def __bool__(self): |
428
|
|
|
raise ValueError() |
429
|
|
|
__len__ = __bool__ |
430
|
|
|
with pytest.raises(ValueError): |
431
|
|
|
class F(C): |
432
|
|
|
"""A throwaway test class.""" |
433
|
|
|
with namespace() as ns: |
434
|
|
|
def barter(self): |
435
|
|
|
pass |
436
|
|
|
barter.__isabstractmethod__ = NotBool() |
437
|
|
|
footer = property(barter) |
438
|
|
|
|
439
|
|
|
|
440
|
|
|
def test_customdescriptors_with_abstractmethod(abc): |
441
|
|
|
"""Test abstract custom descriptors work as expected. |
442
|
|
|
|
443
|
|
|
Adapted from Python's test suite. |
444
|
|
|
""" |
445
|
|
|
class Descriptor: |
446
|
|
|
"""A descriptor class integrated some with the ABC protocol.""" |
447
|
|
|
|
448
|
|
|
def __init__(self, fget, fset=None): |
449
|
|
|
self._fget = fget |
450
|
|
|
self._fset = fset |
451
|
|
|
|
452
|
|
|
def getter(self, callable): |
453
|
|
|
return Descriptor(callable, self._fget) |
454
|
|
|
|
455
|
|
|
def setter(self, callable): |
456
|
|
|
return Descriptor(self._fget, callable) |
457
|
|
|
|
458
|
|
|
@property |
459
|
|
|
def __isabstractmethod__(self): |
460
|
|
|
return (getattr(self._fget, '__isabstractmethod__', False) or |
461
|
|
|
getattr(self._fset, '__isabstractmethod__', False)) |
462
|
|
|
|
463
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
464
|
|
|
"""A throwaway test class.""" |
465
|
|
|
@Descriptor |
466
|
|
|
@abstractmethod |
467
|
|
|
def footer(self): |
468
|
|
|
return 3 |
469
|
|
|
|
470
|
|
|
@footer.setter |
471
|
|
|
@abstractmethod |
472
|
|
|
def footer(self, val): |
473
|
|
|
pass |
474
|
|
|
with pytest.raises(TypeError): |
475
|
|
|
print(C()) |
476
|
|
|
|
477
|
|
|
class D(C): |
478
|
|
|
"""A throwaway test class.""" |
479
|
|
|
@C.footer.getter |
480
|
|
|
def footer(self): |
481
|
|
|
return super().footer |
482
|
|
|
with pytest.raises(TypeError): |
483
|
|
|
print(D()) |
484
|
|
|
|
485
|
|
|
class E(D): |
486
|
|
|
"""A throwaway test class.""" |
487
|
|
|
@D.footer.setter |
488
|
|
|
def footer(self, val): |
489
|
|
|
pass |
490
|
|
|
assert not (E.footer.__isabstractmethod__) |
491
|
|
|
|
492
|
|
|
|
493
|
|
|
def test_customdescriptors_with_abstractmethod_namespaced(abc, namespace): |
494
|
|
|
"""Test abstract custom descriptors work as expected under a namespace. |
495
|
|
|
|
496
|
|
|
Adapted from Python's test suite. |
497
|
|
|
""" |
498
|
|
|
class Descriptor: |
499
|
|
|
"""A descriptor class integrated some with the ABC protocol.""" |
500
|
|
|
|
501
|
|
|
def __init__(self, fget, fset=None): |
502
|
|
|
self._fget = fget |
503
|
|
|
self._fset = fset |
504
|
|
|
|
505
|
|
|
def getter(self, callable): |
506
|
|
|
return Descriptor(callable, self._fget) |
507
|
|
|
|
508
|
|
|
def setter(self, callable): |
509
|
|
|
return Descriptor(self._fget, callable) |
510
|
|
|
|
511
|
|
|
@property |
512
|
|
|
def __isabstractmethod__(self): |
513
|
|
|
return (getattr(self._fget, '__isabstractmethod__', False) or |
514
|
|
|
getattr(self._fset, '__isabstractmethod__', False)) |
515
|
|
|
|
516
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
517
|
|
|
"""A throwaway test class.""" |
518
|
|
|
with namespace() as ns: |
519
|
|
|
@Descriptor |
520
|
|
|
@abstractmethod |
521
|
|
|
def footer(self): |
522
|
|
|
return 3 |
523
|
|
|
|
524
|
|
|
@footer.setter |
525
|
|
|
@abstractmethod |
526
|
|
|
def footer(self, val): |
527
|
|
|
pass |
528
|
|
|
with pytest.raises(TypeError): |
529
|
|
|
print(C()) |
530
|
|
|
|
531
|
|
|
class D(C): |
532
|
|
|
"""A throwaway test class.""" |
533
|
|
|
with namespace() as ns: |
534
|
|
|
@C.ns.footer.getter |
535
|
|
|
def footer(self): |
536
|
|
|
return super().ns.footer |
537
|
|
|
with pytest.raises(TypeError): |
538
|
|
|
print(D()) |
539
|
|
|
|
540
|
|
|
class E(D): |
541
|
|
|
"""A throwaway test class.""" |
542
|
|
|
with namespace() as ns: |
543
|
|
|
@D.ns.footer.setter |
544
|
|
|
def footer(self, val): |
545
|
|
|
pass |
546
|
|
|
assert not (E.ns.footer.__isabstractmethod__) |
547
|
|
|
|
548
|
|
|
|
549
|
|
|
def test_metaclass_abc(abc): |
550
|
|
|
"""Test abstract metaclasses work as expected. |
551
|
|
|
|
552
|
|
|
Adapted from Python's test suite. |
553
|
|
|
""" |
554
|
|
|
# Metaclasses can be ABCs, too. |
555
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
556
|
|
|
"""A throwaway test class.""" |
557
|
|
|
@abstractmethod |
558
|
|
|
def x(self): |
559
|
|
|
pass |
560
|
|
|
assert A.__abstractmethods__ == {"x"} |
561
|
|
|
|
562
|
|
|
class meta(type, A): |
563
|
|
|
"""A throwaway test metaclass.""" |
564
|
|
|
|
565
|
|
|
def x(self): |
566
|
|
|
return 1 |
567
|
|
|
|
568
|
|
|
class C(metaclass=meta): |
569
|
|
|
"""A throwaway test class.""" |
570
|
|
|
|
571
|
|
|
|
572
|
|
|
def test_metaclass_abc_namespaced(abc, namespace): |
573
|
|
|
"""Test abstract metaclasses work as expected, with namespaces. |
574
|
|
|
|
575
|
|
|
Adapted from Python's test suite. |
576
|
|
|
""" |
577
|
|
|
# Metaclasses can be ABCs, too. |
578
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
579
|
|
|
"""A throwaway test class.""" |
580
|
|
|
with namespace() as ns: |
581
|
|
|
@abstractmethod |
582
|
|
|
def x(self): |
583
|
|
|
pass |
584
|
|
|
assert A.__abstractmethods__ == {"ns.x"} |
585
|
|
|
|
586
|
|
|
class meta(type, A): |
587
|
|
|
"""A throwaway test metaclass.""" |
588
|
|
|
with namespace() as ns: |
589
|
|
|
def x(self): |
590
|
|
|
return 1 |
591
|
|
|
|
592
|
|
|
class C(metaclass=meta): |
593
|
|
|
"""A throwaway test class.""" |
594
|
|
|
|
595
|
|
|
|
596
|
|
|
def test_registration_basics(abc): |
597
|
|
|
"""Test ABC registration. |
598
|
|
|
|
599
|
|
|
Adapted from Python's test suite. |
600
|
|
|
""" |
601
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
602
|
|
|
"""A throwaway test class.""" |
603
|
|
|
|
604
|
|
|
class B(object): |
605
|
|
|
"""A throwaway test class.""" |
606
|
|
|
b = B() |
607
|
|
|
assert not (issubclass(B, A)) |
608
|
|
|
assert not (issubclass(B, (A,))) |
609
|
|
|
assert not isinstance(b, A) |
610
|
|
|
assert not isinstance(b, (A,)) |
611
|
|
|
B1 = A.register(B) |
612
|
|
|
assert issubclass(B, A) |
613
|
|
|
assert issubclass(B, (A,)) |
614
|
|
|
assert isinstance(b, A) |
615
|
|
|
assert isinstance(b, (A,)) |
616
|
|
|
assert B1 is B |
617
|
|
|
|
618
|
|
|
class C(B): |
619
|
|
|
"""A throwaway test class.""" |
620
|
|
|
c = C() |
621
|
|
|
assert issubclass(C, A) |
622
|
|
|
assert issubclass(C, (A,)) |
623
|
|
|
assert isinstance(c, A) |
624
|
|
|
assert isinstance(c, (A,)) |
625
|
|
|
|
626
|
|
|
|
627
|
|
|
def test_register_as_class_deco(abc): |
628
|
|
|
"""Test ABC registration decorator. |
629
|
|
|
|
630
|
|
|
Adapted from Python's test suite. |
631
|
|
|
""" |
632
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
633
|
|
|
"""A throwaway test class.""" |
634
|
|
|
|
635
|
|
|
@A.register |
636
|
|
|
class B(object): |
637
|
|
|
"""A throwaway test class.""" |
638
|
|
|
b = B() |
639
|
|
|
assert issubclass(B, A) |
640
|
|
|
assert issubclass(B, (A,)) |
641
|
|
|
assert isinstance(b, A) |
642
|
|
|
assert isinstance(b, (A,)) |
643
|
|
|
|
644
|
|
|
@A.register |
645
|
|
|
class C(B): |
646
|
|
|
"""A throwaway test class.""" |
647
|
|
|
c = C() |
648
|
|
|
assert issubclass(C, A) |
649
|
|
|
assert issubclass(C, (A,)) |
650
|
|
|
assert isinstance(c, A) |
651
|
|
|
assert isinstance(c, (A,)) |
652
|
|
|
assert C is A.register(C) |
653
|
|
|
|
654
|
|
|
|
655
|
|
|
@pytest.mark.xfail(sys.version_info < (3, 4), |
656
|
|
|
reason="python3.4 api changes?", strict=True) |
657
|
|
|
def test_isinstance_invalidation(abc): |
658
|
|
|
"""Test after-the-fact registration behavior. |
659
|
|
|
|
660
|
|
|
Adapted from Python's test suite. |
661
|
|
|
""" |
662
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
663
|
|
|
"""A throwaway test class.""" |
664
|
|
|
|
665
|
|
|
class B: |
666
|
|
|
"""A throwaway test class.""" |
667
|
|
|
b = B() |
668
|
|
|
assert not (isinstance(b, A)) |
669
|
|
|
assert not (isinstance(b, (A,))) |
670
|
|
|
token_old = abc_main.get_cache_token() |
671
|
|
|
A.register(B) |
672
|
|
|
token_new = abc_main.get_cache_token() |
673
|
|
|
assert token_old != token_new |
674
|
|
|
assert isinstance(b, A) |
675
|
|
|
assert isinstance(b, (A,)) |
676
|
|
|
|
677
|
|
|
|
678
|
|
|
def test_registration_builtins(abc): |
679
|
|
|
"""Test making builtin classes into registered subclasses. |
680
|
|
|
|
681
|
|
|
Adapted from Python's test suite. |
682
|
|
|
""" |
683
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
684
|
|
|
"""A throwaway test class.""" |
685
|
|
|
A.register(int) |
686
|
|
|
assert isinstance(42, A) |
687
|
|
|
assert isinstance(42, (A,)) |
688
|
|
|
assert issubclass(int, A) |
689
|
|
|
assert issubclass(int, (A,)) |
690
|
|
|
|
691
|
|
|
class B(A): |
692
|
|
|
"""A throwaway test class.""" |
693
|
|
|
B.register(str) |
694
|
|
|
|
695
|
|
|
class C(str): |
696
|
|
|
"""A throwaway test class.""" |
697
|
|
|
assert isinstance("", A) |
698
|
|
|
assert isinstance("", (A,)) |
699
|
|
|
assert issubclass(str, A) |
700
|
|
|
assert issubclass(str, (A,)) |
701
|
|
|
assert issubclass(C, A) |
702
|
|
|
assert issubclass(C, (A,)) |
703
|
|
|
|
704
|
|
|
|
705
|
|
|
def test_registration_edge_cases(abc): |
706
|
|
|
"""Test edge cases in registration: reflexive, cyclic, repeated... |
707
|
|
|
|
708
|
|
|
Adapted from Python's test suite. |
709
|
|
|
""" |
710
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
711
|
|
|
"""A throwaway test class.""" |
712
|
|
|
A.register(A) # should pass silently |
713
|
|
|
|
714
|
|
|
class A1(A): |
715
|
|
|
"""A throwaway test class.""" |
716
|
|
|
with pytest.raises(RuntimeError): |
717
|
|
|
A1.register(A) # cycles not allowed |
718
|
|
|
|
719
|
|
|
class B(object): |
720
|
|
|
"""A throwaway test class.""" |
721
|
|
|
A1.register(B) # ok |
722
|
|
|
A1.register(B) # should pass silently |
723
|
|
|
|
724
|
|
|
class C(A): |
725
|
|
|
"""A throwaway test class.""" |
726
|
|
|
A.register(C) # should pass silently |
727
|
|
|
with pytest.raises(RuntimeError): |
728
|
|
|
C.register(A) # cycles not allowed |
729
|
|
|
C.register(B) # ok |
730
|
|
|
|
731
|
|
|
|
732
|
|
|
def test_register_non_class(abc): |
733
|
|
|
"""Test that non-classes cannot be registered. |
734
|
|
|
|
735
|
|
|
Adapted from Python's test suite. |
736
|
|
|
""" |
737
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
738
|
|
|
"""A throwaway test class.""" |
739
|
|
|
with pytest.raises(TypeError, message="Can only register classes"): |
740
|
|
|
print(A.register(4)) |
741
|
|
|
|
742
|
|
|
|
743
|
|
|
def test_registration_transitiveness(abc): |
744
|
|
|
"""Test that chains of registration hold. |
745
|
|
|
|
746
|
|
|
Adapted from Python's test suite. |
747
|
|
|
""" |
748
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
749
|
|
|
"""A throwaway test class.""" |
750
|
|
|
assert issubclass(A, A) |
751
|
|
|
assert issubclass(A, (A,)) |
752
|
|
|
|
753
|
|
|
class B(metaclass=abc.NamespaceableABCMeta): |
754
|
|
|
"""A throwaway test class.""" |
755
|
|
|
assert not (issubclass(A, B)) |
756
|
|
|
assert not (issubclass(A, (B,))) |
757
|
|
|
assert not (issubclass(B, A)) |
758
|
|
|
assert not (issubclass(B, (A,))) |
759
|
|
|
|
760
|
|
|
class C(metaclass=abc.NamespaceableABCMeta): |
761
|
|
|
"""A throwaway test class.""" |
762
|
|
|
A.register(B) |
763
|
|
|
|
764
|
|
|
class B1(B): |
765
|
|
|
"""A throwaway test class.""" |
766
|
|
|
assert issubclass(B1, A) |
767
|
|
|
assert issubclass(B1, (A,)) |
768
|
|
|
|
769
|
|
|
class C1(C): |
770
|
|
|
"""A throwaway test class.""" |
771
|
|
|
B1.register(C1) |
772
|
|
|
assert not issubclass(C, B) |
773
|
|
|
assert not issubclass(C, (B,)) |
774
|
|
|
assert not issubclass(C, B1) |
775
|
|
|
assert not issubclass(C, (B1,)) |
776
|
|
|
assert issubclass(C1, A) |
777
|
|
|
assert issubclass(C1, (A,)) |
778
|
|
|
assert issubclass(C1, B) |
779
|
|
|
assert issubclass(C1, (B,)) |
780
|
|
|
assert issubclass(C1, B1) |
781
|
|
|
assert issubclass(C1, (B1,)) |
782
|
|
|
C1.register(int) |
783
|
|
|
|
784
|
|
|
class MyInt(int): |
785
|
|
|
"""A throwaway test class.""" |
786
|
|
|
assert issubclass(MyInt, A) |
787
|
|
|
assert issubclass(MyInt, (A,)) |
788
|
|
|
assert isinstance(42, A) |
789
|
|
|
assert isinstance(42, (A,)) |
790
|
|
|
|
791
|
|
|
|
792
|
|
|
def test_all_new_methods_are_called(abc): |
793
|
|
|
"""Test that super delegation still works using abstract classes. |
794
|
|
|
|
795
|
|
|
Adapted from Python's test suite. |
796
|
|
|
""" |
797
|
|
|
class A(metaclass=abc.NamespaceableABCMeta): |
798
|
|
|
"""A throwaway test class.""" |
799
|
|
|
|
800
|
|
|
class B(object): |
801
|
|
|
"""A throwaway test class.""" |
802
|
|
|
counter = 0 |
803
|
|
|
|
804
|
|
|
def __new__(cls): |
805
|
|
|
B.counter += 1 |
806
|
|
|
return super().__new__(cls) |
807
|
|
|
|
808
|
|
|
class C(A, B): |
809
|
|
|
"""A throwaway test class.""" |
810
|
|
|
assert B.counter == 0 |
811
|
|
|
C() |
812
|
|
|
assert B.counter == 1 |
813
|
|
|
|