@@ -70,6 +70,25 @@ def foo(): return 4
7070
7171
7272 class TestABC (unittest .TestCase ):
73+ def check_isinstance (self , obj , target_class ):
74+ self .assertIsInstance (obj , target_class )
75+ self .assertIsInstance (obj , (target_class ,))
76+ self .assertIsInstance (obj , target_class | target_class )
77+
78+ def check_not_isinstance (self , obj , target_class ):
79+ self .assertNotIsInstance (obj , target_class )
80+ self .assertNotIsInstance (obj , (target_class ,))
81+ self .assertNotIsInstance (obj , target_class | target_class )
82+
83+ def check_issubclass (self , klass , target_class ):
84+ self .assertIsSubclass (klass , target_class )
85+ self .assertIsSubclass (klass , (target_class ,))
86+ self .assertIsSubclass (klass , target_class | target_class )
87+
88+ def check_not_issubclass (self , klass , target_class ):
89+ self .assertNotIsSubclass (klass , target_class )
90+ self .assertNotIsSubclass (klass , (target_class ,))
91+ self .assertNotIsSubclass (klass , target_class | target_class )
7392
7493 def test_ABC_helper (self ):
7594 # create an ABC using the helper class and perform basic checks
@@ -270,29 +289,75 @@ def x(self):
270289 class C (metaclass = meta ):
271290 pass
272291
292+ def test_isinstance_direct_inheritance (self ):
293+ class A (metaclass = abc_ABCMeta ):
294+ pass
295+ class B (A ):
296+ pass
297+ class C (A ):
298+ pass
299+
300+ a = A ()
301+ b = B ()
302+ c = C ()
303+ # trigger caching
304+ for _ in range (2 ):
305+ self .check_isinstance (a , A )
306+ self .check_not_isinstance (a , B )
307+ self .check_not_isinstance (a , C )
308+
309+ self .check_isinstance (b , B )
310+ self .check_isinstance (b , A )
311+ self .check_not_isinstance (b , C )
312+
313+ self .check_isinstance (c , C )
314+ self .check_isinstance (c , A )
315+ self .check_not_isinstance (c , B )
316+
317+ self .check_issubclass (B , A )
318+ self .check_issubclass (C , A )
319+ self .check_not_issubclass (B , C )
320+ self .check_not_issubclass (C , B )
321+ self .check_not_issubclass (A , B )
322+ self .check_not_issubclass (A , C )
323+
273324 def test_registration_basics (self ):
274325 class A (metaclass = abc_ABCMeta ):
275326 pass
276327 class B (object ):
277328 pass
329+
330+ a = A ()
278331 b = B ()
279- self .assertNotIsSubclass (B , A )
280- self .assertNotIsSubclass (B , (A ,))
281- self .assertNotIsInstance (b , A )
282- self .assertNotIsInstance (b , (A ,))
332+ # trigger caching
333+ for _ in range (2 ):
334+ self .check_not_issubclass (B , A )
335+ self .check_not_isinstance (b , A )
336+
337+ self .check_not_issubclass (A , B )
338+ self .check_not_isinstance (a , B )
339+
283340 B1 = A .register (B )
284- self .assertIsSubclass (B , A )
285- self .assertIsSubclass (B , (A ,))
286- self .assertIsInstance (b , A )
287- self .assertIsInstance (b , (A ,))
288- self .assertIs (B1 , B )
341+ # trigger caching
342+ for _ in range (2 ):
343+ self .check_issubclass (B , A )
344+ self .check_isinstance (b , A )
345+ self .assertIs (B1 , B )
346+
347+ self .check_not_issubclass (A , B )
348+ self .check_not_isinstance (a , B )
349+
289350 class C (B ):
290351 pass
352+
291353 c = C ()
292- self .assertIsSubclass (C , A )
293- self .assertIsSubclass (C , (A ,))
294- self .assertIsInstance (c , A )
295- self .assertIsInstance (c , (A ,))
354+ # trigger caching
355+ for _ in range (2 ):
356+ self .check_issubclass (C , A )
357+ self .check_isinstance (c , A )
358+
359+ self .check_not_issubclass (A , C )
360+ self .check_not_isinstance (a , C )
296361
297362 def test_register_as_class_deco (self ):
298363 class A (metaclass = abc_ABCMeta ):
@@ -377,39 +442,95 @@ class A(metaclass=abc_ABCMeta):
377442 pass
378443 self .assertIsSubclass (A , A )
379444 self .assertIsSubclass (A , (A ,))
445+
380446 class B (metaclass = abc_ABCMeta ):
381447 pass
382448 self .assertNotIsSubclass (A , B )
383449 self .assertNotIsSubclass (A , (B ,))
384450 self .assertNotIsSubclass (B , A )
385451 self .assertNotIsSubclass (B , (A ,))
452+
386453 class C (metaclass = abc_ABCMeta ):
387454 pass
388455 A .register (B )
389456 class B1 (B ):
390457 pass
391- self .assertIsSubclass (B1 , A )
392- self .assertIsSubclass (B1 , (A ,))
458+ # trigger caching
459+ for _ in range (2 ):
460+ self .assertIsSubclass (B1 , A )
461+ self .assertIsSubclass (B1 , (A ,))
462+
393463 class C1 (C ):
394464 pass
395465 B1 .register (C1 )
396- self .assertNotIsSubclass (C , B )
397- self .assertNotIsSubclass (C , (B ,))
398- self .assertNotIsSubclass (C , B1 )
399- self .assertNotIsSubclass (C , (B1 ,))
400- self .assertIsSubclass (C1 , A )
401- self .assertIsSubclass (C1 , (A ,))
402- self .assertIsSubclass (C1 , B )
403- self .assertIsSubclass (C1 , (B ,))
404- self .assertIsSubclass (C1 , B1 )
405- self .assertIsSubclass (C1 , (B1 ,))
466+ # trigger caching
467+ for _ in range (2 ):
468+ self .assertNotIsSubclass (C , B )
469+ self .assertNotIsSubclass (C , (B ,))
470+ self .assertNotIsSubclass (C , B1 )
471+ self .assertNotIsSubclass (C , (B1 ,))
472+ self .assertIsSubclass (C1 , A )
473+ self .assertIsSubclass (C1 , (A ,))
474+ self .assertIsSubclass (C1 , B )
475+ self .assertIsSubclass (C1 , (B ,))
476+ self .assertIsSubclass (C1 , B1 )
477+ self .assertIsSubclass (C1 , (B1 ,))
478+
406479 C1 .register (int )
407480 class MyInt (int ):
408481 pass
409- self .assertIsSubclass (MyInt , A )
410- self .assertIsSubclass (MyInt , (A ,))
411- self .assertIsInstance (42 , A )
412- self .assertIsInstance (42 , (A ,))
482+ # trigger caching
483+ for _ in range (2 ):
484+ self .assertIsSubclass (MyInt , A )
485+ self .assertIsSubclass (MyInt , (A ,))
486+ self .assertIsInstance (42 , A )
487+ self .assertIsInstance (42 , (A ,))
488+
489+ def test_custom_subclasses (self ):
490+ class A : pass
491+ class B (A ): pass
492+
493+ class C : pass
494+ class D (C ): pass
495+
496+ class Root (metaclass = abc_ABCMeta ): pass
497+
498+ class Parent1 (Root ):
499+ @classmethod
500+ def __subclasses__ (cls ):
501+ return [A ]
502+
503+ class Parent2 (Root ):
504+ __subclasses__ = lambda : [A ]
505+
506+ # trigger caching
507+ for _ in range (2 ):
508+ self .check_isinstance (A (), Parent1 )
509+ self .check_isinstance (B (), Parent1 )
510+ self .check_issubclass (A , Parent1 )
511+ self .check_issubclass (B , Parent1 )
512+ self .check_not_isinstance (C (), Parent1 )
513+ self .check_not_isinstance (D (), Parent1 )
514+ self .check_not_issubclass (C , Parent1 )
515+ self .check_not_issubclass (D , Parent1 )
516+
517+ self .check_isinstance (A (), Parent2 )
518+ self .check_isinstance (B (), Parent2 )
519+ self .check_issubclass (A , Parent2 )
520+ self .check_issubclass (B , Parent2 )
521+ self .check_not_isinstance (C (), Parent2 )
522+ self .check_not_isinstance (D (), Parent2 )
523+ self .check_not_issubclass (C , Parent2 )
524+ self .check_not_issubclass (D , Parent2 )
525+
526+ self .check_isinstance (A (), Root )
527+ self .check_isinstance (B (), Root )
528+ self .check_issubclass (A , Root )
529+ self .check_issubclass (B , Root )
530+ self .check_not_isinstance (C (), Root )
531+ self .check_not_isinstance (D (), Root )
532+ self .check_not_issubclass (C , Root )
533+ self .check_not_issubclass (D , Root )
413534
414535 def test_issubclass_bad_arguments (self ):
415536 class A (metaclass = abc_ABCMeta ):
@@ -460,8 +581,32 @@ class S(metaclass=abc_ABCMeta):
460581 with self .assertRaisesRegex (CustomError , exc_msg ):
461582 issubclass (int , S )
462583
463- def test_subclasshook (self ):
584+ def test_issubclass_bad_class (self ):
464585 class A (metaclass = abc .ABCMeta ):
586+ pass
587+
588+ A ._abc_impl = 1
589+ error_msg = "_abc_impl is set to a wrong type"
590+ with self .assertRaisesRegex (TypeError , error_msg ):
591+ issubclass (A , A )
592+
593+ class B (metaclass = _py_abc .ABCMeta ):
594+ pass
595+
596+ B ._abc_cache = 1
597+ error_msg = "argument of type 'int' is not a container or iterable"
598+ with self .assertRaisesRegex (TypeError , error_msg ):
599+ issubclass (B , B )
600+
601+ class C (metaclass = _py_abc .ABCMeta ):
602+ pass
603+
604+ C ._abc_negative_cache = 1
605+ with self .assertRaisesRegex (TypeError , error_msg ):
606+ issubclass (C , C )
607+
608+ def test_subclasshook (self ):
609+ class A (metaclass = abc_ABCMeta ):
465610 @classmethod
466611 def __subclasshook__ (cls , C ):
467612 if cls is A :
@@ -478,6 +623,26 @@ class C:
478623 self .assertNotIsSubclass (C , A )
479624 self .assertNotIsSubclass (C , (A ,))
480625
626+ def test_subclasshook_exception (self ):
627+ # Check that issubclass() propagates exceptions raised by
628+ # __subclasshook__.
629+ class CustomError (Exception ): ...
630+ exc_msg = "exception from __subclasshook__"
631+ class A (metaclass = abc_ABCMeta ):
632+ @classmethod
633+ def __subclasshook__ (cls , C ):
634+ raise CustomError (exc_msg )
635+ with self .assertRaisesRegex (CustomError , exc_msg ):
636+ issubclass (A , A )
637+ class B (A ):
638+ pass
639+ with self .assertRaisesRegex (CustomError , exc_msg ):
640+ issubclass (B , A )
641+ class C :
642+ pass
643+ with self .assertRaisesRegex (CustomError , exc_msg ):
644+ issubclass (C , A )
645+
481646 def test_all_new_methods_are_called (self ):
482647 class A (metaclass = abc_ABCMeta ):
483648 pass
@@ -522,7 +687,6 @@ def foo(self):
522687 self .assertEqual (A .__abstractmethods__ , set ())
523688 A ()
524689
525-
526690 def test_update_new_abstractmethods (self ):
527691 class A (metaclass = abc_ABCMeta ):
528692 @abc .abstractmethod
0 commit comments