# Tests some corner cases with isinstance() and issubclass(). While these # tests use new style classes and properties, they actually do whitebox # testing of error conditions uncovered when using extension types. import unittest import test_support class TestIsInstanceWhitebox(unittest.TestCase): # Test to make sure that an AttributeError when accessing the instance's # class's bases is masked. This was actually a bug in Python 2.2 and # 2.2.1 where the exception wasn't caught but it also wasn't being cleared # (leading to an "undetected error" in the debug build). Set up is, # isinstance(inst, cls) where: # # - inst isn't an InstanceType # - cls isn't a ClassType, a TypeType, or a TupleType # - cls has a __bases__ attribute # - inst has a __class__ attribute # - inst.__class__ as no __bases__ attribute # # Sounds complicated, I know, but this mimics a situation where an # extension type raises an AttributeError when its __bases__ attribute is # gotten. In that case, isinstance() should return False. def test_class_has_no_bases(self): class I(object): def getclass(self): # This must return an object that has no __bases__ attribute return None __class__ = property(getclass) class C(object): def getbases(self): return () __bases__ = property(getbases) self.assertEqual(False, isinstance(I(), C())) # Like above except that inst.__class__.__bases__ raises an exception # other than AttributeError def test_bases_raises_other_than_attribute_error(self): class E(object): def getbases(self): raise RuntimeError __bases__ = property(getbases) class I(object): def getclass(self): return E() __class__ = property(getclass) class C(object): def getbases(self): return () __bases__ = property(getbases) self.assertRaises(RuntimeError, isinstance, I(), C()) # Here's a situation where getattr(cls, '__bases__') raises an exception. # If that exception is not AttributeError, it should not get masked def test_dont_mask_non_attribute_error(self): class I: pass class C(object): def getbases(self): raise RuntimeError __bases__ = property(getbases) self.assertRaises(RuntimeError, isinstance, I(), C()) # Like above, except that getattr(cls, '__bases__') raises an # AttributeError, which /should/ get masked as a TypeError def test_mask_attribute_error(self): class I: pass class C(object): def getbases(self): raise AttributeError __bases__ = property(getbases) self.assertRaises(TypeError, isinstance, I(), C()) # These tests are similar to above, but tickle certain code paths in # issubclass() instead of isinstance() -- really PyObject_IsSubclass() # vs. PyObject_IsInstance(). class TestIsSubclassWhitebox(unittest.TestCase): def test_dont_mask_non_attribute_error(self): class C(object): def getbases(self): raise RuntimeError __bases__ = property(getbases) class S(C): pass self.assertRaises(RuntimeError, issubclass, C(), S()) def test_mask_attribute_error(self): class C(object): def getbases(self): raise AttributeError __bases__ = property(getbases) class S(C): pass self.assertRaises(TypeError, issubclass, C(), S()) # Like above, but test the second branch, where the __bases__ of the # second arg (the cls arg) is tested. This means the first arg must # return a valid __bases__, and it's okay for it to be a normal -- # unrelated by inheritance -- class. def test_dont_mask_non_attribute_error_in_cls_arg(self): class B: pass class C(object): def getbases(self): raise RuntimeError __bases__ = property(getbases) self.assertRaises(RuntimeError, issubclass, B, C()) def test_mask_attribute_error_in_cls_arg(self): class B: pass class C(object): def getbases(self): raise AttributeError __bases__ = property(getbases) self.assertRaises(TypeError, issubclass, B, C()) def test_main(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TestIsInstanceWhitebox)) suite.addTest(unittest.makeSuite(TestIsSubclassWhitebox)) test_support.run_suite(suite) if __name__ == '__main__': test_main()