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 from test import test_support
      7 import sys
      8 
      9 
     10 
     12 class TestIsInstanceExceptions(unittest.TestCase):
     13     # Test to make sure that an AttributeError when accessing the instance's
     14     # class's bases is masked.  This was actually a bug in Python 2.2 and
     15     # 2.2.1 where the exception wasn't caught but it also wasn't being cleared
     16     # (leading to an "undetected error" in the debug build).  Set up is,
     17     # isinstance(inst, cls) where:
     18     #
     19     # - inst isn't an InstanceType
     20     # - cls isn't a ClassType, a TypeType, or a TupleType
     21     # - cls has a __bases__ attribute
     22     # - inst has a __class__ attribute
     23     # - inst.__class__ as no __bases__ attribute
     24     #
     25     # Sounds complicated, I know, but this mimics a situation where an
     26     # extension type raises an AttributeError when its __bases__ attribute is
     27     # gotten.  In that case, isinstance() should return False.
     28     def test_class_has_no_bases(self):
     29         class I(object):
     30             def getclass(self):
     31                 # This must return an object that has no __bases__ attribute
     32                 return None
     33             __class__ = property(getclass)
     34 
     35         class C(object):
     36             def getbases(self):
     37                 return ()
     38             __bases__ = property(getbases)
     39 
     40         self.assertEqual(False, isinstance(I(), C()))
     41 
     42     # Like above except that inst.__class__.__bases__ raises an exception
     43     # other than AttributeError
     44     def test_bases_raises_other_than_attribute_error(self):
     45         class E(object):
     46             def getbases(self):
     47                 raise RuntimeError
     48             __bases__ = property(getbases)
     49 
     50         class I(object):
     51             def getclass(self):
     52                 return E()
     53             __class__ = property(getclass)
     54 
     55         class C(object):
     56             def getbases(self):
     57                 return ()
     58             __bases__ = property(getbases)
     59 
     60         self.assertRaises(RuntimeError, isinstance, I(), C())
     61 
     62     # Here's a situation where getattr(cls, '__bases__') raises an exception.
     63     # If that exception is not AttributeError, it should not get masked
     64     def test_dont_mask_non_attribute_error(self):
     65         class I: pass
     66 
     67         class C(object):
     68             def getbases(self):
     69                 raise RuntimeError
     70             __bases__ = property(getbases)
     71 
     72         self.assertRaises(RuntimeError, isinstance, I(), C())
     73 
     74     # Like above, except that getattr(cls, '__bases__') raises an
     75     # AttributeError, which /should/ get masked as a TypeError
     76     def test_mask_attribute_error(self):
     77         class I: pass
     78 
     79         class C(object):
     80             def getbases(self):
     81                 raise AttributeError
     82             __bases__ = property(getbases)
     83 
     84         self.assertRaises(TypeError, isinstance, I(), C())
     85 
     86 
     87 
     89 # These tests are similar to above, but tickle certain code paths in
     90 # issubclass() instead of isinstance() -- really PyObject_IsSubclass()
     91 # vs. PyObject_IsInstance().
     92 class TestIsSubclassExceptions(unittest.TestCase):
     93     def test_dont_mask_non_attribute_error(self):
     94         class C(object):
     95             def getbases(self):
     96                 raise RuntimeError
     97             __bases__ = property(getbases)
     98 
     99         class S(C): pass
    100 
    101         self.assertRaises(RuntimeError, issubclass, C(), S())
    102 
    103     def test_mask_attribute_error(self):
    104         class C(object):
    105             def getbases(self):
    106                 raise AttributeError
    107             __bases__ = property(getbases)
    108 
    109         class S(C): pass
    110 
    111         self.assertRaises(TypeError, issubclass, C(), S())
    112 
    113     # Like above, but test the second branch, where the __bases__ of the
    114     # second arg (the cls arg) is tested.  This means the first arg must
    115     # return a valid __bases__, and it's okay for it to be a normal --
    116     # unrelated by inheritance -- class.
    117     def test_dont_mask_non_attribute_error_in_cls_arg(self):
    118         class B: pass
    119 
    120         class C(object):
    121             def getbases(self):
    122                 raise RuntimeError
    123             __bases__ = property(getbases)
    124 
    125         self.assertRaises(RuntimeError, issubclass, B, C())
    126 
    127     def test_mask_attribute_error_in_cls_arg(self):
    128         class B: pass
    129 
    130         class C(object):
    131             def getbases(self):
    132                 raise AttributeError
    133             __bases__ = property(getbases)
    134 
    135         self.assertRaises(TypeError, issubclass, B, C())
    136 
    137 
    138 
    140 # meta classes for creating abstract classes and instances
    141 class AbstractClass(object):
    142     def __init__(self, bases):
    143         self.bases = bases
    144 
    145     def getbases(self):
    146         return self.bases
    147     __bases__ = property(getbases)
    148 
    149     def __call__(self):
    150         return AbstractInstance(self)
    151 
    152 class AbstractInstance(object):
    153     def __init__(self, klass):
    154         self.klass = klass
    155 
    156     def getclass(self):
    157         return self.klass
    158     __class__ = property(getclass)
    159 
    160 # abstract classes
    161 AbstractSuper = AbstractClass(bases=())
    162 
    163 AbstractChild = AbstractClass(bases=(AbstractSuper,))
    164 
    165 # normal classes
    166 class Super:
    167     pass
    168 
    169 class Child(Super):
    170     pass
    171 
    172 # new-style classes
    173 class NewSuper(object):
    174     pass
    175 
    176 class NewChild(NewSuper):
    177     pass
    178 
    179 
    180 
    182 class TestIsInstanceIsSubclass(unittest.TestCase):
    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_subclass_normal(self):
    213         # normal classes
    214         self.assertEqual(True, issubclass(Super, Super))
    215         self.assertEqual(False, issubclass(Super, AbstractSuper))
    216         self.assertEqual(False, issubclass(Super, Child))
    217 
    218         self.assertEqual(True, issubclass(Child, Child))
    219         self.assertEqual(True, issubclass(Child, Super))
    220         self.assertEqual(False, issubclass(Child, AbstractSuper))
    221 
    222     def test_subclass_abstract(self):
    223         # abstract classes
    224         self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
    225         self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
    226         self.assertEqual(False, issubclass(AbstractSuper, Child))
    227 
    228         self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
    229         self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
    230         self.assertEqual(False, issubclass(AbstractChild, Super))
    231         self.assertEqual(False, issubclass(AbstractChild, Child))
    232 
    233     def test_subclass_tuple(self):
    234         # test with a tuple as the second argument classes
    235         self.assertEqual(True, issubclass(Child, (Child,)))
    236         self.assertEqual(True, issubclass(Child, (Super,)))
    237         self.assertEqual(False, issubclass(Super, (Child,)))
    238         self.assertEqual(True, issubclass(Super, (Child, Super)))
    239         self.assertEqual(False, issubclass(Child, ()))
    240         self.assertEqual(True, issubclass(Super, (Child, (Super,))))
    241 
    242         self.assertEqual(True, issubclass(NewChild, (NewChild,)))
    243         self.assertEqual(True, issubclass(NewChild, (NewSuper,)))
    244         self.assertEqual(False, issubclass(NewSuper, (NewChild,)))
    245         self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper)))
    246         self.assertEqual(False, issubclass(NewChild, ()))
    247         self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,))))
    248 
    249         self.assertEqual(True, issubclass(int, (long, (float, int))))
    250         if test_support.have_unicode:
    251             self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring))))
    252 
    253     def test_subclass_recursion_limit(self):
    254         # make sure that issubclass raises RuntimeError before the C stack is
    255         # blown
    256         self.assertRaises(RuntimeError, blowstack, issubclass, str, str)
    257 
    258     def test_isinstance_recursion_limit(self):
    259         # make sure that issubclass raises RuntimeError before the C stack is
    260         # blown
    261         self.assertRaises(RuntimeError, blowstack, isinstance, '', str)
    262 
    263 def blowstack(fxn, arg, compare_to):
    264     # Make sure that calling isinstance with a deeply nested tuple for its
    265     # argument will raise RuntimeError eventually.
    266     tuple_arg = (compare_to,)
    267     for cnt in xrange(sys.getrecursionlimit()+5):
    268         tuple_arg = (tuple_arg,)
    269         fxn(arg, tuple_arg)
    270 
    271 
    273 def test_main():
    274     test_support.run_unittest(
    275         TestIsInstanceExceptions,
    276         TestIsSubclassExceptions,
    277         TestIsInstanceIsSubclass
    278     )
    279 
    280 
    281 if __name__ == '__main__':
    282     test_main()
    283