Home | History | Annotate | Download | only in test
      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 
      8 
      9 
     11 class TestIsInstanceExceptions(unittest.TestCase):
     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 I(object):
     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 C(object):
     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 E(object):
     44             def getbases(self):
     45                 raise RuntimeError
     46             __bases__ = property(getbases)
     47 
     48         class I(object):
     49             def getclass(self):
     50                 return E()
     51             __class__ = property(getclass)
     52 
     53         class C(object):
     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 I: pass
     64 
     65         class C(object):
     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 I: pass
     76 
     77         class C(object):
     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 C(object):
     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 D: pass
     97         self.assertRaises(RuntimeError, isinstance, c, D)
     98 
     99 
    101 # These tests are similar to above, but tickle certain code paths in
    102 # issubclass() instead of isinstance() -- really PyObject_IsSubclass()
    103 # vs. PyObject_IsInstance().
    104 class TestIsSubclassExceptions(unittest.TestCase):
    105     def test_dont_mask_non_attribute_error(self):
    106         class C(object):
    107             def getbases(self):
    108                 raise RuntimeError
    109             __bases__ = property(getbases)
    110 
    111         class S(C): pass
    112 
    113         self.assertRaises(RuntimeError, issubclass, C(), S())
    114 
    115     def test_mask_attribute_error(self):
    116         class C(object):
    117             def getbases(self):
    118                 raise AttributeError
    119             __bases__ = property(getbases)
    120 
    121         class S(C): pass
    122 
    123         self.assertRaises(TypeError, issubclass, C(), S())
    124 
    125     # Like above, but test the second branch, where the __bases__ of the
    126     # second arg (the cls arg) is tested.  This means the first arg must
    127     # return a valid __bases__, and it's okay for it to be a normal --
    128     # unrelated by inheritance -- class.
    129     def test_dont_mask_non_attribute_error_in_cls_arg(self):
    130         class B: pass
    131 
    132         class C(object):
    133             def getbases(self):
    134                 raise RuntimeError
    135             __bases__ = property(getbases)
    136 
    137         self.assertRaises(RuntimeError, issubclass, B, C())
    138 
    139     def test_mask_attribute_error_in_cls_arg(self):
    140         class B: pass
    141 
    142         class C(object):
    143             def getbases(self):
    144                 raise AttributeError
    145             __bases__ = property(getbases)
    146 
    147         self.assertRaises(TypeError, issubclass, B, C())
    148 
    149 
    150 
    152 # meta classes for creating abstract classes and instances
    153 class AbstractClass(object):
    154     def __init__(self, bases):
    155         self.bases = bases
    156 
    157     def getbases(self):
    158         return self.bases
    159     __bases__ = property(getbases)
    160 
    161     def __call__(self):
    162         return AbstractInstance(self)
    163 
    164 class AbstractInstance(object):
    165     def __init__(self, klass):
    166         self.klass = klass
    167 
    168     def getclass(self):
    169         return self.klass
    170     __class__ = property(getclass)
    171 
    172 # abstract classes
    173 AbstractSuper = AbstractClass(bases=())
    174 
    175 AbstractChild = AbstractClass(bases=(AbstractSuper,))
    176 
    177 # normal classes
    178 class Super:
    179     pass
    180 
    181 class Child(Super):
    182     pass
    183 
    184 # new-style classes
    185 class NewSuper(object):
    186     pass
    187 
    188 class NewChild(NewSuper):
    189     pass
    190 
    191 
    192 
    194 class TestIsInstanceIsSubclass(unittest.TestCase):
    195     # Tests to ensure that isinstance and issubclass work on abstract
    196     # classes and instances.  Before the 2.2 release, TypeErrors were
    197     # raised when boolean values should have been returned.  The bug was
    198     # triggered by mixing 'normal' classes and instances were with
    199     # 'abstract' classes and instances.  This case tries to test all
    200     # combinations.
    201 
    202     def test_isinstance_normal(self):
    203         # normal instances
    204         self.assertEqual(True, isinstance(Super(), Super))
    205         self.assertEqual(False, isinstance(Super(), Child))
    206         self.assertEqual(False, isinstance(Super(), AbstractSuper))
    207         self.assertEqual(False, isinstance(Super(), AbstractChild))
    208 
    209         self.assertEqual(True, isinstance(Child(), Super))
    210         self.assertEqual(False, isinstance(Child(), AbstractSuper))
    211 
    212     def test_isinstance_abstract(self):
    213         # abstract instances
    214         self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
    215         self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
    216         self.assertEqual(False, isinstance(AbstractSuper(), Super))
    217         self.assertEqual(False, isinstance(AbstractSuper(), Child))
    218 
    219         self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
    220         self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
    221         self.assertEqual(False, isinstance(AbstractChild(), Super))
    222         self.assertEqual(False, isinstance(AbstractChild(), Child))
    223 
    224     def test_subclass_normal(self):
    225         # normal classes
    226         self.assertEqual(True, issubclass(Super, Super))
    227         self.assertEqual(False, issubclass(Super, AbstractSuper))
    228         self.assertEqual(False, issubclass(Super, Child))
    229 
    230         self.assertEqual(True, issubclass(Child, Child))
    231         self.assertEqual(True, issubclass(Child, Super))
    232         self.assertEqual(False, issubclass(Child, AbstractSuper))
    233 
    234     def test_subclass_abstract(self):
    235         # abstract classes
    236         self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
    237         self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
    238         self.assertEqual(False, issubclass(AbstractSuper, Child))
    239 
    240         self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
    241         self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
    242         self.assertEqual(False, issubclass(AbstractChild, Super))
    243         self.assertEqual(False, issubclass(AbstractChild, Child))
    244 
    245     def test_subclass_tuple(self):
    246         # test with a tuple as the second argument classes
    247         self.assertEqual(True, issubclass(Child, (Child,)))
    248         self.assertEqual(True, issubclass(Child, (Super,)))
    249         self.assertEqual(False, issubclass(Super, (Child,)))
    250         self.assertEqual(True, issubclass(Super, (Child, Super)))
    251         self.assertEqual(False, issubclass(Child, ()))
    252         self.assertEqual(True, issubclass(Super, (Child, (Super,))))
    253 
    254         self.assertEqual(True, issubclass(NewChild, (NewChild,)))
    255         self.assertEqual(True, issubclass(NewChild, (NewSuper,)))
    256         self.assertEqual(False, issubclass(NewSuper, (NewChild,)))
    257         self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper)))
    258         self.assertEqual(False, issubclass(NewChild, ()))
    259         self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,))))
    260 
    261         self.assertEqual(True, issubclass(int, (int, (float, int))))
    262         self.assertEqual(True, issubclass(str, (str, (Child, NewChild, str))))
    263 
    264     def test_subclass_recursion_limit(self):
    265         # make sure that issubclass raises RecursionError before the C stack is
    266         # blown
    267         self.assertRaises(RecursionError, blowstack, issubclass, str, str)
    268 
    269     def test_isinstance_recursion_limit(self):
    270         # make sure that issubclass raises RecursionError before the C stack is
    271         # blown
    272         self.assertRaises(RecursionError, blowstack, isinstance, '', str)
    273 
    274 def blowstack(fxn, arg, compare_to):
    275     # Make sure that calling isinstance with a deeply nested tuple for its
    276     # argument will raise RecursionError eventually.
    277     tuple_arg = (compare_to,)
    278     for cnt in range(sys.getrecursionlimit()+5):
    279         tuple_arg = (tuple_arg,)
    280         fxn(arg, tuple_arg)
    281 
    282 
    284 if __name__ == '__main__':
    285     unittest.main()
    286