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_abstractmethod_integration(self):
153            for abstractthing in [abc.abstractmethod, abc.abstractproperty,
154                                  abc.abstractclassmethod,
155                                  abc.abstractstaticmethod]:
156                class C(metaclass=abc_ABCMeta):
157                    @abstractthing
158                    def foo(self): pass  # abstract
159                    def bar(self): pass  # concrete
160                self.assertEqual(C.__abstractmethods__, {"foo"})
161                self.assertRaises(TypeError, C)  # because foo is abstract
162                self.assertTrue(isabstract(C))
163                class D(C):
164                    def bar(self): pass  # concrete override of concrete
165                self.assertEqual(D.__abstractmethods__, {"foo"})
166                self.assertRaises(TypeError, D)  # because foo is still abstract
167                self.assertTrue(isabstract(D))
168                class E(D):
169                    def foo(self): pass
170                self.assertEqual(E.__abstractmethods__, set())
171                E()  # now foo is concrete, too
172                self.assertFalse(isabstract(E))
173                class F(E):
174                    @abstractthing
175                    def bar(self): pass  # abstract override of concrete
176                self.assertEqual(F.__abstractmethods__, {"bar"})
177                self.assertRaises(TypeError, F)  # because bar is abstract now
178                self.assertTrue(isabstract(F))
179
180        def test_descriptors_with_abstractmethod(self):
181            class C(metaclass=abc_ABCMeta):
182                @property
183                @abc.abstractmethod
184                def foo(self): return 3
185                @foo.setter
186                @abc.abstractmethod
187                def foo(self, val): pass
188            self.assertRaises(TypeError, C)
189            class D(C):
190                @C.foo.getter
191                def foo(self): return super().foo
192            self.assertRaises(TypeError, D)
193            class E(D):
194                @D.foo.setter
195                def foo(self, val): pass
196            self.assertEqual(E().foo, 3)
197            # check that the property's __isabstractmethod__ descriptor does the
198            # right thing when presented with a value that fails truth testing:
199            class NotBool(object):
200                def __bool__(self):
201                    raise ValueError()
202                __len__ = __bool__
203            with self.assertRaises(ValueError):
204                class F(C):
205                    def bar(self):
206                        pass
207                    bar.__isabstractmethod__ = NotBool()
208                    foo = property(bar)
209
210
211        def test_customdescriptors_with_abstractmethod(self):
212            class Descriptor:
213                def __init__(self, fget, fset=None):
214                    self._fget = fget
215                    self._fset = fset
216                def getter(self, callable):
217                    return Descriptor(callable, self._fget)
218                def setter(self, callable):
219                    return Descriptor(self._fget, callable)
220                @property
221                def __isabstractmethod__(self):
222                    return (getattr(self._fget, '__isabstractmethod__', False)
223                            or getattr(self._fset, '__isabstractmethod__', False))
224            class C(metaclass=abc_ABCMeta):
225                @Descriptor
226                @abc.abstractmethod
227                def foo(self): return 3
228                @foo.setter
229                @abc.abstractmethod
230                def foo(self, val): pass
231            self.assertRaises(TypeError, C)
232            class D(C):
233                @C.foo.getter
234                def foo(self): return super().foo
235            self.assertRaises(TypeError, D)
236            class E(D):
237                @D.foo.setter
238                def foo(self, val): pass
239            self.assertFalse(E.foo.__isabstractmethod__)
240
241        def test_metaclass_abc(self):
242            # Metaclasses can be ABCs, too.
243            class A(metaclass=abc_ABCMeta):
244                @abc.abstractmethod
245                def x(self):
246                    pass
247            self.assertEqual(A.__abstractmethods__, {"x"})
248            class meta(type, A):
249                def x(self):
250                    return 1
251            class C(metaclass=meta):
252                pass
253
254        def test_registration_basics(self):
255            class A(metaclass=abc_ABCMeta):
256                pass
257            class B(object):
258                pass
259            b = B()
260            self.assertFalse(issubclass(B, A))
261            self.assertFalse(issubclass(B, (A,)))
262            self.assertNotIsInstance(b, A)
263            self.assertNotIsInstance(b, (A,))
264            B1 = A.register(B)
265            self.assertTrue(issubclass(B, A))
266            self.assertTrue(issubclass(B, (A,)))
267            self.assertIsInstance(b, A)
268            self.assertIsInstance(b, (A,))
269            self.assertIs(B1, B)
270            class C(B):
271                pass
272            c = C()
273            self.assertTrue(issubclass(C, A))
274            self.assertTrue(issubclass(C, (A,)))
275            self.assertIsInstance(c, A)
276            self.assertIsInstance(c, (A,))
277
278        def test_register_as_class_deco(self):
279            class A(metaclass=abc_ABCMeta):
280                pass
281            @A.register
282            class B(object):
283                pass
284            b = B()
285            self.assertTrue(issubclass(B, A))
286            self.assertTrue(issubclass(B, (A,)))
287            self.assertIsInstance(b, A)
288            self.assertIsInstance(b, (A,))
289            @A.register
290            class C(B):
291                pass
292            c = C()
293            self.assertTrue(issubclass(C, A))
294            self.assertTrue(issubclass(C, (A,)))
295            self.assertIsInstance(c, A)
296            self.assertIsInstance(c, (A,))
297            self.assertIs(C, A.register(C))
298
299        def test_isinstance_invalidation(self):
300            class A(metaclass=abc_ABCMeta):
301                pass
302            class B:
303                pass
304            b = B()
305            self.assertFalse(isinstance(b, A))
306            self.assertFalse(isinstance(b, (A,)))
307            token_old = abc_get_cache_token()
308            A.register(B)
309            token_new = abc_get_cache_token()
310            self.assertNotEqual(token_old, token_new)
311            self.assertTrue(isinstance(b, A))
312            self.assertTrue(isinstance(b, (A,)))
313
314        def test_registration_builtins(self):
315            class A(metaclass=abc_ABCMeta):
316                pass
317            A.register(int)
318            self.assertIsInstance(42, A)
319            self.assertIsInstance(42, (A,))
320            self.assertTrue(issubclass(int, A))
321            self.assertTrue(issubclass(int, (A,)))
322            class B(A):
323                pass
324            B.register(str)
325            class C(str): pass
326            self.assertIsInstance("", A)
327            self.assertIsInstance("", (A,))
328            self.assertTrue(issubclass(str, A))
329            self.assertTrue(issubclass(str, (A,)))
330            self.assertTrue(issubclass(C, A))
331            self.assertTrue(issubclass(C, (A,)))
332
333        def test_registration_edge_cases(self):
334            class A(metaclass=abc_ABCMeta):
335                pass
336            A.register(A)  # should pass silently
337            class A1(A):
338                pass
339            self.assertRaises(RuntimeError, A1.register, A)  # cycles not allowed
340            class B(object):
341                pass
342            A1.register(B)  # ok
343            A1.register(B)  # should pass silently
344            class C(A):
345                pass
346            A.register(C)  # should pass silently
347            self.assertRaises(RuntimeError, C.register, A)  # cycles not allowed
348            C.register(B)  # ok
349
350        def test_register_non_class(self):
351            class A(metaclass=abc_ABCMeta):
352                pass
353            self.assertRaisesRegex(TypeError, "Can only register classes",
354                                   A.register, 4)
355
356        def test_registration_transitiveness(self):
357            class A(metaclass=abc_ABCMeta):
358                pass
359            self.assertTrue(issubclass(A, A))
360            self.assertTrue(issubclass(A, (A,)))
361            class B(metaclass=abc_ABCMeta):
362                pass
363            self.assertFalse(issubclass(A, B))
364            self.assertFalse(issubclass(A, (B,)))
365            self.assertFalse(issubclass(B, A))
366            self.assertFalse(issubclass(B, (A,)))
367            class C(metaclass=abc_ABCMeta):
368                pass
369            A.register(B)
370            class B1(B):
371                pass
372            self.assertTrue(issubclass(B1, A))
373            self.assertTrue(issubclass(B1, (A,)))
374            class C1(C):
375                pass
376            B1.register(C1)
377            self.assertFalse(issubclass(C, B))
378            self.assertFalse(issubclass(C, (B,)))
379            self.assertFalse(issubclass(C, B1))
380            self.assertFalse(issubclass(C, (B1,)))
381            self.assertTrue(issubclass(C1, A))
382            self.assertTrue(issubclass(C1, (A,)))
383            self.assertTrue(issubclass(C1, B))
384            self.assertTrue(issubclass(C1, (B,)))
385            self.assertTrue(issubclass(C1, B1))
386            self.assertTrue(issubclass(C1, (B1,)))
387            C1.register(int)
388            class MyInt(int):
389                pass
390            self.assertTrue(issubclass(MyInt, A))
391            self.assertTrue(issubclass(MyInt, (A,)))
392            self.assertIsInstance(42, A)
393            self.assertIsInstance(42, (A,))
394
395        def test_issubclass_bad_arguments(self):
396            class A(metaclass=abc_ABCMeta):
397                pass
398
399            with self.assertRaises(TypeError):
400                issubclass({}, A)  # unhashable
401
402            with self.assertRaises(TypeError):
403                issubclass(42, A)  # No __mro__
404
405            # Python version supports any iterable as __mro__.
406            # But it's implementation detail and don't emulate it in C version.
407            class C:
408                __mro__ = 42  # __mro__ is not tuple
409
410            with self.assertRaises(TypeError):
411                issubclass(C(), A)
412
413            # bpo-34441: Check that issubclass() doesn't crash on bogus
414            # classes.
415            bogus_subclasses = [
416                None,
417                lambda x: [],
418                lambda: 42,
419                lambda: [42],
420            ]
421
422            for i, func in enumerate(bogus_subclasses):
423                class S(metaclass=abc_ABCMeta):
424                    __subclasses__ = func
425
426                with self.subTest(i=i):
427                    with self.assertRaises(TypeError):
428                        issubclass(int, S)
429
430            # Also check that issubclass() propagates exceptions raised by
431            # __subclasses__.
432            exc_msg = "exception from __subclasses__"
433
434            def raise_exc():
435                raise Exception(exc_msg)
436
437            class S(metaclass=abc_ABCMeta):
438                __subclasses__ = raise_exc
439
440            with self.assertRaisesRegex(Exception, exc_msg):
441                issubclass(int, S)
442
443        def test_all_new_methods_are_called(self):
444            class A(metaclass=abc_ABCMeta):
445                pass
446            class B(object):
447                counter = 0
448                def __new__(cls):
449                    B.counter += 1
450                    return super().__new__(cls)
451            class C(A, B):
452                pass
453            self.assertEqual(B.counter, 0)
454            C()
455            self.assertEqual(B.counter, 1)
456
457        def test_ABC_has___slots__(self):
458            self.assertTrue(hasattr(abc.ABC, '__slots__'))
459
460        def test_tricky_new_works(self):
461            def with_metaclass(meta, *bases):
462                class metaclass(type):
463                    def __new__(cls, name, this_bases, d):
464                        return meta(name, bases, d)
465                return type.__new__(metaclass, 'temporary_class', (), {})
466            class A: ...
467            class B: ...
468            class C(with_metaclass(abc_ABCMeta, A, B)):
469                pass
470            self.assertEqual(C.__class__, abc_ABCMeta)
471
472
473    class TestABCWithInitSubclass(unittest.TestCase):
474        def test_works_with_init_subclass(self):
475            class abc_ABC(metaclass=abc_ABCMeta):
476                __slots__ = ()
477            saved_kwargs = {}
478            class ReceivesClassKwargs:
479                def __init_subclass__(cls, **kwargs):
480                    super().__init_subclass__()
481                    saved_kwargs.update(kwargs)
482            class Receiver(ReceivesClassKwargs, abc_ABC, x=1, y=2, z=3):
483                pass
484            self.assertEqual(saved_kwargs, dict(x=1, y=2, z=3))
485    return TestLegacyAPI, TestABC, TestABCWithInitSubclass
486
487TestLegacyAPI_Py, TestABC_Py, TestABCWithInitSubclass_Py = test_factory(abc.ABCMeta,
488                                                                        abc.get_cache_token)
489TestLegacyAPI_C, TestABC_C, TestABCWithInitSubclass_C = test_factory(_py_abc.ABCMeta,
490                                                                     _py_abc.get_cache_token)
491
492if __name__ == "__main__":
493    unittest.main()
494