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