Completed
Push — master ( bce456...f2be89 )
by Max
02:05
created

test_isinstance_invalidation()   B

Complexity

Conditions 6

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 6
c 2
b 1
f 0
dl 0
loc 17
rs 8
1
from abc import abstractmethod
2
from inspect import isabstract
3
import sys
4
5
import pytest
6
7
8
def test_import_compat(compat):
9
    """Test the module is importable. See conftest.py for the actual import."""
10
    assert compat
11
12
13
def test_import_abc(abc):
14
    """Test the module is importable. See conftest.py for the actual import."""
15
    assert abc
16
17
18
def test_has_class(abc):
19
    assert abc.NamespaceableABC
20
21
22
def test_ABC_helper(abc):
23
    # create an ABC using the helper class and perform basic checks
24
    class C(abc.NamespaceableABC):
25
        @classmethod
26
        @abstractmethod
27
        def foo(cls):
28
            return cls.__name__
29
    assert isinstance(C, type(abc.NamespaceableABC))
30
    with pytest.raises(TypeError):
31
        print(C())
32
33
    class D(C):
34
        @classmethod
35
        def foo(cls):
36
            return super().foo()
37
    assert D.foo() == 'D'
38
39
40
def test_abstractmethod_basics(abc):
41
    @abstractmethod
42
    def foo(self):
43
        pass
44
    assert foo.__isabstractmethod__
45
46
    def bar(self):
47
        pass
48
    assert not hasattr(bar, "__isabstractmethod__")
49
50
51 View Code Duplication
def test_abstractproperty_basics(abc):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
52
    @property
53
    @abstractmethod
54
    def foo(self):
55
        pass
56
    assert foo.__isabstractmethod__
57
58
    def bar(self):
59
        pass
60
    assert not getattr(bar, "__isabstractmethod__", False)
61
62
    class C(metaclass=type(abc.NamespaceableABC)):
63
        @property
64
        @abstractmethod
65
        def foo(self):
66
            return 3
67
    with pytest.raises(TypeError):
68
        print(C())
69
70
    class D(C):
71
        @C.foo.getter
72
        def foo(self):
73
            return super().foo
74
    assert D().foo == 3
75
76
77 View Code Duplication
def test_abstractclassmethod_basics(abc):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
78
    @classmethod
79
    @abstractmethod
80
    def foo(cls):
81
        pass
82
    assert foo.__isabstractmethod__
83
84
    @classmethod
85
    def bar(cls):
86
        pass
87
    assert not getattr(bar, "__isabstractmethod__", False)
88
89
    class C(metaclass=type(abc.NamespaceableABC)):
90
        @classmethod
91
        @abstractmethod
92
        def foo(cls):
93
            return cls.__name__
94
    with pytest.raises(TypeError):
95
        print(C())
96
97
    class D(C):
98
        @classmethod
99
        def foo(cls):
100
            return super().foo()
101
    assert D.foo() == 'D'
102
    assert D().foo() == 'D'
103
104
105
def test_abstractstaticmethod_basics(abc):
106
    @staticmethod
107
    @abstractmethod
108
    def foo():
109
        pass
110
    assert foo.__isabstractmethod__
111
112
    @staticmethod
113
    def bar():
114
        pass
115
    assert not (getattr(bar, "__isabstractmethod__", False))
116
117
    class C(metaclass=type(abc.NamespaceableABC)):
118
        @staticmethod
119
        @abstractmethod
120
        def foo():
121
            return 3
122
    with pytest.raises(TypeError):
123
        print(C())
124
125
    class D(C):
126
        @staticmethod
127
        def foo():
128
            return 4
129
    assert D.foo() == 4
130
    assert D().foo() == 4
131
132
133
def test_abstractmethod_integration(abc):
134
    for abstractthing in [abstractmethod, abc.abstractproperty,
135
                          abc.abstractclassmethod,
136
                          abc.abstractstaticmethod]:
137
        class C(metaclass=type(abc.NamespaceableABC)):
138
            @abstractthing
139
            def foo(self):
140
                pass  # abstract
141
142
            def bar(self):
143
                pass  # concrete
144
        assert C.__abstractmethods__ == {"foo"}
145
        with pytest.raises(TypeError):
146
            print(C())  # because foo is abstract
147
        assert isabstract(C)
148
149
        class D(C):
150
            def bar(self):
151
                pass  # concrete override of concrete
152
        assert D.__abstractmethods__ == {"foo"}
153
        with pytest.raises(TypeError):
