blob: 65751ab91685503ae4d8b9baf533c06e0c4374b3 [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
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000180
181class TestIsInstanceIsSubclass(unittest.TestCase):
182 # Tests to ensure that isinstance and issubclass work on abstract
183 # classes and instances. Before the 2.2 release, TypeErrors were
184 # raised when boolean values should have been returned. The bug was
185 # triggered by mixing 'normal' classes and instances were with
186 # 'abstract' classes and instances. This case tries to test all
187 # combinations.
188
189 def test_isinstance_normal(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000190 # normal instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000191 self.assertEqual(True, isinstance(Super(), Super))
192 self.assertEqual(False, isinstance(Super(), Child))
193 self.assertEqual(False, isinstance(Super(), AbstractSuper))
194 self.assertEqual(False, isinstance(Super(), AbstractChild))
195
196 self.assertEqual(True, isinstance(Child(), Super))
197 self.assertEqual(False, isinstance(Child(), AbstractSuper))
198
199 def test_isinstance_abstract(self):
Tim Peters8ac14952002-05-23 15:15:30 +0000200 # abstract instances
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000201 self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
202 self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
203 self.assertEqual(False, isinstance(AbstractSuper(), Super))
204 self.assertEqual(False, isinstance(AbstractSuper(), Child))
205
206 self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
207 self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
208 self.assertEqual(False, isinstance(AbstractChild(), Super))
209 self.assertEqual(False, isinstance(AbstractChild(), Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000210
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000211 def test_subclass_normal(self):
212 # normal classes
213 self.assertEqual(True, issubclass(Super, Super))
214 self.assertEqual(False, issubclass(Super, AbstractSuper))
215 self.assertEqual(False, issubclass(Super, Child))
216
217 self.assertEqual(True, issubclass(Child, Child))
218 self.assertEqual(True, issubclass(Child, Super))
219 self.assertEqual(False, issubclass(Child, AbstractSuper))
220
221 def test_subclass_abstract(self):
222 # abstract classes
223 self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
224 self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
225 self.assertEqual(False, issubclass(AbstractSuper, Child))
226
227 self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
228 self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
229 self.assertEqual(False, issubclass(AbstractChild, Super))
230 self.assertEqual(False, issubclass(AbstractChild, Child))
Tim Peters8ac14952002-05-23 15:15:30 +0000231
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000232 def test_subclass_tuple(self):
233 # test with a tuple as the second argument classes
234 self.assertEqual(True, issubclass(Child, (Child,)))
235 self.assertEqual(True, issubclass(Child, (Super,)))
236 self.assertEqual(False, issubclass(Super, (Child,)))
237 self.assertEqual(True, issubclass(Super, (Child, Super)))
238 self.assertEqual(False, issubclass(Child, ()))
Walter Dörwald7e5c6a02002-12-12 19:14:08 +0000239 self.assertEqual(True, issubclass(Super, (Child, (Super,))))
240
Guido van Rossume2a383d2007-01-15 16:59:06 +0000241 self.assertEqual(True, issubclass(int, (int, (float, int))))
Jim Fasarakis-Hilliard094909a2017-05-02 20:17:18 +0300242 self.assertEqual(True, issubclass(str, (str, (Child, str))))
Walter Dörwaldd9a6ad32002-12-12 16:41:44 +0000243
Brett Cannon4f653312004-03-20 22:52:14 +0000244 def test_subclass_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400245 # make sure that issubclass raises RecursionError before the C stack is
Brett Cannon4f653312004-03-20 22:52:14 +0000246 # blown
Yury Selivanovf488fb42015-07-03 01:04:23 -0400247 self.assertRaises(RecursionError, blowstack, issubclass, str, str)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000248
Brett Cannon4f653312004-03-20 22:52:14 +0000249 def test_isinstance_recursion_limit(self):
Yury Selivanovf488fb42015-07-03 01:04:23 -0400250 # make sure that issubclass raises RecursionError before the C stack is
Tim Peters27f88362004-07-08 04:22:35 +0000251 # blown
Yury Selivanovf488fb42015-07-03 01:04:23 -0400252 self.assertRaises(RecursionError, blowstack, isinstance, '', str)
Brett Cannon4f653312004-03-20 22:52:14 +0000253
254def blowstack(fxn, arg, compare_to):
255 # Make sure that calling isinstance with a deeply nested tuple for its
Yury Selivanovf488fb42015-07-03 01:04:23 -0400256 # argument will raise RecursionError eventually.
Brett Cannon4f653312004-03-20 22:52:14 +0000257 tuple_arg = (compare_to,)
Guido van Rossum805365e2007-05-07 22:24:25 +0000258 for cnt in range(sys.getrecursionlimit()+5):
Brett Cannon4f653312004-03-20 22:52:14 +0000259 tuple_arg = (tuple_arg,)
260 fxn(arg, tuple_arg)
Neil Schemenauer3b04d632002-04-24 03:33:02 +0000261
262
Barry Warsaw906569d2002-04-23 22:48:42 +0000263if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500264 unittest.main()