blob: 25b0816713bba1d17922bbe627f326ad70a3aa89 [file] [log] [blame]
Barry Warsaw906569d2002-04-23 22:48:42 +00001# 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
Barry Warsaw04f357c2002-07-23 19:04:11 +00006from test import test_support
Brett Cannon4f653312004-03-20 22:52:14 +00007import sys
Barry Warsaw906569d2002-04-23 22:48:42 +00008
9
10
Neil Schemenauer3b04d632002-04-24 03:33:02 +000011class TestIsInstanceExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +000012 # 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().
Neil Schemenauer3b04d632002-04-24 03:33:02 +000090class TestIsSubclassExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +000091 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
Tim Peters8ac14952002-05-23 15:15:30 +0000108
Barry Warsaw906569d2002-04-23 22:48:42 +0000109 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
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000137# 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
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000169# new-style classes
170class NewSuper(object):
171 pass
172
173class NewChild(NewSuper):
174 pass
175
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000176
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):
Tim Peters8ac14952002-05-23 15:15:30 +0000187 # normal instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000188 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):
Tim Peters8ac14952002-05-23 15:15:30 +0000197 # abstract instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000198 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))
Tim Peters8ac14952002-05-23 15:15:30 +0000207
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000208 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))
Tim Peters8ac14952002-05-23 15:15:30 +0000228
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000229 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, ()))
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000236 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))))
Walter Dörwald4e41a4b2005-08-03 17:09:04 +0000246 if test_support.have_unicode:
247 self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring))))
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000248
Brett Cannon4f653312004-03-20 22:52:14 +0000249 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)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000253
Brett Cannon4f653312004-03-20 22:52:14 +0000254 def test_isinstance_recursion_limit(self):
255 # make sure that issubclass raises RuntimeError before the C stack is
Tim Peters27f88362004-07-08 04:22:35 +0000256 # blown
Brett Cannon4f653312004-03-20 22:52:14 +0000257 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)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000266
267
Barry Warsaw906569d2002-04-23 22:48:42 +0000268def test_main():
Walter Dörwald21d3a322003-05-01 17:45:56 +0000269 test_support.run_unittest(
270 TestIsInstanceExceptions,
271 TestIsSubclassExceptions,
272 TestIsInstanceIsSubclass
273 )
Barry Warsaw906569d2002-04-23 22:48:42 +0000274
275
276if __name__ == '__main__':
277 test_main()