154
            print(D())  # because foo is still abstract
155
        assert isabstract(D)
156
157
        class E(D):
158
            def foo(self):
159
                pass
160
        assert E.__abstractmethods__ == set()
161
        E()  # now foo is concrete, too
162
        assert not isabstract(E)
163
164
        class F(E):
165
            @abstractthing
166
            def bar(self):
167
                pass  # abstract override of concrete
168
        assert F.__abstractmethods__ == {"bar"}
169
        with pytest.raises(TypeError):
170
            print(F())  # because bar is abstract now
171
        assert isabstract(F)
172
173
174
def test_descriptors_with_abstractmethod(abc):
175
    class C(metaclass=type(abc.NamespaceableABC)):
176
        @property
177
        @abstractmethod
178
        def foo(self):
179
            return 3
180
181
        @foo.setter
182
        @abstractmethod
183
        def foo(self, val):
184
            pass
185
    with pytest.raises(TypeError):
186
        print(C())
187
188
    class D(C):
189
        @C.foo.getter
190
        def foo(self):
191
            return super().foo
192
    with pytest.raises(TypeError):
193
        print(D())
194
195
    class E(D):
196
        @D.foo.setter
197
        def foo(self, val):
198
            pass
199
    assert E().foo == 3
200
    # check that the property's __isabstractmethod__ descriptor does the
201
    # right thing when presented with a value that fails truth testing:
202
203
    class NotBool(object):
204
        def __bool__(self):
205
            raise ValueError()
206
        __len__ = __bool__
207
    with pytest.raises(ValueError):
208
        class F(C):
209
            def bar(self):
210
                pass
211
            bar.__isabstractmethod__ = NotBool()
212
            foo = property(bar)
213
214
215
def test_customdescriptors_with_abstractmethod(abc):
216
    class Descriptor:
217
        def __init__(self, fget, fset=None):
218
            self._fget = fget
219
            self._fset = fset
220
221
        def getter(self, callable):
222
            return Descriptor(callable, self._fget)
223
224
        def setter(self, callable):
225
            return Descriptor(self._fget, callable)
226
227
        @property
228
        def __isabstractmethod__(self):
229
            return (getattr(self._fget, '__isabstractmethod__', False) or
230
                    getattr(self._fset, '__isabstractmethod__', False))
231
232
    class C(metaclass=type(abc.NamespaceableABC)):
233
        @Descriptor
234
        @abstractmethod
235
        def foo(self):
236
            return 3
237
238
        @foo.setter
239
        @abstractmethod
240
        def foo(self, val):
241
            pass
242
    with pytest.raises(TypeError):
243
        print(C())
244
245
    class D(C):
246
        @C.foo.getter
247
        def foo(self):
248
            return super().foo
249
    with pytest.raises(TypeError):
250
        print(D())
251
252
    class E(D):
253
        @D.foo.setter
254
        def foo(self, val):
255
            pass
256
    assert not (E.foo.__isabstractmethod__)
257
258
259
def test_metaclass_abc(abc):
260
    # Metaclasses can be ABCs, too.
261
    class A(metaclass=type(abc.NamespaceableABC)):
262
        @abstractmethod
263
        def x(self):
264
            pass
265
    assert A.__abstractmethods__ == {"x"}
266
267
    class meta(type, A):
268
        def x(self):
269
            return 1
270
271
    class C(metaclass=meta):
272
        pass
273
274
275
def test_registration_basics(abc):
276
    class A(metaclass=type(abc.NamespaceableABC)):
277
        pass
278
279
    class B(object):
280
        pass
281
    b = B()
282
    assert not (issubclass(B, A))
283
    assert not (issubclass(B, (A,)))
284
    assert not isinstance(b, A)
285
    assert not isinstance(b, (A,))
286
    B1 = A.register(B)
287
    assert issubclass(B, A)
288
    assert issubclass(B, (A,))
289
    assert isinstance(b, A)
290
    assert isinstance(b, (A,))
291
    assert B1 is B
292
293
    class C(B):
294
        pass
295
    c = C()
296
    assert issubclass(C, A)
297
    assert issubclass(C, (A,))
298
    assert isinstance(c, A)
299
    assert isinstance(c, (A,))
300
301
302
def test_register_as_class_deco(abc):
303
    class A(metaclass=type(abc.NamespaceableABC)):
304
        pass
305
306
    @A.register
307
    class B(object):
308
        pass
309
    b = B()
310
    assert issubclass(B, A)
311
    assert issubclass(B, (A,))
