• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2007 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4# Note: each test is run with Python and C versions of ABCMeta. Except for
5# test_ABC_helper(), which assures that abc.ABC is an instance of abc.ABCMeta.
6
7"""Unit tests for abc.py."""
8
9import unittest
10
11import abc
12import _py_abc
13from inspect import isabstract
14
15def test_factory(abc_ABCMeta, abc_get_cache_token):
16    class TestLegacyAPI(unittest.TestCase):
17
18        def test_abstractproperty_basics(self):
19            @abc.abstractproperty
20            def foo(self): pass
21            self.assertTrue(foo.__isabstractmethod__)
22            def bar(self): pass
23            self.assertFalse(hasattr(bar, "__isabstractmethod__"))
24
25            class C(metaclass=abc_ABCMeta):
26                @abc.abstractproperty
27                def foo(self): return 3
28            self.assertRaises(TypeError, C)
29            class D(C):
30                @property
31                def foo(self): return super().foo
32            self.assertEqual(D().foo, 3)
33            self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
34
35        def test_abstractclassmethod_basics(self):
36            @abc.abstractclassmethod
37            def foo(cls): pass
38            self.assertTrue(foo.__isabstractmethod__)
39            @classmethod
40            def bar(cls): pass
41            self.assertFalse(getattr(bar, "__isabstractmethod__", False))
42
43            class C(metaclass=abc_ABCMeta):
44                @abc.abstractclassmethod
45                def foo(cls): return cls.__name__
46            self.assertRaises(TypeError, C)
47            class D(C):
48                @classmethod
49                def foo(cls): return super().foo()
50            self.assertEqual(D.foo(), 'D')
51            self.assertEqual(D().foo(), 'D')
52
53        def test_abstractstaticmethod_basics(self):
54            @abc.abstractstaticmethod
55            def foo(): pass
56            self.assertTrue(foo.__isabstractmethod__)
57            @staticmethod
58            def bar(): pass
59            self.assertFalse(getattr(bar, "__isabstractmethod__", False))
60
61            class C(metaclass=abc_ABCMeta):
62                @abc.abstractstaticmethod
63                def foo(): return 3
64            self.assertRaises(TypeError, C)
65            class D(C):
66                @staticmethod
67                def foo(): return 4
68            self.assertEqual(D.foo(), 4)
69            self.assertEqual(D().foo(), 4)
70
71
72    class TestABC(unittest.TestCase):
73
74        def test_ABC_helper(self):
75            # create an ABC using the helper class and perform basic checks
76            class C(abc.ABC):
77                @classmethod
78                @abc.abstractmethod
79                def foo(cls): return cls.__name__
80            self.assertEqual(type(C), abc.ABCMeta)
81            self.assertRaises(TypeError, C)
82            class D(C):
83                @classmethod
84                def foo(cls): return super().foo()
85            self.assertEqual(D.foo(), 'D')
86
87        def test_abstractmethod_basics(self):
88            @abc.abstractmethod
89            def foo(self): pass
90            self.assertTrue(foo.__isabstractmethod__)
91            def bar(self): pass
92            self.assertFalse(hasattr(bar, "__isabstractmethod__"))
93
94        def test_abstractproperty_basics(self):
95            @property
96            @abc.abstractmethod
97            def foo(self): pass
98            self.assertTrue(foo.__isabstractmethod__)
99            def bar(self): pass
100            self.assertFalse(getattr(bar, "__isabstractmethod__", False))
101
102            class C(metaclass=abc_ABCMeta):
103                @property
104                @abc.abstractmethod
105                def foo(self): return 3
106            self.assertRaises(TypeError, C)
107            class D(C):
108                @C.foo.getter
109                def foo(self): return super().foo
110            self.assertEqual(D().foo, 3)
111
112        def test_abstractclassmethod_basics(self):
113            @classmethod
114            @abc.abstractmethod
115            def foo(cls): pass
116            self.assertTrue(foo.__isabstractmethod__)
117            @classmethod
118            def bar(cls): pass
119            self.assertFalse(getattr(bar, "__isabstractmethod__", False))
120
121            class C(metaclass=abc_ABCMeta):
122                @classmethod
123                @abc.abstractmethod
124                def foo(cls): return cls.__name__
125            self.assertRaises(TypeError, C)
126            class D(C):
127                @classmethod
128                def foo(cls): return super().foo()
129            self.assertEqual(D.foo(), 'D')
130            self.assertEqual(D().foo(), 'D')
131
132        def test_abstractstaticmethod_basics(self):
133            @staticmethod
134            @abc.abstractmethod
135            def foo(): pass
136            self.assertTrue(foo.__isabstractmethod__)
137            @staticmethod
138            def bar(): pass
139            self.assertFalse(getattr(bar, "__isabstractmethod__", False))
140
141            class C(metaclass=abc_ABCMeta):
142                @staticmethod
143                @abc.abstractmethod
144                def foo(): return 3
145            self.assertRaises(TypeError, C)
146            class D(C):
147                @staticmethod
148                def foo(): return 4
149            self.assertEqual(D.foo(), 4)
150            self.assertEqual(D().foo(), 4)
151
152        def test_object_new_with_one_abstractmethod(self):
153            class C(metaclass=abc_ABCMeta):
154                @abc.abstractmethod
155                def method_one(self):
156                    pass
157            msg = r"class C with abstract method method_one"
158            self.assertRaisesRegex(TypeError, msg, C)
159
160        def test_object_new_with_many_abstractmethods(self):
161            class C(metaclass=abc_ABCMeta):
162                @abc.abstractmethod
163                def method_one(self):
164                    pass
165                @abc.abstractmethod
166                def method_two(self):
167                    pass
168            msg = r"class C with abstract methods method_one, method_two"
169            self.assertRaisesRegex(TypeError, msg, C)
170
171        def test_abstractmethod_integration(self):
172            for abstractthing in [abc.abstractmethod, abc.abstractproperty,
173                                  abc.abstractclassmethod,
174                                  abc.abstractstaticmethod]:
175                class C(metaclass=abc_ABCMeta):
176                    @abstractthing
177                    def foo(self): pass  # abstract
178                    def bar(self): pass  # concrete
179                self.assertEqual(C.__abstractmethods__, {"foo"})
180                self.assertRaises(TypeError, C)  # because foo is abstract
181                self.assertTrue(isabstract(C))
182                class D(C):
183                    def bar(self): pass  # concrete override of concrete
184                self.assertEqual(D.__abstractmethods__, {"foo"})
185                self.assertRaises(TypeError, D)  # because foo is still abstract
186                self.assertTrue(isabstract(D))
187                class E(D):
188                    def foo(self): pass
189                self.assertEqual(E.__abstractmethods__, set())
190                E()  # now foo is concrete, too
191                self.assertFalse(isabstract(E))
192                class F(E):
193                    @abstractthing
194                    def bar(self): pass  # abstract override of concrete
195                self.assertEqual(F.__abstractmethods__, {"bar"})
196                self.assertRaises(TypeError, F)  # because bar is abstract now
197                self.assertTrue(isabstract(F))
198
199        def test_descriptors_with_abstractmethod(self):
200            class C(metaclass=abc_ABCMeta):
201                @property
202                @abc.abstractmethod
203                def foo(self): return 3
204                @foo.setter
205                @abc.abstractmethod
206                def foo(self, val): pass
207            self.assertRaises(TypeError, C)
208            class D(C):
209                @C.foo.getter
210                def foo(self): return super().foo
211            self.assertRaises(TypeError, D)
212            class E(D):
213                @D.foo.setter
214                def foo(self, val): pass
215            self.assertEqual(E().foo, 3)
216            # check that the property's __isabstractmethod__ descriptor does the
217            # right thing when presented with a value that fails truth testing:
218            class NotBool(object):
219                def __bool__(self):
220                    raise ValueError()
221                __len__ = __bool__
222            with self.assertRaises(ValueError):
223                class F(C):
224                    def bar(self):
225                        pass
226                    bar.__isabstractmethod__ = NotBool()
227                    foo = property(bar)
228
229
230        def test_customdescriptors_with_abstractmethod(self):
231            class Descriptor:
232                def __init__(self, fget, fset=None):
233                    self._fget = fget
234                    self._fset = fset
235                def getter(self, callable):
236                    return Descriptor(callable, self._fget)
237                def setter(self, callable):
238                    return Descriptor(self._fget, callable)
239                @property
240                def __isabstractmethod__(self):
241                    return (getattr(self._fget, '__isabstractmethod__', False)
242                            or getattr(self._fset, '__isabstractmethod__', False))
243            class C(metaclass=abc_ABCMeta):
244                @Descriptor
245                @abc.abstractmethod
246                def foo(self): return 3
247                @foo.setter
248                @abc.abstractmethod
249                def foo(self, val): pass
250            self.assertRaises(TypeError, C)
251            class D(C):
252                @C.foo.getter
253                def foo(self): return super().foo
254            self.assertRaises(TypeError, D)
255            class E(D):
256                @D.foo.setter
257                def foo(self, val): pass
258            self.assertFalse(E.foo.__isabstractmethod__)
259
260        def test_metaclass_abc(self):
261            # Metaclasses can be ABCs, too.
262            class A(metaclass=abc_ABCMeta):
263                @abc.abstractmethod
264                def x(self):
265                    pass
266            self.assertEqual(A.__abstractmethods__, {"x"})
267            class meta(type, A):
268                def x(self):
269                    return 1
270            class C(metaclass=meta):
271                pass
272
273        def test_registration_basics(self):
274            class A(metaclass=abc_ABCMeta):
275                pass
276            class B(object):
277                pass
278            b = B()
279            self.assertFalse(issubclass(B, A))
280            self.assertFalse(issubclass(B, (A,)))
281            self.assertNotIsInstance(b, A)
282            self.assertNotIsInstance(b, (A,))
283            B1 = A.register(B)
284            self.assertTrue(issubclass(B, A))
285            self.assertTrue(issubclass(B, (A,)))
286            self.assertIsInstance(b, A)
287            self.assertIsInstance(b, (A,))
288            self.assertIs(B1, B)
289            class C(B):
290                pass
291            c = C()
292            self.assertTrue(issubclass(C, A))
293            self.assertTrue(issubclass(C, (A,)))
294            self.assertIsInstance(c, A)
295            self.assertIsInstance(c, (A,))
296
297        def test_register_as_class_deco(self):
298            class A(metaclass=abc_ABCMeta):
299                pass
300            @A.register
301            class B(object):
302                pass
303            b = B()
304            self.assertTrue(issubclass(B, A))
305            self.assertTrue(issubclass(B, (A,)))
306            self.assertIsInstance(b, A)
307            self.assertIsInstance(b, (A,))
308            @A.register
309            class C(B):
310                pass
311            c = C()
312            self.assertTrue(issubclass(C, A))
313            self.assertTrue(issubclass(C, (A,)))
314            self.assertIsInstance(c, A)
315            self.assertIsInstance(c, (A,))
316            self.assertIs(C, A.register(C))
317
318        def test_isinstance_invalidation(self):
319            class A(metaclass=abc_ABCMeta):
320                pass
321            class B:
322                pass
323            b = B()
324            self.assertFalse(isinstance(b, A))
325            self.assertFalse(isinstance(b, (A,)))
326            token_old = abc_get_cache_token()
327            A.register(B)
328            token_new = abc_get_cache_token()
329            self.assertGreater(token_new, token_old)
330            self.assertTrue(isinstance(b, A))
331            self.assertTrue(isinstance(b, (A,)))
332
333        def test_registration_builtins(self):
334            class A(metaclass=abc_ABCMeta):
335                pass
336            A.register(int)
337            self.assertIsInstance(42, A)
338            self.assertIsInstance(42, (A,))
339            self.assertTrue(issubclass(int, A))
340            self.assertTrue(issubclass(int, (A,)))
341            class B(A):
342                pass
343            B.register(str)
344            class C(str): pass
345            self.assertIsInstance("", A)
346            self.assertIsInstance("", (A,))
347            self.assertTrue(issubclass(str, A))
348            self.assertTrue(issubclass(str, (A,)))
349            self.assertTrue(issubclass(C, A))
350            self.assertTrue(issubclass(C, (A,)))
351
352        def test_registration_edge_cases(self):
353            class A(metaclass=abc_ABCMeta):
354                pass
355            A.register(A)  # should pass silently
356            class A1(A):
357                pass
358            self.assertRaises(RuntimeError, A1.register, A)  # cycles not allowed
359            class B(object):
360                pass
361            A1.register(B)  # ok
362            A1.register(B)  # should pass silently
363            class C(A):
364                pass
365            A.register(C)  # should pass silently
366            self.assertRaises(RuntimeError, C.register, A)  # cycles not allowed
367            C.register(B)  # ok
368
369        def test_register_non_class(self):
370            class A(metaclass=abc_ABCMeta):
371                pass
372            self.assertRaisesRegex(TypeError, "Can only register classes",
373                                   A.register, 4)
374
375        def test_registration_transitiveness(self):
376            class A(metaclass=abc_ABCMeta):
377                pass
378            self.assertTrue(issubclass(A, A))
379            self.assertTrue(issubclass(A, (A,)))
380            class B(metaclass=abc_ABCMeta):
381                pass
382            self.assertFalse(issubclass(A, B))
383            self.assertFalse(issubclass(A, (B,)))
384            self.assertFalse(issubclass(B, A))
385            self.assertFalse(issubclass(B, (A,)))
386            class C(metaclass=abc_ABCMeta):
387                pass
388            A.register(B)
389            class B1(B):
390                pass
391            self.assertTrue(issubclass(B1, A))
392            self.assertTrue(issubclass(B1, (A,)))
393            class C1(C):
394                pass
395            B1.register(C1)
396            self.assertFalse(issubclass(C, B))
397            self.assertFalse(issubclass(C, (B,)))
398            self.assertFalse(issubclass(C, B1))
399            self.assertFalse(issubclass(C, (B1,)))
400            self.assertTrue(issubclass(C1, A))
401            self.assertTrue(issubclass(C1, (A,)))
402            self.assertTrue(issubclass(C1, B))
403            self.assertTrue(issubclass(C1, (B,)))
404            self.assertTrue(issubclass(C1, B1))
405            self.assertTrue(issubclass(C1, (B1,)))
406            C1.register(int)
407            class MyInt(int):
408                pass
409            self.assertTrue(issubclass(MyInt, A))
410            self.assertTrue(issubclass(MyInt, (A,)))
411            self.assertIsInstance(42, A)
412            self.assertIsInstance(42, (A,))
413
414        def test_issubclass_bad_arguments(self):
415            class A(metaclass=abc_ABCMeta):
416                pass
417
418            with self.assertRaises(TypeError):
419                issubclass({}, A)  # unhashable
420
421            with self.assertRaises(TypeError):
422                issubclass(42, A)  # No __mro__
423
424            # Python version supports any iterable as __mro__.
425            # But it's implementation detail and don't emulate it in C version.
426            class C:
427                __mro__ = 42  # __mro__ is not tuple
428
429            with self.assertRaises(TypeError):
430                issubclass(C(), A)
431
432            # bpo-34441: Check that issubclass() doesn't crash on bogus
433            # classes.
434            bogus_subclasses = [
435                None,
436                lambda x: [],
437                lambda: 42,
438                lambda: [42],
439            ]
440
441            for i, func in enumerate(bogus_subclasses):
442                class S(metaclass=abc_ABCMeta):
443                    __subclasses__ = func
444
445                with self.subTest(i=i):
446                    with self.assertRaises(TypeError):
447                        issubclass(int, S)
448
449            # Also check that issubclass() propagates exceptions raised by
450            # __subclasses__.
451            exc_msg = "exception from __subclasses__"
452
453            def raise_exc():
454                raise Exception(exc_msg)
455
456            class S(metaclass=abc_ABCMeta):
457                __subclasses__ = raise_exc
458
459            with self.assertRaisesRegex(Exception, exc_msg):
460                issubclass(int, S)
461
462        def test_subclasshook(self):
463            class A(metaclass=abc.ABCMeta):
464                @classmethod
465                def __subclasshook__(cls, C):
466                    if cls is A:
467                        return 'foo' in C.__dict__
468                    return NotImplemented
469            self.assertFalse(issubclass(A, A))
470            self.assertFalse(issubclass(A, (A,)))
471            class B:
472                foo = 42
473            self.assertTrue(issubclass(B, A))
474            self.assertTrue(issubclass(B, (A,)))
475            class C:
476                spam = 42
477            self.assertFalse(issubclass(C, A))
478            self.assertFalse(issubclass(C, (A,)))
479
480        def test_all_new_methods_are_called(self):
481            class A(metaclass=abc_ABCMeta):
482                pass
483            class B(object):
484                counter = 0
485                def __new__(cls):
486                    B.counter += 1
487                    return super().__new__(cls)
488            class C(A, B):
489                pass
490            self.assertEqual(B.counter, 0)
491            C()
492            self.assertEqual(B.counter, 1)
493
494        def test_ABC_has___slots__(self):
495            self.assertTrue(hasattr(abc.ABC, '__slots__'))
496
497        def test_tricky_new_works(self):
498            def with_metaclass(meta, *bases):
499                class metaclass(type):
500                    def __new__(cls, name, this_bases, d):
501                        return meta(name, bases, d)
502                return type.__new__(metaclass, 'temporary_class', (), {})
503            class A: ...
504            class B: ...
505            class C(with_metaclass(abc_ABCMeta, A, B)):
506                pass
507            self.assertEqual(C.__class__, abc_ABCMeta)
508
509        def test_update_del(self):
510            class A(metaclass=abc_ABCMeta):
511                @abc.abstractmethod
512                def foo(self):
513                    pass
514
515            del A.foo
516            self.assertEqual(A.__abstractmethods__, {'foo'})
517            self.assertFalse(hasattr(A, 'foo'))
518
519            abc.update_abstractmethods(A)
520
521            self.assertEqual(A.__abstractmethods__, set())
522            A()
523
524
525        def test_update_new_abstractmethods(self):
526            class A(metaclass=abc_ABCMeta):
527                @abc.abstractmethod
528                def bar(self):
529                    pass
530
531            @abc.abstractmethod
532            def updated_foo(self):
533                pass
534
535            A.foo = updated_foo
536            abc.update_abstractmethods(A)
537            self.assertEqual(A.__abstractmethods__, {'foo', 'bar'})
538            msg = "class A with abstract methods bar, foo"
539            self.assertRaisesRegex(TypeError, msg, A)
540
541        def test_update_implementation(self):
542            class A(metaclass=abc_ABCMeta):
543                @abc.abstractmethod
544                def foo(self):
545                    pass
546
547            class B(A):
548                pass
549
550            msg = "class B with abstract method foo"
551            self.assertRaisesRegex(TypeError, msg, B)
552            self.assertEqual(B.__abstractmethods__, {'foo'})
553
554            B.foo = lambda self: None
555
556            abc.update_abstractmethods(B)
557
558            B()
559            self.assertEqual(B.__abstractmethods__, set())
560
561        def test_update_as_decorator(self):
562            class A(metaclass=abc_ABCMeta):
563                @abc.abstractmethod
564                def foo(self):
565                    pass
566
567            def class_decorator(cls):
568                cls.foo = lambda self: None
569                return cls
570
571            @abc.update_abstractmethods
572            @class_decorator
573            class B(A):
574                pass
575
576            B()
577            self.assertEqual(B.__abstractmethods__, set())
578
579        def test_update_non_abc(self):
580            class A:
581                pass
582
583            @abc.abstractmethod
584            def updated_foo(self):
585                pass
586
587            A.foo = updated_foo
588            abc.update_abstractmethods(A)
589            A()
590            self.assertFalse(hasattr(A, '__abstractmethods__'))
591
592        def test_update_del_implementation(self):
593            class A(metaclass=abc_ABCMeta):
594                @abc.abstractmethod
595                def foo(self):
596                    pass
597
598            class B(A):
599                def foo(self):
600                    pass
601
602            B()
603
604            del B.foo
605
606            abc.update_abstractmethods(B)
607
608            msg = "class B with abstract method foo"
609            self.assertRaisesRegex(TypeError, msg, B)
610
611        def test_update_layered_implementation(self):
612            class A(metaclass=abc_ABCMeta):
613                @abc.abstractmethod
614                def foo(self):
615                    pass
616
617            class B(A):
618                pass
619
620            class C(B):
621                def foo(self):
622                    pass
623
624            C()
625
626            del C.foo
627
628            abc.update_abstractmethods(C)
629
630            msg = "class C with abstract method foo"
631            self.assertRaisesRegex(TypeError, msg, C)
632
633        def test_update_multi_inheritance(self):
634            class A(metaclass=abc_ABCMeta):
635                @abc.abstractmethod
636                def foo(self):
637                    pass
638
639            class B(metaclass=abc_ABCMeta):
640                def foo(self):
641                    pass
642
643            class C(B, A):
644                @abc.abstractmethod
645                def foo(self):
646                    pass
647
648            self.assertEqual(C.__abstractmethods__, {'foo'})
649
650            del C.foo
651
652            abc.update_abstractmethods(C)
653
654            self.assertEqual(C.__abstractmethods__, set())
655
656            C()
657
658
659    class TestABCWithInitSubclass(unittest.TestCase):
660        def test_works_with_init_subclass(self):
661            class abc_ABC(metaclass=abc_ABCMeta):
662                __slots__ = ()
663            saved_kwargs = {}
664            class ReceivesClassKwargs:
665                def __init_subclass__(cls, **kwargs):
666                    super().__init_subclass__()
667                    saved_kwargs.update(kwargs)
668            class Receiver(ReceivesClassKwargs, abc_ABC, x=1, y=2, z=3):
669                pass
670            self.assertEqual(saved_kwargs, dict(x=1, y=2, z=3))
671    return TestLegacyAPI, TestABC, TestABCWithInitSubclass
672
673TestLegacyAPI_Py, TestABC_Py, TestABCWithInitSubclass_Py = test_factory(abc.ABCMeta,
674                                                                        abc.get_cache_token)
675TestLegacyAPI_C, TestABC_C, TestABCWithInitSubclass_C = test_factory(_py_abc.ABCMeta,
676                                                                     _py_abc.get_cache_token)
677
678if __name__ == "__main__":
679    unittest.main()
680