(root)/
Python-3.12.0/
Lib/
test/
test_isinstance.py
       1  # Tests some corner cases with isinstance() and issubclass().  While these
       2  # tests use new style classes and properties, they actually do whitebox
       3  # testing of error conditions uncovered when using extension types.
       4  
       5  import unittest
       6  import typing
       7  from test import support
       8  
       9  
      10  
      11  class ESC[4;38;5;81mTestIsInstanceExceptions(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      12      # Test to make sure that an AttributeError when accessing the instance's
      13      # class's bases is masked.  This was actually a bug in Python 2.2 and
      14      # 2.2.1 where the exception wasn't caught but it also wasn't being cleared
      15      # (leading to an "undetected error" in the debug build).  Set up is,
      16      # isinstance(inst, cls) where:
      17      #
      18      # - cls isn't a type, or a tuple
      19      # - cls has a __bases__ attribute
      20      # - inst has a __class__ attribute
      21      # - inst.__class__ as no __bases__ attribute
      22      #
      23      # Sounds complicated, I know, but this mimics a situation where an
      24      # extension type raises an AttributeError when its __bases__ attribute is
      25      # gotten.  In that case, isinstance() should return False.
      26      def test_class_has_no_bases(self):
      27          class ESC[4;38;5;81mI(ESC[4;38;5;149mobject):
      28              def getclass(self):
      29                  # This must return an object that has no __bases__ attribute
      30                  return None
      31              __class__ = property(getclass)
      32  
      33          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
      34              def getbases(self):
      35                  return ()
      36              __bases__ = property(getbases)
      37  
      38          self.assertEqual(False, isinstance(I(), C()))
      39  
      40      # Like above except that inst.__class__.__bases__ raises an exception
      41      # other than AttributeError
      42      def test_bases_raises_other_than_attribute_error(self):
      43          class ESC[4;38;5;81mE(ESC[4;38;5;149mobject):
      44              def getbases(self):
      45                  raise RuntimeError
      46              __bases__ = property(getbases)
      47  
      48          class ESC[4;38;5;81mI(ESC[4;38;5;149mobject):
      49              def getclass(self):
      50                  return E()
      51              __class__ = property(getclass)
      52  
      53          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
      54              def getbases(self):
      55                  return ()
      56              __bases__ = property(getbases)
      57  
      58          self.assertRaises(RuntimeError, isinstance, I(), C())
      59  
      60      # Here's a situation where getattr(cls, '__bases__') raises an exception.
      61      # If that exception is not AttributeError, it should not get masked
      62      def test_dont_mask_non_attribute_error(self):
      63          class ESC[4;38;5;81mI: pass
      64  
      65          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
      66              def getbases(self):
      67                  raise RuntimeError
      68              __bases__ = property(getbases)
      69  
      70          self.assertRaises(RuntimeError, isinstance, I(), C())
      71  
      72      # Like above, except that getattr(cls, '__bases__') raises an
      73      # AttributeError, which /should/ get masked as a TypeError
      74      def test_mask_attribute_error(self):
      75          class ESC[4;38;5;81mI: pass
      76  
      77          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
      78              def getbases(self):
      79                  raise AttributeError
      80              __bases__ = property(getbases)
      81  
      82          self.assertRaises(TypeError, isinstance, I(), C())
      83  
      84      # check that we don't mask non AttributeErrors
      85      # see: http://bugs.python.org/issue1574217
      86      def test_isinstance_dont_mask_non_attribute_error(self):
      87          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
      88              def getclass(self):
      89                  raise RuntimeError
      90              __class__ = property(getclass)
      91  
      92          c = C()
      93          self.assertRaises(RuntimeError, isinstance, c, bool)
      94  
      95          # test another code path
      96          class ESC[4;38;5;81mD: pass
      97          self.assertRaises(RuntimeError, isinstance, c, D)
      98  
      99  
     100  # These tests are similar to above, but tickle certain code paths in
     101  # issubclass() instead of isinstance() -- really PyObject_IsSubclass()
     102  # vs. PyObject_IsInstance().
     103  class ESC[4;38;5;81mTestIsSubclassExceptions(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     104      def test_dont_mask_non_attribute_error(self):
     105          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     106              def getbases(self):
     107                  raise RuntimeError
     108              __bases__ = property(getbases)
     109  
     110          class ESC[4;38;5;81mS(ESC[4;38;5;149mC): pass
     111  
     112          self.assertRaises(RuntimeError, issubclass, C(), S())
     113  
     114      def test_mask_attribute_error(self):
     115          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     116              def getbases(self):
     117                  raise AttributeError
     118              __bases__ = property(getbases)
     119  
     120          class ESC[4;38;5;81mS(ESC[4;38;5;149mC): pass
     121  
     122          self.assertRaises(TypeError, issubclass, C(), S())
     123  
     124      # Like above, but test the second branch, where the __bases__ of the
     125      # second arg (the cls arg) is tested.  This means the first arg must
     126      # return a valid __bases__, and it's okay for it to be a normal --
     127      # unrelated by inheritance -- class.
     128      def test_dont_mask_non_attribute_error_in_cls_arg(self):
     129          class ESC[4;38;5;81mB: pass
     130  
     131          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     132              def getbases(self):
     133                  raise RuntimeError
     134              __bases__ = property(getbases)
     135  
     136          self.assertRaises(RuntimeError, issubclass, B, C())
     137  
     138      def test_mask_attribute_error_in_cls_arg(self):
     139          class ESC[4;38;5;81mB: pass
     140  
     141          class ESC[4;38;5;81mC(ESC[4;38;5;149mobject):
     142              def getbases(self):
     143                  raise AttributeError
     144              __bases__ = property(getbases)
     145  
     146          self.assertRaises(TypeError, issubclass, B, C())
     147  
     148  
     149  
     150  # meta classes for creating abstract classes and instances
     151  class ESC[4;38;5;81mAbstractClass(ESC[4;38;5;149mobject):
     152      def __init__(self, bases):
     153          self.bases = bases
     154  
     155      def getbases(self):
     156          return self.bases
     157      __bases__ = property(getbases)
     158  
     159      def __call__(self):
     160          return AbstractInstance(self)
     161  
     162  class ESC[4;38;5;81mAbstractInstance(ESC[4;38;5;149mobject):
     163      def __init__(self, klass):
     164          self.klass = klass
     165  
     166      def getclass(self):
     167          return self.klass
     168      __class__ = property(getclass)
     169  
     170  # abstract classes
     171  AbstractSuper = AbstractClass(bases=())
     172  
     173  AbstractChild = AbstractClass(bases=(AbstractSuper,))
     174  
     175  # normal classes
     176  class ESC[4;38;5;81mSuper:
     177      pass
     178  
     179  class ESC[4;38;5;81mChild(ESC[4;38;5;149mSuper):
     180      pass
     181  
     182  class ESC[4;38;5;81mTestIsInstanceIsSubclass(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     183      # Tests to ensure that isinstance and issubclass work on abstract
     184      # classes and instances.  Before the 2.2 release, TypeErrors were
     185      # raised when boolean values should have been returned.  The bug was
     186      # triggered by mixing 'normal' classes and instances were with
     187      # 'abstract' classes and instances.  This case tries to test all
     188      # combinations.
     189  
     190      def test_isinstance_normal(self):
     191          # normal instances
     192          self.assertEqual(True, isinstance(Super(), Super))
     193          self.assertEqual(False, isinstance(Super(), Child))
     194          self.assertEqual(False, isinstance(Super(), AbstractSuper))
     195          self.assertEqual(False, isinstance(Super(), AbstractChild))
     196  
     197          self.assertEqual(True, isinstance(Child(), Super))
     198          self.assertEqual(False, isinstance(Child(), AbstractSuper))
     199  
     200      def test_isinstance_abstract(self):
     201          # abstract instances
     202          self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
     203          self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
     204          self.assertEqual(False, isinstance(AbstractSuper(), Super))
     205          self.assertEqual(False, isinstance(AbstractSuper(), Child))
     206  
     207          self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
     208          self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
     209          self.assertEqual(False, isinstance(AbstractChild(), Super))
     210          self.assertEqual(False, isinstance(AbstractChild(), Child))
     211  
     212      def test_isinstance_with_or_union(self):
     213          self.assertTrue(isinstance(Super(), Super | int))
     214          self.assertFalse(isinstance(None, str | int))
     215          self.assertTrue(isinstance(3, str | int))
     216          self.assertTrue(isinstance("", str | int))
     217          self.assertTrue(isinstance([], typing.List | typing.Tuple))
     218          self.assertTrue(isinstance(2, typing.List | int))
     219          self.assertFalse(isinstance(2, typing.List | typing.Tuple))
     220          self.assertTrue(isinstance(None, int | None))
     221          self.assertFalse(isinstance(3.14, int | str))
     222          with self.assertRaises(TypeError):
     223              isinstance(2, list[int])
     224          with self.assertRaises(TypeError):
     225              isinstance(2, list[int] | int)
     226          with self.assertRaises(TypeError):
     227              isinstance(2, float | str | list[int] | int)
     228  
     229  
     230  
     231      def test_subclass_normal(self):
     232          # normal classes
     233          self.assertEqual(True, issubclass(Super, Super))
     234          self.assertEqual(False, issubclass(Super, AbstractSuper))
     235          self.assertEqual(False, issubclass(Super, Child))
     236  
     237          self.assertEqual(True, issubclass(Child, Child))
     238          self.assertEqual(True, issubclass(Child, Super))
     239          self.assertEqual(False, issubclass(Child, AbstractSuper))
     240          self.assertTrue(issubclass(typing.List, typing.List|typing.Tuple))
     241          self.assertFalse(issubclass(int, typing.List|typing.Tuple))
     242  
     243      def test_subclass_abstract(self):
     244          # abstract classes
     245          self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
     246          self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
     247          self.assertEqual(False, issubclass(AbstractSuper, Child))
     248  
     249          self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
     250          self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
     251          self.assertEqual(False, issubclass(AbstractChild, Super))
     252          self.assertEqual(False, issubclass(AbstractChild, Child))
     253  
     254      def test_subclass_tuple(self):
     255          # test with a tuple as the second argument classes
     256          self.assertEqual(True, issubclass(Child, (Child,)))
     257          self.assertEqual(True, issubclass(Child, (Super,)))
     258          self.assertEqual(False, issubclass(Super, (Child,)))
     259          self.assertEqual(True, issubclass(Super, (Child, Super)))
     260          self.assertEqual(False, issubclass(Child, ()))
     261          self.assertEqual(True, issubclass(Super, (Child, (Super,))))
     262  
     263          self.assertEqual(True, issubclass(int, (int, (float, int))))
     264          self.assertEqual(True, issubclass(str, (str, (Child, str))))
     265  
     266      def test_subclass_recursion_limit(self):
     267          # make sure that issubclass raises RecursionError before the C stack is
     268          # blown
     269          with support.infinite_recursion():
     270              self.assertRaises(RecursionError, blowstack, issubclass, str, str)
     271  
     272      def test_isinstance_recursion_limit(self):
     273          # make sure that issubclass raises RecursionError before the C stack is
     274          # blown
     275          with support.infinite_recursion():
     276              self.assertRaises(RecursionError, blowstack, isinstance, '', str)
     277  
     278      def test_subclass_with_union(self):
     279          self.assertTrue(issubclass(int, int | float | int))
     280          self.assertTrue(issubclass(str, str | Child | str))
     281          self.assertFalse(issubclass(dict, float|str))
     282          self.assertFalse(issubclass(object, float|str))
     283          with self.assertRaises(TypeError):
     284              issubclass(2, Child | Super)
     285          with self.assertRaises(TypeError):
     286              issubclass(int, list[int] | Child)
     287  
     288      def test_issubclass_refcount_handling(self):
     289          # bpo-39382: abstract_issubclass() didn't hold item reference while
     290          # peeking in the bases tuple, in the single inheritance case.
     291          class ESC[4;38;5;81mA:
     292              @property
     293              def __bases__(self):
     294                  return (int, )
     295  
     296          class ESC[4;38;5;81mB:
     297              def __init__(self):
     298                  # setting this here increases the chances of exhibiting the bug,
     299                  # probably due to memory layout changes.
     300                  self.x = 1
     301  
     302              @property
     303              def __bases__(self):
     304                  return (A(), )
     305  
     306          self.assertEqual(True, issubclass(B(), int))
     307  
     308      def test_infinite_recursion_in_bases(self):
     309          class ESC[4;38;5;81mX:
     310              @property
     311              def __bases__(self):
     312                  return self.__bases__
     313          with support.infinite_recursion():
     314              self.assertRaises(RecursionError, issubclass, X(), int)
     315              self.assertRaises(RecursionError, issubclass, int, X())
     316              self.assertRaises(RecursionError, isinstance, 1, X())
     317  
     318      def test_infinite_recursion_via_bases_tuple(self):
     319          """Regression test for bpo-30570."""
     320          class ESC[4;38;5;81mFailure(ESC[4;38;5;149mobject):
     321              def __getattr__(self, attr):
     322                  return (self, None)
     323          with support.infinite_recursion():
     324              with self.assertRaises(RecursionError):
     325                  issubclass(Failure(), int)
     326  
     327      def test_infinite_cycle_in_bases(self):
     328          """Regression test for bpo-30570."""
     329          class ESC[4;38;5;81mX:
     330              @property
     331              def __bases__(self):
     332                  return (self, self, self)
     333          with support.infinite_recursion():
     334              self.assertRaises(RecursionError, issubclass, X(), int)
     335  
     336      def test_infinitely_many_bases(self):
     337          """Regression test for bpo-30570."""
     338          class ESC[4;38;5;81mX:
     339              def __getattr__(self, attr):
     340                  self.assertEqual(attr, "__bases__")
     341                  class ESC[4;38;5;81mA:
     342                      pass
     343                  class ESC[4;38;5;81mB:
     344                      pass
     345                  A.__getattr__ = B.__getattr__ = X.__getattr__
     346                  return (A(), B())
     347          with support.infinite_recursion():
     348              self.assertRaises(RecursionError, issubclass, X(), int)
     349  
     350  
     351  def blowstack(fxn, arg, compare_to):
     352      # Make sure that calling isinstance with a deeply nested tuple for its
     353      # argument will raise RecursionError eventually.
     354      tuple_arg = (compare_to,)
     355      for cnt in range(support.EXCEEDS_RECURSION_LIMIT):
     356          tuple_arg = (tuple_arg,)
     357          fxn(arg, tuple_arg)
     358  
     359  
     360  if __name__ == '__main__':
     361      unittest.main()