312
    assert isinstance(b, A)
313
    assert isinstance(b, (A,))
314
315
    @A.register
316
    class C(B):
317
        pass
318
    c = C()
319
    assert issubclass(C, A)
320
    assert issubclass(C, (A,))
321
    assert isinstance(c, A)
322
    assert isinstance(c, (A,))
323
    assert C is A.register(C)
324
325
326
@pytest.mark.xfail(sys.version_info < (3, 4),
327
                   reason="python3.4 api changes?", strict=True)
328
def test_isinstance_invalidation(abc):
329
    class A(metaclass=type(abc.NamespaceableABC)):
330
        pass
331
332
    class B:
333
        pass
334
    b = B()
335
    assert not (isinstance(b, A))
336
    assert not (isinstance(b, (A,)))
337
    token_old = abc.get_cache_token()
338
    A.register(B)
339
    token_new = abc.get_cache_token()
340
    assert token_old != token_new
341
    assert isinstance(b, A)
342
    assert isinstance(b, (A,))
343
344
345
def test_registration_builtins(abc):
346
    class A(metaclass=type(abc.NamespaceableABC)):
347
        pass
348
    A.register(int)
349
    assert isinstance(42, A)
350
    assert isinstance(42, (A,))
351
    assert issubclass(int, A)
352
    assert issubclass(int, (A,))
353
354
    class B(A):
355
        pass
356
    B.register(str)
357
358
    class C(str):
359
        pass
360
    assert isinstance("", A)
361
    assert isinstance("", (A,))
362
    assert issubclass(str, A)
363
    assert issubclass(str, (A,))
364
    assert issubclass(C, A)
365
    assert issubclass(C, (A,))
366
367
368
def test_registration_edge_cases(abc):
369
    class A(metaclass=type(abc.NamespaceableABC)):
370
        pass
371
    A.register(A)  # should pass silently
372
373
    class A1(A):
374
        pass
375
    with pytest.raises(RuntimeError):
376
        A1.register(A)  # cycles not allowed
377
378
    class B(object):
379
        pass
380
    A1.register(B)  # ok
381
    A1.register(B)  # should pass silently
382
383
    class C(A):
384
        pass
385
    A.register(C)  # should pass silently
386
    with pytest.raises(RuntimeError):
387
        C.register(A)  # cycles not allowed
388
    C.register(B)  # ok
389
390
391
def test_register_non_class(abc):
392
    class A(metaclass=type(abc.NamespaceableABC)):
393
        pass
394
    with pytest.raises(TypeError, message="Can only register classes"):
395
        print(A.register(4))
396
397
398
def test_registration_transitiveness(abc):
399
    class A(metaclass=type(abc.NamespaceableABC)):
400
        pass
401
    assert issubclass(A, A)
402
    assert issubclass(A, (A,))
403
404
    class B(metaclass=type(abc.NamespaceableABC)):
405
        pass
406
    assert not (issubclass(A, B))
407
    assert not (issubclass(A, (B,)))
408
    assert not (issubclass(B, A))
409
    assert not (issubclass(B, (A,)))
410
411
    class C(metaclass=type(abc.NamespaceableABC)):
412
        pass
413
    A.register(B)
414
415
    class B1(B):
416
        pass
417
    assert issubclass(B1, A)
418
    assert issubclass(B1, (A,))
419
420
    class C1(C):
421
        pass
422
    B1.register(C1)
423
    assert not issubclass(C, B)
424
    assert not issubclass(C, (B,))
425
    assert not issubclass(C, B1)
426
    assert not issubclass(C, (B1,))
427
    assert issubclass(C1, A)
428
    assert issubclass(C1, (A,))
429
    assert issubclass(C1, B)
430
    assert issubclass(C1, (B,))
431
    assert issubclass(C1, B1)
432
    assert issubclass(C1, (B1,))
433
    C1.register(int)
434
435
    class MyInt(int):
436
        pass
437
    assert issubclass(MyInt, A)
438
    assert issubclass(MyInt, (A,))
439
    assert isinstance(42, A)
440
    assert isinstance(42, (A,))
441
442
443
def test_all_new_methods_are_called(abc):
444
    class A(metaclass=type(abc.NamespaceableABC)):
445
        pass
446
447
    class B(object):
448
        counter = 0
449
450
        def __new__(cls):
451
            B.counter += 1
452
            return super().__new__(cls)
453
454
    class C(A, B):
455
        pass
456
    assert B.counter == 0
457
    C()
458
    assert B.counter == 1
459