(root)/
Python-3.11.7/
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 sys
       7  import typing
       8  from test import support
       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  # meta classes for creating abstract classes and instances
     150  class ESC[4;38;5;81mAbstractClass(ESC[4;38;5;149mobject):
     151      def __init__(self, bases):
     152          self.bases = bases
     153  
     154      def getbases(self):
     155          return self.bases
     156      __bases__ = property(getbases)
     157  
     158      def __call__(self):
     159          return AbstractInstance(self)
     160  
     161  class ESC[4;38;5;81mAbstractInstance(ESC[4;38;5;149mobject):
     162      def __init__(self, klass):
     163          self.klass = klass
     164  
     165      def getclass(self):
     166          return self.klass
     167      __class__ = property(getclass)
     168  
     169  # abstract classes
     170  AbstractSuper = AbstractClass(bases=())
     171  
     172  AbstractChild = AbstractClass(bases=(AbstractSuper,))
     173  
     174  # normal classes
     175  class ESC[4;38;5;81mSuper:
     176      pass
     177  
     178  class ESC[4;38;5;81mChild(ESC[4;38;5;149mSuper):
     179      pass
     180  
     181  class ESC[4;38;5;81mTestIsInstanceIsSubclass(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     182      # Tests to ensure that isinstance and issubclass work on abstract
     183      # classes and instances.  Before the 2.2 release, TypeErrors were
     184      # raised when boolean values should have been returned.  The bug was
     185      # triggered by mixing 'normal' classes and instances were with
     186      # 'abstract' classes and instances.  This case tries to test all
     187      # combinations.
     188  
     189      def test_isinstance_normal(self):
     190          # normal instances
     191          self.assertEqual(True, isinstance(Super(), Super))
     192          self.assertEqual(False, isinstance(Super(), Child))
     193          self.assertEqual(False, isinstance(Super(), AbstractSuper))
     194          self.assertEqual(False, isinstance(Super(), AbstractChild))
     195  
     196          self.assertEqual(True, isinstance(Child(), Super))
     197          self.assertEqual(False, isinstance(Child(), AbstractSuper))
     198  
     199      def test_isinstance_abstract(self):
     200          # abstract instances
     201          self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
     202          self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
     203          self.assertEqual(False, isinstance(AbstractSuper(), Super))
     204          self.assertEqual(False, isinstance(AbstractSuper(), Child))
     205  
     206          self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
     207          self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
     208          self.assertEqual(False, isinstance(AbstractChild(), Super))
     209          self.assertEqual(False, isinstance(AbstractChild(), Child))
     210  
     211      def test_isinstance_with_or_union(self):
     212          self.assertTrue(isinstance(Super(), Super | int))
     213          self.assertFalse(isinstance(None, str | int))
     214          self.assertTrue(isinstance(3, str | int))
     215          self.assertTrue(isinstance("", str | int))
     216          self.assertTrue(isinstance([], typing.List | typing.Tuple))
     217          self.assertTrue(isinstance(2, typing.List | int))
     218          self.assertFalse(isinstance(2, typing.List | typing.Tuple))
     219          self.assertTrue(isinstance(None, int | None))
     220          self.assertFalse(isinstance(3.14, int | str))
     221          with self.assertRaises(TypeError):
     222              isinstance(2, list[int])
     223          with self.assertRaises(TypeError):
     224              isinstance(2, list[int] | int)
     225          with self.assertRaises(TypeError):
     226              isinstance(2, float | str | list[int] | int)
     227  
     228  
     229  
     230      def test_subclass_normal(self):
     231          # normal classes
     232          self.assertEqual(True, issubclass(Super, Super))
     233          self.assertEqual(False, issubclass(Super, AbstractSuper))
     234          self.assertEqual(False, issubclass(Super, Child))
     235  
     236          self.assertEqual(True, issubclass(Child, Child))
     237          self.assertEqual(True, issubclass(Child, Super))
     238          self.assertEqual(False, issubclass(Child, AbstractSuper))
     239          self.assertTrue(issubclass(typing.List, typing.List|typing.Tuple))
     240          self.assertFalse(issubclass(int, typing.List|typing.Tuple))
     241  
     242      def test_subclass_abstract(self):
     243          # abstract classes
     244          self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
     245          self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
     246          self.assertEqual(False, issubclass(AbstractSuper, Child))
     247  
     248          self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
     249          self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
     250          self.assertEqual(False, issubclass(AbstractChild, Super))
     251          self.assertEqual(False, issubclass(AbstractChild, Child))
     252  
     253      def test_subclass_tuple(self):
     254          # test with a tuple as the second argument classes
     255          self.assertEqual(True, issubclass(Child, (Child,)))
     256          self.assertEqual(True, issubclass(Child, (Super,)))
     257          self.assertEqual(False, issubclass(Super, (Child,)))
     258          self.assertEqual(True, issubclass(Super, (Child, Super)))
     259          self.assertEqual(False, issubclass(Child, ()))
     260          self.assertEqual(True, issubclass(Super, (Child, (Super,))))
     261  
     262          self.assertEqual(True, issubclass(int, (int, (float, int))))
     263          self.assertEqual(True, issubclass(str, (str, (Child, str))))
     264  
     265      def test_subclass_recursion_limit(self):
     266          # make sure that issubclass raises RecursionError before the C stack is
     267          # blown
     268          with support.infinite_recursion():
     269              self.assertRaises(RecursionError, blowstack, issubclass, str, str)
     270  
     271      def test_isinstance_recursion_limit(self):
     272          # make sure that issubclass raises RecursionError before the C stack is
     273          # blown
     274          with support.infinite_recursion():
     275              self.assertRaises(RecursionError, blowstack, isinstance, '', str)
     276  
     277      def test_subclass_with_union(self):
     278          self.assertTrue(issubclass(int, int | float | int))
     279          self.assertTrue(issubclass(str, str | Child | str))
     280          self.assertFalse(issubclass(dict, float|str))
     281          self.assertFalse(issubclass(object, float|str))
     282          with self.assertRaises(TypeError):
     283              issubclass(2, Child | Super)
     284          with self.assertRaises(TypeError):
     285              issubclass(int, list[int] | Child)
     286  
     287      def test_issubclass_refcount_handling(self):
     288          # bpo-39382: abstract_issubclass() didn't hold item reference while
     289          # peeking in the bases tuple, in the single inheritance case.
     290          class ESC[4;38;5;81mA:
     291              @property
     292              def __bases__(self):
     293                  return (int, )
     294  
     295          class ESC[4;38;5;81mB:
     296              def __init__(self):
     297                  # setting this here increases the chances of exhibiting the bug,
     298                  # probably due to memory layout changes.
     299                  self.x = 1
     300  
     301              @property
     302              def __bases__(self):
     303                  return (A(), )
     304  
     305          self.assertEqual(True, issubclass(B(), int))
     306  
     307      def test_infinite_recursion_in_bases(self):
     308          class ESC[4;38;5;81mX:
     309              @property
     310              def __bases__(self):
     311                  return self.__bases__
     312          with support.infinite_recursion():
     313              self.assertRaises(RecursionError, issubclass, X(), int)
     314              self.assertRaises(RecursionError, issubclass, int, X())
     315              self.assertRaises(RecursionError, isinstance, 1, X())
     316  
     317      def test_infinite_recursion_via_bases_tuple(self):
     318          """Regression test for bpo-30570."""
     319          class ESC[4;38;5;81mFailure(ESC[4;38;5;149mobject):
     320              def __getattr__(self, attr):
     321                  return (self, None)
     322          with support.infinite_recursion():
     323              with self.assertRaises(RecursionError):
     324                  issubclass(Failure(), int)
     325  
     326      def test_infinite_cycle_in_bases(self):
     327          """Regression test for bpo-30570."""
     328          class ESC[4;38;5;81mX:
     329              @property
     330              def __bases__(self):
     331                  return (self, self, self)
     332          with support.infinite_recursion():
     333              self.assertRaises(RecursionError, issubclass, X(), int)
     334  
     335      def test_infinitely_many_bases(self):
     336          """Regression test for bpo-30570."""
     337          class ESC[4;38;5;81mX:
     338              def __getattr__(self, attr):
     339                  self.assertEqual(attr, "__bases__")
     340                  class ESC[4;38;5;81mA:
     341                      pass
     342                  class ESC[4;38;5;81mB:
     343                      pass
     344                  A.__getattr__ = B.__getattr__ = X.__getattr__
     345                  return (A(), B())
     346          with support.infinite_recursion():
     347              self.assertRaises(RecursionError, issubclass, X(), int)
     348  
     349  
     350  def blowstack(fxn, arg, compare_to):
     351      # Make sure that calling isinstance with a deeply nested tuple for its
     352      # argument will raise RecursionError eventually.
     353      tuple_arg = (compare_to,)
     354      for cnt in range(sys.getrecursionlimit()+5):
     355          tuple_arg = (tuple_arg,)
     356          fxn(arg, tuple_arg)
     357  
     358  
     359  if __name__ == '__main__':
     360      unittest.main()