blob: 9d37cff990338559b1fc2c19e938d323b77eb9ab [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
Maggie Moss1b4552c2020-09-09 13:23:24 -07007import typing
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -07008from test import support
Barry Warsaw906569d2002-04-23 22:48:42 +00009
10
11
Neil Schemenauer3b04d632002-04-24 03:33:02 +000012class TestIsInstanceExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +000013 # 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 #
Ezio Melotti3f5db392013-01-27 06:20:14 +020019 # - cls isn't a type, or a tuple
Barry Warsaw906569d2002-04-23 22:48:42 +000020 # - 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
R. David Murray6bb99892010-11-20 16:33:30 +000085 # check that we don't mask non AttributeErrors
86 # see: http://bugs.python.org/issue1574217
87 def test_isinstance_dont_mask_non_attribute_error(self):
88 class C(object):
89 def getclass(self):
Benjamin Peterson5c417872010-11-20 17:22:13 +000090 raise RuntimeError
91 __class__ = property(getclass)
R. David Murray6bb99892010-11-20 16:33:30 +000092
Benjamin Peterson5c417872010-11-20 17:22:13 +000093 c = C()
R. David Murray6bb99892010-11-20 16:33:30 +000094 self.assertRaises(RuntimeError, isinstance, c, bool)
95
96 # test another code path
97 class D: pass
98 self.assertRaises(RuntimeError, isinstance, c, D)
Barry Warsaw906569d2002-04-23 22:48:42 +000099
100
101# These tests are similar to above, but tickle certain code paths in
102# issubclass() instead of isinstance() -- really PyObject_IsSubclass()
103# vs. PyObject_IsInstance().
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000104class TestIsSubclassExceptions(unittest.TestCase):
Barry Warsaw906569d2002-04-23 22:48:42 +0000105 def test_dont_mask_non_attribute_error(self):
106 class C(object):
107 def getbases(self):
108 raise RuntimeError
109 __bases__ = property(getbases)
110
111 class S(C): pass
112
113 self.assertRaises(RuntimeError, issubclass, C(), S())
114
115 def test_mask_attribute_error(self):
116 class C(object):
117 def getbases(self):
118 raise AttributeError
119 __bases__ = property(getbases)
120
121 class S(C): pass
Tim Peters8ac14952002-05-23 15:15:30 +0000122
Barry Warsaw906569d2002-04-23 22:48:42 +0000123 self.assertRaises(TypeError, issubclass, C(), S())
124
125 # Like above, but test the second branch, where the __bases__ of the
126 # second arg (the cls arg) is tested. This means the first arg must
127 # return a valid __bases__, and it's okay for it to be a normal --
128 # unrelated by inheritance -- class.
129 def test_dont_mask_non_attribute_error_in_cls_arg(self):
130 class B: pass
131
132 class C(object):
133 def getbases(self):
134 raise RuntimeError
135 __bases__ = property(getbases)
136
137 self.assertRaises(RuntimeError, issubclass, B, C())
138
139 def test_mask_attribute_error_in_cls_arg(self):
140 class B: pass
141
142 class C(object):
143 def getbases(self):
144 raise AttributeError
145 __bases__ = property(getbases)
146
147 self.assertRaises(TypeError, issubclass, B, C())
148
149
150
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000151# meta classes for creating abstract classes and instances
152class AbstractClass(object):
153 def __init__(self, bases):
154 self.bases = bases
155
156 def getbases(self):
157 return self.bases
158 __bases__ = property(getbases)
159
160 def __call__(self):
161 return AbstractInstance(self)
162
163class AbstractInstance(object):
164 def __init__(self, klass):
165 self.klass = klass
166
167 def getclass(self):
168 return self.klass
169 __class__ = property(getclass)
170
171# abstract classes
172AbstractSuper = AbstractClass(bases=())
173
174AbstractChild = AbstractClass(bases=(AbstractSuper,))
175
176# normal classes
177class Super:
178 pass
179
180class Child(Super):
181 pass
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000182
183class TestIsInstanceIsSubclass(unittest.TestCase):
184 # Tests to ensure that isinstance and issubclass work on abstract
185 # classes and instances. Before the 2.2 release, TypeErrors were
186 # raised when boolean values should have been returned. The bug was
187 # triggered by mixing 'normal' classes and instances were with
188 # 'abstract' classes and instances. This case tries to test all
189 # combinations.
190
191 def test_isinstance_normal(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000192 # normal instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000193 self.assertEqual(True, isinstance(Super(), Super))
194 self.assertEqual(False, isinstance(Super(), Child))
195 self.assertEqual(False, isinstance(Super(), AbstractSuper))
196 self.assertEqual(False, isinstance(Super(), AbstractChild))
197
198 self.assertEqual(True, isinstance(Child(), Super))
199 self.assertEqual(False, isinstance(Child(), AbstractSuper))
200
201 def test_isinstance_abstract(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000202 # abstract instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000203 self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
204 self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
205 self.assertEqual(False, isinstance(AbstractSuper(), Super))
206 self.assertEqual(False, isinstance(AbstractSuper(), Child))
207
208 self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
209 self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
210 self.assertEqual(False, isinstance(AbstractChild(), Super))
211 self.assertEqual(False, isinstance(AbstractChild(), Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000212
Maggie Moss1b4552c2020-09-09 13:23:24 -0700213 def test_isinstance_with_or_union(self):
214 self.assertTrue(isinstance(Super(), Super | int))
215 self.assertFalse(isinstance(None, str | int))
216 self.assertTrue(isinstance(3, str | int))
217 self.assertTrue(isinstance("", str | int))
218 self.assertTrue(isinstance([], typing.List | typing.Tuple))
219 self.assertTrue(isinstance(2, typing.List | int))
220 self.assertFalse(isinstance(2, typing.List | typing.Tuple))
221 self.assertTrue(isinstance(None, int | None))
222 self.assertFalse(isinstance(3.14, int | str))
223 with self.assertRaises(TypeError):
224 isinstance(2, list[int])
225 with self.assertRaises(TypeError):
226 isinstance(2, list[int] | int)
227 with self.assertRaises(TypeError):
228 isinstance(2, int | str | list[int] | float)
229
230
231
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000232 def test_subclass_normal(self):
233 # normal classes
234 self.assertEqual(True, issubclass(Super, Super))
235 self.assertEqual(False, issubclass(Super, AbstractSuper))
236 self.assertEqual(False, issubclass(Super, Child))
237
238 self.assertEqual(True, issubclass(Child, Child))
239 self.assertEqual(True, issubclass(Child, Super))
240 self.assertEqual(False, issubclass(Child, AbstractSuper))
Maggie Moss1b4552c2020-09-09 13:23:24 -0700241 self.assertTrue(issubclass(typing.List, typing.List|typing.Tuple))
242 self.assertFalse(issubclass(int, typing.List|typing.Tuple))
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000243
244 def test_subclass_abstract(self):
245 # abstract classes
246 self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
247 self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
248 self.assertEqual(False, issubclass(AbstractSuper, Child))
249
250 self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
251 self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
252 self.assertEqual(False, issubclass(AbstractChild, Super))
253 self.assertEqual(False, issubclass(AbstractChild, Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000254
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000255 def test_subclass_tuple(self):
256 # test with a tuple as the second argument classes
257 self.assertEqual(True, issubclass(Child, (Child,)))
258 self.assertEqual(True, issubclass(Child, (Super,)))
259 self.assertEqual(False, issubclass(Super, (Child,)))
260 self.assertEqual(True, issubclass(Super, (Child, Super)))
261 self.assertEqual(False, issubclass(Child, ()))
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000262 self.assertEqual(True, issubclass(Super, (Child, (Super,))))
263
Guido van Rossume2a383d2007-01-15 16:59:06 +0000264 self.assertEqual(True, issubclass(int, (int, (float, int))))
Jim Fasarakis-Hilliard094909a2017-05-02 20:17:18 +0300265 self.assertEqual(True, issubclass(str, (str, (Child, str))))
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000266
Brett Cannon4f653312004-03-20 22:52:14 +0000267 def test_subclass_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400268 # make sure that issubclass raises RecursionError before the C stack is
Brett Cannon4f653312004-03-20 22:52:14 +0000269 # blown
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700270 with support.infinite_recursion():
271 self.assertRaises(RecursionError, blowstack, issubclass, str, str)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000272
Brett Cannon4f653312004-03-20 22:52:14 +0000273 def test_isinstance_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400274 # make sure that issubclass raises RecursionError before the C stack is
Tim Peters27f88362004-07-08 04:22:35 +0000275 # blown
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700276 with support.infinite_recursion():
277 self.assertRaises(RecursionError, blowstack, isinstance, '', str)
Brett Cannon4f653312004-03-20 22:52:14 +0000278
Maggie Moss1b4552c2020-09-09 13:23:24 -0700279 def test_subclass_with_union(self):
280 self.assertTrue(issubclass(int, int | float | int))
281 self.assertTrue(issubclass(str, str | Child | str))
282 self.assertFalse(issubclass(dict, float|str))
283 self.assertFalse(issubclass(object, float|str))
284 with self.assertRaises(TypeError):
285 issubclass(2, Child | Super)
286 with self.assertRaises(TypeError):
287 issubclass(int, list[int] | Child)
288
Yonatan Goldschmidt1c56f8f2020-02-22 15:11:48 +0200289 def test_issubclass_refcount_handling(self):
290 # bpo-39382: abstract_issubclass() didn't hold item reference while
291 # peeking in the bases tuple, in the single inheritance case.
292 class A:
293 @property
294 def __bases__(self):
295 return (int, )
296
297 class B:
298 def __init__(self):
299 # setting this here increases the chances of exhibiting the bug,
300 # probably due to memory layout changes.
301 self.x = 1
302
303 @property
304 def __bases__(self):
305 return (A(), )
306
307 self.assertEqual(True, issubclass(B(), int))
308
Serhiy Storchaka9ece9cd2020-10-05 00:55:57 +0300309 def test_infinite_recursion_in_bases(self):
310 class X:
311 @property
312 def __bases__(self):
313 return self.__bases__
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700314 with support.infinite_recursion():
315 self.assertRaises(RecursionError, issubclass, X(), int)
316 self.assertRaises(RecursionError, issubclass, int, X())
317 self.assertRaises(RecursionError, isinstance, 1, X())
Serhiy Storchaka9ece9cd2020-10-05 00:55:57 +0300318
Miss Islington (bot)f812fef2021-10-22 14:46:56 -0700319 def test_infinite_recursion_via_bases_tuple(self):
320 """Regression test for bpo-30570."""
321 class Failure(object):
322 def __getattr__(self, attr):
323 return (self, None)
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700324 with support.infinite_recursion():
325 with self.assertRaises(RecursionError):
326 issubclass(Failure(), int)
Miss Islington (bot)f812fef2021-10-22 14:46:56 -0700327
328 def test_infinite_cycle_in_bases(self):
329 """Regression test for bpo-30570."""
330 class X:
331 @property
332 def __bases__(self):
333 return (self, self, self)
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700334 with support.infinite_recursion():
335 self.assertRaises(RecursionError, issubclass, X(), int)
Miss Islington (bot)f812fef2021-10-22 14:46:56 -0700336
337 def test_infinitely_many_bases(self):
338 """Regression test for bpo-30570."""
339 class X:
340 def __getattr__(self, attr):
341 self.assertEqual(attr, "__bases__")
342 class A:
343 pass
344 class B:
345 pass
346 A.__getattr__ = B.__getattr__ = X.__getattr__
347 return (A(), B())
Miss Islington (bot)1f3ae5c2021-11-04 13:45:05 -0700348 with support.infinite_recursion():
349 self.assertRaises(RecursionError, issubclass, X(), int)
Miss Islington (bot)f812fef2021-10-22 14:46:56 -0700350
Yonatan Goldschmidt1c56f8f2020-02-22 15:11:48 +0200351
Brett Cannon4f653312004-03-20 22:52:14 +0000352def blowstack(fxn, arg, compare_to):
353 # Make sure that calling isinstance with a deeply nested tuple for its
Yury Selivanovf488fb42015-07-03 01:04:23 -0400354 # argument will raise RecursionError eventually.
Brett Cannon4f653312004-03-20 22:52:14 +0000355 tuple_arg = (compare_to,)
Guido van Rossum805365e2007-05-07 22:24:25 +0000356 for cnt in range(sys.getrecursionlimit()+5):
Brett Cannon4f653312004-03-20 22:52:14 +0000357 tuple_arg = (tuple_arg,)
358 fxn(arg, tuple_arg)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000359
360
Barry Warsaw906569d2002-04-23 22:48:42 +0000361if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500362 unittest.main()