blob: e63d59b346a2acda5935459014dc6f1b382f5e42 [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
Brett Cannon4f653312004-03-20 22:52:14 +00006import sys
Barry Warsaw906569d2002-04-23 22:48:42 +00007
8
9
Neil Schemenauer3b04d632002-04-24 03:33:02 +000010class TestIsInstanceExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +000011 # Test to make sure that an AttributeError when accessing the instance's
12 # class's bases is masked. This was actually a bug in Python 2.2 and
13 # 2.2.1 where the exception wasn't caught but it also wasn't being cleared
14 # (leading to an "undetected error" in the debug build). Set up is,
15 # isinstance(inst, cls) where:
16 #
Ezio Melotti3f5db392013-01-27 06:20:14 +020017 # - cls isn't a type, or a tuple
Barry Warsaw906569d2002-04-23 22:48:42 +000018 # - cls has a __bases__ attribute
19 # - inst has a __class__ attribute
20 # - inst.__class__ as no __bases__ attribute
21 #
22 # Sounds complicated, I know, but this mimics a situation where an
23 # extension type raises an AttributeError when its __bases__ attribute is
24 # gotten. In that case, isinstance() should return False.
25 def test_class_has_no_bases(self):
26 class I(object):
27 def getclass(self):
28 # This must return an object that has no __bases__ attribute
29 return None
30 __class__ = property(getclass)
31
32 class C(object):
33 def getbases(self):
34 return ()
35 __bases__ = property(getbases)
36
37 self.assertEqual(False, isinstance(I(), C()))
38
39 # Like above except that inst.__class__.__bases__ raises an exception
40 # other than AttributeError
41 def test_bases_raises_other_than_attribute_error(self):
42 class E(object):
43 def getbases(self):
44 raise RuntimeError
45 __bases__ = property(getbases)
46
47 class I(object):
48 def getclass(self):
49 return E()
50 __class__ = property(getclass)
51
52 class C(object):
53 def getbases(self):
54 return ()
55 __bases__ = property(getbases)
56
57 self.assertRaises(RuntimeError, isinstance, I(), C())
58
59 # Here's a situation where getattr(cls, '__bases__') raises an exception.
60 # If that exception is not AttributeError, it should not get masked
61 def test_dont_mask_non_attribute_error(self):
62 class I: pass
63
64 class C(object):
65 def getbases(self):
66 raise RuntimeError
67 __bases__ = property(getbases)
68
69 self.assertRaises(RuntimeError, isinstance, I(), C())
70
71 # Like above, except that getattr(cls, '__bases__') raises an
72 # AttributeError, which /should/ get masked as a TypeError
73 def test_mask_attribute_error(self):
74 class I: pass
75
76 class C(object):
77 def getbases(self):
78 raise AttributeError
79 __bases__ = property(getbases)
80
81 self.assertRaises(TypeError, isinstance, I(), C())
82
R. David Murray6bb99892010-11-20 16:33:30 +000083 # check that we don't mask non AttributeErrors
84 # see: http://bugs.python.org/issue1574217
85 def test_isinstance_dont_mask_non_attribute_error(self):
86 class C(object):
87 def getclass(self):
Benjamin Peterson5c417872010-11-20 17:22:13 +000088 raise RuntimeError
89 __class__ = property(getclass)
R. David Murray6bb99892010-11-20 16:33:30 +000090
Benjamin Peterson5c417872010-11-20 17:22:13 +000091 c = C()
R. David Murray6bb99892010-11-20 16:33:30 +000092 self.assertRaises(RuntimeError, isinstance, c, bool)
93
94 # test another code path
95 class D: pass
96 self.assertRaises(RuntimeError, isinstance, c, D)
Barry Warsaw906569d2002-04-23 22:48:42 +000097
98
99# These tests are similar to above, but tickle certain code paths in
100# issubclass() instead of isinstance() -- really PyObject_IsSubclass()
101# vs. PyObject_IsInstance().
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000102class TestIsSubclassExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +0000103 def test_dont_mask_non_attribute_error(self):
104 class C(object):
105 def getbases(self):
106 raise RuntimeError
107 __bases__ = property(getbases)
108
109 class S(C): pass
110
111 self.assertRaises(RuntimeError, issubclass, C(), S())
112
113 def test_mask_attribute_error(self):
114 class C(object):
115 def getbases(self):
116 raise AttributeError
117 __bases__ = property(getbases)
118
119 class S(C): pass
Tim Peters8ac14952002-05-23 15:15:30 +0000120
Barry Warsaw906569d2002-04-23 22:48:42 +0000121 self.assertRaises(TypeError, issubclass, C(), S())
122
123 # Like above, but test the second branch, where the __bases__ of the
124 # second arg (the cls arg) is tested. This means the first arg must
125 # return a valid __bases__, and it's okay for it to be a normal --
126 # unrelated by inheritance -- class.
127 def test_dont_mask_non_attribute_error_in_cls_arg(self):
128 class B: pass
129
130 class C(object):
131 def getbases(self):
132 raise RuntimeError
133 __bases__ = property(getbases)
134
135 self.assertRaises(RuntimeError, issubclass, B, C())
136
137 def test_mask_attribute_error_in_cls_arg(self):
138 class B: pass
139
140 class C(object):
141 def getbases(self):
142 raise AttributeError
143 __bases__ = property(getbases)
144
145 self.assertRaises(TypeError, issubclass, B, C())
146
147
148
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000149# meta classes for creating abstract classes and instances
150class AbstractClass(object):
151 def __init__(self, bases):
152 self.bases = bases
153
154 def getbases(self):
155 return self.bases
156 __bases__ = property(getbases)
157
158 def __call__(self):
159 return AbstractInstance(self)
160
161class AbstractInstance(object):
162 def __init__(self, klass):
163 self.klass = klass
164
165 def getclass(self):
166 return self.klass
167 __class__ = property(getclass)
168
169# abstract classes
170AbstractSuper = AbstractClass(bases=())
171
172AbstractChild = AbstractClass(bases=(AbstractSuper,))
173
174# normal classes
175class Super:
176 pass
177
178class Child(Super):
179 pass
180
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000181# new-style classes
182class NewSuper(object):
183 pass
184
185class NewChild(NewSuper):
186 pass
187
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000188
189
190class TestIsInstanceIsSubclass(unittest.TestCase):
191 # Tests to ensure that isinstance and issubclass work on abstract
192 # classes and instances. Before the 2.2 release, TypeErrors were
193 # raised when boolean values should have been returned. The bug was
194 # triggered by mixing 'normal' classes and instances were with
195 # 'abstract' classes and instances. This case tries to test all
196 # combinations.
197
198 def test_isinstance_normal(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000199 # normal instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000200 self.assertEqual(True, isinstance(Super(), Super))
201 self.assertEqual(False, isinstance(Super(), Child))
202 self.assertEqual(False, isinstance(Super(), AbstractSuper))
203 self.assertEqual(False, isinstance(Super(), AbstractChild))
204
205 self.assertEqual(True, isinstance(Child(), Super))
206 self.assertEqual(False, isinstance(Child(), AbstractSuper))
207
208 def test_isinstance_abstract(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000209 # abstract instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000210 self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
211 self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
212 self.assertEqual(False, isinstance(AbstractSuper(), Super))
213 self.assertEqual(False, isinstance(AbstractSuper(), Child))
214
215 self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
216 self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
217 self.assertEqual(False, isinstance(AbstractChild(), Super))
218 self.assertEqual(False, isinstance(AbstractChild(), Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000219
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000220 def test_subclass_normal(self):
221 # normal classes
222 self.assertEqual(True, issubclass(Super, Super))
223 self.assertEqual(False, issubclass(Super, AbstractSuper))
224 self.assertEqual(False, issubclass(Super, Child))
225
226 self.assertEqual(True, issubclass(Child, Child))
227 self.assertEqual(True, issubclass(Child, Super))
228 self.assertEqual(False, issubclass(Child, AbstractSuper))
229
230 def test_subclass_abstract(self):
231 # abstract classes
232 self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
233 self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
234 self.assertEqual(False, issubclass(AbstractSuper, Child))
235
236 self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
237 self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
238 self.assertEqual(False, issubclass(AbstractChild, Super))
239 self.assertEqual(False, issubclass(AbstractChild, Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000240
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000241 def test_subclass_tuple(self):
242 # test with a tuple as the second argument classes
243 self.assertEqual(True, issubclass(Child, (Child,)))
244 self.assertEqual(True, issubclass(Child, (Super,)))
245 self.assertEqual(False, issubclass(Super, (Child,)))
246 self.assertEqual(True, issubclass(Super, (Child, Super)))
247 self.assertEqual(False, issubclass(Child, ()))
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000248 self.assertEqual(True, issubclass(Super, (Child, (Super,))))
249
250 self.assertEqual(True, issubclass(NewChild, (NewChild,)))
251 self.assertEqual(True, issubclass(NewChild, (NewSuper,)))
252 self.assertEqual(False, issubclass(NewSuper, (NewChild,)))
253 self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper)))
254 self.assertEqual(False, issubclass(NewChild, ()))
255 self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,))))
256
Guido van Rossume2a383d2007-01-15 16:59:06 +0000257 self.assertEqual(True, issubclass(int, (int, (float, int))))
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000258 self.assertEqual(True, issubclass(str, (str, (Child, NewChild, str))))
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000259
Brett Cannon4f653312004-03-20 22:52:14 +0000260 def test_subclass_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400261 # make sure that issubclass raises RecursionError before the C stack is
Brett Cannon4f653312004-03-20 22:52:14 +0000262 # blown
Yury Selivanovf488fb42015-07-03 01:04:23 -0400263 self.assertRaises(RecursionError, blowstack, issubclass, str, str)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000264
Brett Cannon4f653312004-03-20 22:52:14 +0000265 def test_isinstance_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400266 # make sure that issubclass raises RecursionError before the C stack is
Tim Peters27f88362004-07-08 04:22:35 +0000267 # blown
Yury Selivanovf488fb42015-07-03 01:04:23 -0400268 self.assertRaises(RecursionError, blowstack, isinstance, '', str)
Brett Cannon4f653312004-03-20 22:52:14 +0000269
270def blowstack(fxn, arg, compare_to):
271 # Make sure that calling isinstance with a deeply nested tuple for its
Yury Selivanovf488fb42015-07-03 01:04:23 -0400272 # argument will raise RecursionError eventually.
Brett Cannon4f653312004-03-20 22:52:14 +0000273 tuple_arg = (compare_to,)
Guido van Rossum805365e2007-05-07 22:24:25 +0000274 for cnt in range(sys.getrecursionlimit()+5):
Brett Cannon4f653312004-03-20 22:52:14 +0000275 tuple_arg = (tuple_arg,)
276 fxn(arg, tuple_arg)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000277
278
Barry Warsaw906569d2002-04-23 22:48:42 +0000279if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500280 unittest.main()