Completed
Push — master ( 5587c9...783e16 )
by Max
14:55 queued 10:54
created

test_customdescriptors_with_abstractmethod_namespaced()   B

Complexity

Conditions 4

Size

Total Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 2 Features 0
Metric Value
c 7
b 2
f 0
dl 0
loc 59
rs 8.9846
cc 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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