|
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 |
|
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 # - 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(). |
|
90 class 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 |
|
138 class 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 |
|
149 class 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 |
|
158 AbstractSuper = AbstractClass(bases=()) |
|
159 |
|
160 AbstractChild = AbstractClass(bases=(AbstractSuper,)) |
|
161 |
|
162 # normal classes |
|
163 class Super: |
|
164 pass |
|
165 |
|
166 class Child(Super): |
|
167 pass |
|
168 |
|
169 # new-style classes |
|
170 class NewSuper(object): |
|
171 pass |
|
172 |
|
173 class NewChild(NewSuper): |
|
174 pass |
|
175 |
|
176 |
|
177 |
|
178 class 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 |
|
259 def 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 |
|
268 def test_main(): |
|
269 test_support.run_unittest( |
|
270 TestIsInstanceExceptions, |
|
271 TestIsSubclassExceptions, |
|
272 TestIsInstanceIsSubclass |
|
273 ) |
|
274 |
|
275 |
|
276 if __name__ == '__main__': |
|
277 test_main() |