test_isinstance.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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. import unittest
  5. from test import test_support
  6. import sys
  7. class TestIsInstanceExceptions(unittest.TestCase):
  8. # Test to make sure that an AttributeError when accessing the instance's
  9. # class's bases is masked. This was actually a bug in Python 2.2 and
  10. # 2.2.1 where the exception wasn't caught but it also wasn't being cleared
  11. # (leading to an "undetected error" in the debug build). Set up is,
  12. # isinstance(inst, cls) where:
  13. #
  14. # - inst isn't an InstanceType
  15. # - cls isn't a ClassType, a TypeType, or a TupleType
  16. # - cls has a __bases__ attribute
  17. # - inst has a __class__ attribute
  18. # - inst.__class__ as no __bases__ attribute
  19. #
  20. # Sounds complicated, I know, but this mimics a situation where an
  21. # extension type raises an AttributeError when its __bases__ attribute is
  22. # gotten. In that case, isinstance() should return False.
  23. def test_class_has_no_bases(self):
  24. class I(object):
  25. def getclass(self):
  26. # This must return an object that has no __bases__ attribute
  27. return None
  28. __class__ = property(getclass)
  29. class C(object):
  30. def getbases(self):
  31. return ()
  32. __bases__ = property(getbases)
  33. self.assertEqual(False, isinstance(I(), C()))
  34. # Like above except that inst.__class__.__bases__ raises an exception
  35. # other than AttributeError
  36. def test_bases_raises_other_than_attribute_error(self):
  37. class E(object):
  38. def getbases(self):
  39. raise RuntimeError
  40. __bases__ = property(getbases)
  41. class I(object):
  42. def getclass(self):
  43. return E()
  44. __class__ = property(getclass)
  45. class C(object):
  46. def getbases(self):
  47. return ()
  48. __bases__ = property(getbases)
  49. self.assertRaises(RuntimeError, isinstance, I(), C())
  50. # Here's a situation where getattr(cls, '__bases__') raises an exception.
  51. # If that exception is not AttributeError, it should not get masked
  52. def test_dont_mask_non_attribute_error(self):
  53. class I: pass
  54. class C(object):
  55. def getbases(self):
  56. raise RuntimeError
  57. __bases__ = property(getbases)
  58. self.assertRaises(RuntimeError, isinstance, I(), C())
  59. # Like above, except that getattr(cls, '__bases__') raises an
  60. # AttributeError, which /should/ get masked as a TypeError
  61. def test_mask_attribute_error(self):
  62. class I: pass
  63. class C(object):
  64. def getbases(self):
  65. raise AttributeError
  66. __bases__ = property(getbases)
  67. self.assertRaises(TypeError, isinstance, I(), C())
  68. # These tests are similar to above, but tickle certain code paths in
  69. # issubclass() instead of isinstance() -- really PyObject_IsSubclass()
  70. # vs. PyObject_IsInstance().
  71. class TestIsSubclassExceptions(unittest.TestCase):
  72. def test_dont_mask_non_attribute_error(self):
  73. class C(object):
  74. def getbases(self):
  75. raise RuntimeError
  76. __bases__ = property(getbases)
  77. class S(C): pass
  78. self.assertRaises(RuntimeError, issubclass, C(), S())
  79. def test_mask_attribute_error(self):
  80. class C(object):
  81. def getbases(self):
  82. raise AttributeError
  83. __bases__ = property(getbases)
  84. class S(C): pass
  85. self.assertRaises(TypeError, issubclass, C(), S())
  86. # Like above, but test the second branch, where the __bases__ of the
  87. # second arg (the cls arg) is tested. This means the first arg must
  88. # return a valid __bases__, and it's okay for it to be a normal --
  89. # unrelated by inheritance -- class.
  90. def test_dont_mask_non_attribute_error_in_cls_arg(self):
  91. class B: pass
  92. class C(object):
  93. def getbases(self):
  94. raise RuntimeError
  95. __bases__ = property(getbases)
  96. self.assertRaises(RuntimeError, issubclass, B, C())
  97. def test_mask_attribute_error_in_cls_arg(self):
  98. class B: pass
  99. class C(object):
  100. def getbases(self):
  101. raise AttributeError
  102. __bases__ = property(getbases)
  103. self.assertRaises(TypeError, issubclass, B, C())
  104. # meta classes for creating abstract classes and instances
  105. class AbstractClass(object):
  106. def __init__(self, bases):
  107. self.bases = bases
  108. def getbases(self):
  109. return self.bases
  110. __bases__ = property(getbases)
  111. def __call__(self):
  112. return AbstractInstance(self)
  113. class AbstractInstance(object):
  114. def __init__(self, klass):
  115. self.klass = klass
  116. def getclass(self):
  117. return self.klass
  118. __class__ = property(getclass)
  119. # abstract classes
  120. AbstractSuper = AbstractClass(bases=())
  121. AbstractChild = AbstractClass(bases=(AbstractSuper,))
  122. # normal classes
  123. class Super:
  124. pass
  125. class Child(Super):
  126. pass
  127. # new-style classes
  128. class NewSuper(object):
  129. pass
  130. class NewChild(NewSuper):
  131. pass
  132. class TestIsInstanceIsSubclass(unittest.TestCase):
  133. # Tests to ensure that isinstance and issubclass work on abstract
  134. # classes and instances. Before the 2.2 release, TypeErrors were
  135. # raised when boolean values should have been returned. The bug was
  136. # triggered by mixing 'normal' classes and instances were with
  137. # 'abstract' classes and instances. This case tries to test all
  138. # combinations.
  139. def test_isinstance_normal(self):
  140. # normal instances
  141. self.assertEqual(True, isinstance(Super(), Super))
  142. self.assertEqual(False, isinstance(Super(), Child))
  143. self.assertEqual(False, isinstance(Super(), AbstractSuper))
  144. self.assertEqual(False, isinstance(Super(), AbstractChild))
  145. self.assertEqual(True, isinstance(Child(), Super))
  146. self.assertEqual(False, isinstance(Child(), AbstractSuper))
  147. def test_isinstance_abstract(self):
  148. # abstract instances
  149. self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
  150. self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
  151. self.assertEqual(False, isinstance(AbstractSuper(), Super))
  152. self.assertEqual(False, isinstance(AbstractSuper(), Child))
  153. self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
  154. self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
  155. self.assertEqual(False, isinstance(AbstractChild(), Super))
  156. self.assertEqual(False, isinstance(AbstractChild(), Child))
  157. def test_subclass_normal(self):
  158. # normal classes
  159. self.assertEqual(True, issubclass(Super, Super))
  160. self.assertEqual(False, issubclass(Super, AbstractSuper))
  161. self.assertEqual(False, issubclass(Super, Child))
  162. self.assertEqual(True, issubclass(Child, Child))
  163. self.assertEqual(True, issubclass(Child, Super))
  164. self.assertEqual(False, issubclass(Child, AbstractSuper))
  165. def test_subclass_abstract(self):
  166. # abstract classes
  167. self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
  168. self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
  169. self.assertEqual(False, issubclass(AbstractSuper, Child))
  170. self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
  171. self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
  172. self.assertEqual(False, issubclass(AbstractChild, Super))
  173. self.assertEqual(False, issubclass(AbstractChild, Child))
  174. def test_subclass_tuple(self):
  175. # test with a tuple as the second argument classes
  176. self.assertEqual(True, issubclass(Child, (Child,)))
  177. self.assertEqual(True, issubclass(Child, (Super,)))
  178. self.assertEqual(False, issubclass(Super, (Child,)))
  179. self.assertEqual(True, issubclass(Super, (Child, Super)))
  180. self.assertEqual(False, issubclass(Child, ()))
  181. self.assertEqual(True, issubclass(Super, (Child, (Super,))))
  182. self.assertEqual(True, issubclass(NewChild, (NewChild,)))
  183. self.assertEqual(True, issubclass(NewChild, (NewSuper,)))
  184. self.assertEqual(False, issubclass(NewSuper, (NewChild,)))
  185. self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper)))
  186. self.assertEqual(False, issubclass(NewChild, ()))
  187. self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,))))
  188. self.assertEqual(True, issubclass(int, (long, (float, int))))
  189. if test_support.have_unicode:
  190. self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring))))
  191. def test_subclass_recursion_limit(self):
  192. # make sure that issubclass raises RuntimeError before the C stack is
  193. # blown
  194. self.assertRaises(RuntimeError, blowstack, issubclass, str, str)
  195. def test_isinstance_recursion_limit(self):
  196. # make sure that issubclass raises RuntimeError before the C stack is
  197. # blown
  198. self.assertRaises(RuntimeError, blowstack, isinstance, '', str)
  199. def blowstack(fxn, arg, compare_to):
  200. # Make sure that calling isinstance with a deeply nested tuple for its
  201. # argument will raise RuntimeError eventually.
  202. tuple_arg = (compare_to,)
  203. for cnt in xrange(sys.getrecursionlimit()+5):
  204. tuple_arg = (tuple_arg,)
  205. fxn(arg, tuple_arg)
  206. def test_main():
  207. test_support.run_unittest(
  208. TestIsInstanceExceptions,
  209. TestIsSubclassExceptions,
  210. TestIsInstanceIsSubclass
  211. )
  212. if __name__ == '__main__':
  213. test_main()