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