• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2007 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""Unit tests for abc.py."""
5
6import unittest
7from test import support
8
9import abc
10from inspect import isabstract
11
12
13class TestLegacyAPI(unittest.TestCase):
14
15    def test_abstractproperty_basics(self):
16        @abc.abstractproperty
17        def foo(self): pass
18        self.assertTrue(foo.__isabstractmethod__)
19        def bar(self): pass
20        self.assertFalse(hasattr(bar, "__isabstractmethod__"))
21
22        class C(metaclass=abc.ABCMeta):
23            @abc.abstractproperty
24            def foo(self): return 3
25        self.assertRaises(TypeError, C)
26        class D(C):
27            @property
28            def foo(self): return super().foo
29        self.assertEqual(D().foo, 3)
30        self.assertFalse(getattr(D.foo, "__isabstractmethod__", False))
31
32    def test_abstractclassmethod_basics(self):
33        @abc.abstractclassmethod
34        def foo(cls): pass
35        self.assertTrue(foo.__isabstractmethod__)
36        @classmethod
37        def bar(cls): pass
38        self.assertFalse(getattr(bar, "__isabstractmethod__", False))
39
40        class C(metaclass=abc.ABCMeta):
41            @abc.abstractclassmethod
42            def foo(cls): return cls.__name__
43        self.assertRaises(TypeError, C)
44        class D(C):
45            @classmethod
46            def foo(cls): return super().foo()
47        self.assertEqual(D.foo(), 'D')
48        self.assertEqual(D().foo(), 'D')
49
50    def test_abstractstaticmethod_basics(self):
51        @abc.abstractstaticmethod
52        def foo(): pass
53        self.assertTrue(foo.__isabstractmethod__)
54        @staticmethod
55        def bar(): pass
56        self.assertFalse(getattr(bar, "__isabstractmethod__", False))
57
58        class C(metaclass=abc.ABCMeta):
59            @abc.abstractstaticmethod
60            def foo(): return 3
61        self.assertRaises(TypeError, C)
62        class D(C):
63            @staticmethod
64            def foo(): return 4
65        self.assertEqual(D.foo(), 4)
66        self.assertEqual(D().foo(), 4)
67
68
69class TestABC(unittest.TestCase):
70
71    def test_ABC_helper(self):
72        # create an ABC using the helper class and perform basic checks
73        class C(abc.ABC):
74            @classmethod
75            @abc.abstractmethod
76            def foo(cls): return cls.__name__
77        self.assertEqual(type(C), abc.ABCMeta)
78        self.assertRaises(TypeError, C)
79        class D(C):
80            @classmethod
81            def foo(cls): return super().foo()
82        self.assertEqual(D.foo(), 'D')
83
84    def test_abstractmethod_basics(self):
85        @abc.abstractmethod
86        def foo(self): pass
87        self.assertTrue(foo.__isabstractmethod__)
88        def bar(self): pass
89        self.assertFalse(hasattr(bar, "__isabstractmethod__"))
90
91    def test_abstractproperty_basics(self):
92        @property
93        @abc.abstractmethod
94        def foo(self): pass
95        self.assertTrue(foo.__isabstractmethod__)
96        def bar(self): pass
97        self.assertFalse(getattr(bar, "__isabstractmethod__", False))
98
99        class C(metaclass=abc.ABCMeta):
100            @property
101            @abc.abstractmethod
102            def foo(self): return 3
103        self.assertRaises(TypeError, C)
104        class D(C):
105            @C.foo.getter
106            def foo(self): return super().foo
107        self.assertEqual(D().foo, 3)
108
109    def test_abstractclassmethod_basics(self):
110        @classmethod
111        @abc.abstractmethod
112        def foo(cls): pass
113        self.assertTrue(foo.__isabstractmethod__)
114        @classmethod
115        def bar(cls): pass
116        self.assertFalse(getattr(bar, "__isabstractmethod__", False))
117
118        class C(metaclass=abc.ABCMeta):
119            @classmethod
120            @abc.abstractmethod
121            def foo(cls): return cls.__name__
122        self.assertRaises(TypeError, C)
123        class D(C):
124            @classmethod
125            def foo(cls): return super().foo()
126        self.assertEqual(D.foo(), 'D')
127        self.assertEqual(D().foo(), 'D')
128
129    def test_abstractstaticmethod_basics(self):
130        @staticmethod
131        @abc.abstractmethod
132        def foo(): pass
133        self.assertTrue(foo.__isabstractmethod__)
134        @staticmethod
135        def bar(): pass
136        self.assertFalse(getattr(bar, "__isabstractmethod__", False))
137
138        class C(metaclass=abc.ABCMeta):
139            @staticmethod
140            @abc.abstractmethod
141            def foo(): return 3
142        self.assertRaises(TypeError, C)
143        class D(C):
144            @staticmethod
145            def foo(): return 4
146        self.assertEqual(D.foo(), 4)
147        self.assertEqual(D().foo(), 4)
148
149    def test_abstractmethod_integration(self):
150        for abstractthing in [abc.abstractmethod, abc.abstractproperty,
151                              abc.abstractclassmethod,
152                              abc.abstractstaticmethod]:
153            class C(metaclass=abc.ABCMeta):
154                @abstractthing
155                def foo(self): pass  # abstract
156                def bar(self): pass  # concrete
157            self.assertEqual(C.__abstractmethods__, {"foo"})
158            self.assertRaises(TypeError, C)  # because foo is abstract
159            self.assertTrue(isabstract(C))
160            class D(C):
161                def bar(self): pass  # concrete override of concrete
162            self.assertEqual(D.__abstractmethods__, {"foo"})
163            self.assertRaises(TypeError, D)  # because foo is still abstract
164            self.assertTrue(isabstract(D))
165            class E(D):
166                def foo(self): pass
167            self.assertEqual(E.__abstractmethods__, set())
168            E()  # now foo is concrete, too
169            self.assertFalse(isabstract(E))
170            class F(E):
171                @abstractthing
172                def bar(self): pass  # abstract override of concrete
173            self.assertEqual(F.__abstractmethods__, {"bar"})
174            self.assertRaises(TypeError, F)  # because bar is abstract now
175            self.assertTrue(isabstract(F))
176
177    def test_descriptors_with_abstractmethod(self):
178        class C(metaclass=abc.ABCMeta):
179            @property
180            @abc.abstractmethod
181            def foo(self): return 3
182            @foo.setter
183            @abc.abstractmethod
184            def foo(self, val): pass
185        self.assertRaises(TypeError, C)
186        class D(C):
187            @C.foo.getter
188            def foo(self): return super().foo
189        self.assertRaises(TypeError, D)
190        class E(D):
191            @D.foo.setter
192            def foo(self, val): pass
193        self.assertEqual(E().foo, 3)
194        # check that the property's __isabstractmethod__ descriptor does the
195        # right thing when presented with a value that fails truth testing:
196        class NotBool(object):
197            def __bool__(self):
198                raise ValueError()
199            __len__ = __bool__
200        with self.assertRaises(ValueError):
201            class F(C):
202                def bar(self):
203                    pass
204                bar.__isabstractmethod__ = NotBool()
205                foo = property(bar)
206
207
208    def test_customdescriptors_with_abstractmethod(self):
209        class Descriptor:
210            def __init__(self, fget, fset=None):
211                self._fget = fget
212                self._fset = fset
213            def getter(self, callable):
214                return Descriptor(callable, self._fget)
215            def setter(self, callable):
216                return Descriptor(self._fget, callable)
217            @property
218            def __isabstractmethod__(self):
219                return (getattr(self._fget, '__isabstractmethod__', False)
220                        or getattr(self._fset, '__isabstractmethod__', False))
221        class C(metaclass=abc.ABCMeta):
222            @Descriptor
223            @abc.abstractmethod
224            def foo(self): return 3
225            @foo.setter
226            @abc.abstractmethod
227            def foo(self, val): pass
228        self.assertRaises(TypeError, C)
229        class D(C):
230            @C.foo.getter
231            def foo(self): return super().foo
232        self.assertRaises(TypeError, D)
233        class E(D):
234            @D.foo.setter
235            def foo(self, val): pass
236        self.assertFalse(E.foo.__isabstractmethod__)
237
238    def test_metaclass_abc(self):
239        # Metaclasses can be ABCs, too.
240        class A(metaclass=abc.ABCMeta):
241            @abc.abstractmethod
242            def x(self):
243                pass
244        self.assertEqual(A.__abstractmethods__, {"x"})
245        class meta(type, A):
246            def x(self):
247                return 1
248        class C(metaclass=meta):
249            pass
250
251    def test_registration_basics(self):
252        class A(metaclass=abc.ABCMeta):
253            pass
254        class B(object):
255            pass
256        b = B()
257        self.assertFalse(issubclass(B, A))
258        self.assertFalse(issubclass(B, (A,)))
259        self.assertNotIsInstance(b, A)
260        self.assertNotIsInstance(b, (A,))
261        B1 = A.register(B)
262        self.assertTrue(issubclass(B, A))
263        self.assertTrue(issubclass(B, (A,)))
264        self.assertIsInstance(b, A)
265        self.assertIsInstance(b, (A,))
266        self.assertIs(B1, B)
267        class C(B):
268            pass
269        c = C()
270        self.assertTrue(issubclass(C, A))
271        self.assertTrue(issubclass(C, (A,)))
272        self.assertIsInstance(c, A)
273        self.assertIsInstance(c, (A,))
274
275    def test_register_as_class_deco(self):
276        class A(metaclass=abc.ABCMeta):
277            pass
278        @A.register
279        class B(object):
280            pass
281        b = B()
282        self.assertTrue(issubclass(B, A))
283        self.assertTrue(issubclass(B, (A,)))
284        self.assertIsInstance(b, A)
285        self.assertIsInstance(b, (A,))
286        @A.register
287        class C(B):
288            pass
289        c = C()
290        self.assertTrue(issubclass(C, A))
291        self.assertTrue(issubclass(C, (A,)))
292        self.assertIsInstance(c, A)
293        self.assertIsInstance(c, (A,))
294        self.assertIs(C, A.register(C))
295
296    def test_isinstance_invalidation(self):
297        class A(metaclass=abc.ABCMeta):
298            pass
299        class B:
300            pass
301        b = B()
302        self.assertFalse(isinstance(b, A))
303        self.assertFalse(isinstance(b, (A,)))
304        token_old = abc.get_cache_token()
305        A.register(B)
306        token_new = abc.get_cache_token()
307        self.assertNotEqual(token_old, token_new)
308        self.assertTrue(isinstance(b, A))
309        self.assertTrue(isinstance(b, (A,)))
310
311    def test_registration_builtins(self):
312        class A(metaclass=abc.ABCMeta):
313            pass
314        A.register(int)
315        self.assertIsInstance(42, A)
316        self.assertIsInstance(42, (A,))
317        self.assertTrue(issubclass(int, A))
318        self.assertTrue(issubclass(int, (A,)))
319        class B(A):
320            pass
321        B.register(str)
322        class C(str): pass
323        self.assertIsInstance("", A)
324        self.assertIsInstance("", (A,))
325        self.assertTrue(issubclass(str, A))
326        self.assertTrue(issubclass(str, (A,)))
327        self.assertTrue(issubclass(C, A))
328        self.assertTrue(issubclass(C, (A,)))
329
330    def test_registration_edge_cases(self):
331        class A(metaclass=abc.ABCMeta):
332            pass
333        A.register(A)  # should pass silently
334        class A1(A):
335            pass
336        self.assertRaises(RuntimeError, A1.register, A)  # cycles not allowed
337        class B(object):
338            pass
339        A1.register(B)  # ok
340        A1.register(B)  # should pass silently
341        class C(A):
342            pass
343        A.register(C)  # should pass silently
344        self.assertRaises(RuntimeError, C.register, A)  # cycles not allowed
345        C.register(B)  # ok
346
347    def test_register_non_class(self):
348        class A(metaclass=abc.ABCMeta):
349            pass
350        self.assertRaisesRegex(TypeError, "Can only register classes",
351                               A.register, 4)
352
353    def test_registration_transitiveness(self):
354        class A(metaclass=abc.ABCMeta):
355            pass
356        self.assertTrue(issubclass(A, A))
357        self.assertTrue(issubclass(A, (A,)))
358        class B(metaclass=abc.ABCMeta):
359            pass
360        self.assertFalse(issubclass(A, B))
361        self.assertFalse(issubclass(A, (B,)))
362        self.assertFalse(issubclass(B, A))
363        self.assertFalse(issubclass(B, (A,)))
364        class C(metaclass=abc.ABCMeta):
365            pass
366        A.register(B)
367        class B1(B):
368            pass
369        self.assertTrue(issubclass(B1, A))
370        self.assertTrue(issubclass(B1, (A,)))
371        class C1(C):
372            pass
373        B1.register(C1)
374        self.assertFalse(issubclass(C, B))
375        self.assertFalse(issubclass(C, (B,)))
376        self.assertFalse(issubclass(C, B1))
377        self.assertFalse(issubclass(C, (B1,)))
378        self.assertTrue(issubclass(C1, A))
379        self.assertTrue(issubclass(C1, (A,)))
380        self.assertTrue(issubclass(C1, B))
381        self.assertTrue(issubclass(C1, (B,)))
382        self.assertTrue(issubclass(C1, B1))
383        self.assertTrue(issubclass(C1, (B1,)))
384        C1.register(int)
385        class MyInt(int):
386            pass
387        self.assertTrue(issubclass(MyInt, A))
388        self.assertTrue(issubclass(MyInt, (A,)))
389        self.assertIsInstance(42, A)
390        self.assertIsInstance(42, (A,))
391
392    def test_all_new_methods_are_called(self):
393        class A(metaclass=abc.ABCMeta):
394            pass
395        class B(object):
396            counter = 0
397            def __new__(cls):
398                B.counter += 1
399                return super().__new__(cls)
400        class C(A, B):
401            pass
402        self.assertEqual(B.counter, 0)
403        C()
404        self.assertEqual(B.counter, 1)
405
406
407if __name__ == "__main__":
408    unittest.main()
409