[pypy-svn] pypy default: Fix by fenrrir: in pypy we may need more than

arigo commits-noreply at bitbucket.org
Sat Jan 29 16:44:43 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r41452:aaf11c0792a5
Date: 2011-01-29 16:44 +0100
http://bitbucket.org/pypy/pypy/changeset/aaf11c0792a5/

Log:	Fix by fenrrir: in pypy we may need more than
	sys.getrecursionlimit() levels of recursion to trigger this
	behavior, but we trigger it too.

diff --git a/lib-python/modified-2.7.0/test/test_isinstance.py b/lib-python/modified-2.7.0/test/test_isinstance.py
new file mode 100644
--- /dev/null
+++ b/lib-python/modified-2.7.0/test/test_isinstance.py
@@ -0,0 +1,288 @@
+# Tests some corner cases with isinstance() and issubclass().  While these
+# tests use new style classes and properties, they actually do whitebox
+# testing of error conditions uncovered when using extension types.
+
+import unittest
+from test import test_support
+import sys
+
+
+
+class TestIsInstanceExceptions(unittest.TestCase):
+    # Test to make sure that an AttributeError when accessing the instance's
+    # class's bases is masked.  This was actually a bug in Python 2.2 and
+    # 2.2.1 where the exception wasn't caught but it also wasn't being cleared
+    # (leading to an "undetected error" in the debug build).  Set up is,
+    # isinstance(inst, cls) where:
+    #
+    # - inst isn't an InstanceType
+    # - cls isn't a ClassType, a TypeType, or a TupleType
+    # - cls has a __bases__ attribute
+    # - inst has a __class__ attribute
+    # - inst.__class__ as no __bases__ attribute
+    #
+    # Sounds complicated, I know, but this mimics a situation where an
+    # extension type raises an AttributeError when its __bases__ attribute is
+    # gotten.  In that case, isinstance() should return False.
+    def test_class_has_no_bases(self):
+        class I(object):
+            def getclass(self):
+                # This must return an object that has no __bases__ attribute
+                return None
+            __class__ = property(getclass)
+
+        class C(object):
+            def getbases(self):
+                return ()
+            __bases__ = property(getbases)
+
+        self.assertEqual(False, isinstance(I(), C()))
+
+    # Like above except that inst.__class__.__bases__ raises an exception
+    # other than AttributeError
+    def test_bases_raises_other_than_attribute_error(self):
+        class E(object):
+            def getbases(self):
+                raise RuntimeError
+            __bases__ = property(getbases)
+
+        class I(object):
+            def getclass(self):
+                return E()
+            __class__ = property(getclass)
+
+        class C(object):
+            def getbases(self):
+                return ()
+            __bases__ = property(getbases)
+
+        self.assertRaises(RuntimeError, isinstance, I(), C())
+
+    # Here's a situation where getattr(cls, '__bases__') raises an exception.
+    # If that exception is not AttributeError, it should not get masked
+    def test_dont_mask_non_attribute_error(self):
+        class I: pass
+
+        class C(object):
+            def getbases(self):
+                raise RuntimeError
+            __bases__ = property(getbases)
+
+        self.assertRaises(RuntimeError, isinstance, I(), C())
+
+    # Like above, except that getattr(cls, '__bases__') raises an
+    # AttributeError, which /should/ get masked as a TypeError
+    def test_mask_attribute_error(self):
+        class I: pass
+
+        class C(object):
+            def getbases(self):
+                raise AttributeError
+            __bases__ = property(getbases)
+
+        self.assertRaises(TypeError, isinstance, I(), C())
+
+
+
+# These tests are similar to above, but tickle certain code paths in
+# issubclass() instead of isinstance() -- really PyObject_IsSubclass()
+# vs. PyObject_IsInstance().
+class TestIsSubclassExceptions(unittest.TestCase):
+    def test_dont_mask_non_attribute_error(self):
+        class C(object):
+            def getbases(self):
+                raise RuntimeError
+            __bases__ = property(getbases)
+
+        class S(C): pass
+
+        self.assertRaises(RuntimeError, issubclass, C(), S())
+
+    def test_mask_attribute_error(self):
+        class C(object):
+            def getbases(self):
+                raise AttributeError
+            __bases__ = property(getbases)
+
+        class S(C): pass
+
+        self.assertRaises(TypeError, issubclass, C(), S())
+
+    # Like above, but test the second branch, where the __bases__ of the
+    # second arg (the cls arg) is tested.  This means the first arg must
+    # return a valid __bases__, and it's okay for it to be a normal --
+    # unrelated by inheritance -- class.
+    def test_dont_mask_non_attribute_error_in_cls_arg(self):
+        class B: pass
+
+        class C(object):
+            def getbases(self):
+                raise RuntimeError
+            __bases__ = property(getbases)
+
+        self.assertRaises(RuntimeError, issubclass, B, C())
+
+    def test_mask_attribute_error_in_cls_arg(self):
+        class B: pass
+
+        class C(object):
+            def getbases(self):
+                raise AttributeError
+            __bases__ = property(getbases)
+
+        self.assertRaises(TypeError, issubclass, B, C())
+
+
+
+# meta classes for creating abstract classes and instances
+class AbstractClass(object):
+    def __init__(self, bases):
+        self.bases = bases
+
+    def getbases(self):
+        return self.bases
+    __bases__ = property(getbases)
+
+    def __call__(self):
+        return AbstractInstance(self)
+
+class AbstractInstance(object):
+    def __init__(self, klass):
+        self.klass = klass
+
+    def getclass(self):
+        return self.klass
+    __class__ = property(getclass)
+
+# abstract classes
+AbstractSuper = AbstractClass(bases=())
+
+AbstractChild = AbstractClass(bases=(AbstractSuper,))
+
+# normal classes
+class Super:
+    pass
+
+class Child(Super):
+    pass
+
+# new-style classes
+class NewSuper(object):
+    pass
+
+class NewChild(NewSuper):
+    pass
+
+
+
+class TestIsInstanceIsSubclass(unittest.TestCase):
+    # Tests to ensure that isinstance and issubclass work on abstract
+    # classes and instances.  Before the 2.2 release, TypeErrors were
+    # raised when boolean values should have been returned.  The bug was
+    # triggered by mixing 'normal' classes and instances were with
+    # 'abstract' classes and instances.  This case tries to test all
+    # combinations.
+
+    def test_isinstance_normal(self):
+        # normal instances
+        self.assertEqual(True, isinstance(Super(), Super))
+        self.assertEqual(False, isinstance(Super(), Child))
+        self.assertEqual(False, isinstance(Super(), AbstractSuper))
+        self.assertEqual(False, isinstance(Super(), AbstractChild))
+
+        self.assertEqual(True, isinstance(Child(), Super))
+        self.assertEqual(False, isinstance(Child(), AbstractSuper))
+
+    def test_isinstance_abstract(self):
+        # abstract instances
+        self.assertEqual(True, isinstance(AbstractSuper(), AbstractSuper))
+        self.assertEqual(False, isinstance(AbstractSuper(), AbstractChild))
+        self.assertEqual(False, isinstance(AbstractSuper(), Super))
+        self.assertEqual(False, isinstance(AbstractSuper(), Child))
+
+        self.assertEqual(True, isinstance(AbstractChild(), AbstractChild))
+        self.assertEqual(True, isinstance(AbstractChild(), AbstractSuper))
+        self.assertEqual(False, isinstance(AbstractChild(), Super))
+        self.assertEqual(False, isinstance(AbstractChild(), Child))
+
+    def test_subclass_normal(self):
+        # normal classes
+        self.assertEqual(True, issubclass(Super, Super))
+        self.assertEqual(False, issubclass(Super, AbstractSuper))
+        self.assertEqual(False, issubclass(Super, Child))
+
+        self.assertEqual(True, issubclass(Child, Child))
+        self.assertEqual(True, issubclass(Child, Super))
+        self.assertEqual(False, issubclass(Child, AbstractSuper))
+
+    def test_subclass_abstract(self):
+        # abstract classes
+        self.assertEqual(True, issubclass(AbstractSuper, AbstractSuper))
+        self.assertEqual(False, issubclass(AbstractSuper, AbstractChild))
+        self.assertEqual(False, issubclass(AbstractSuper, Child))
+
+        self.assertEqual(True, issubclass(AbstractChild, AbstractChild))
+        self.assertEqual(True, issubclass(AbstractChild, AbstractSuper))
+        self.assertEqual(False, issubclass(AbstractChild, Super))
+        self.assertEqual(False, issubclass(AbstractChild, Child))
+
+    def test_subclass_tuple(self):
+        # test with a tuple as the second argument classes
+        self.assertEqual(True, issubclass(Child, (Child,)))
+        self.assertEqual(True, issubclass(Child, (Super,)))
+        self.assertEqual(False, issubclass(Super, (Child,)))
+        self.assertEqual(True, issubclass(Super, (Child, Super)))
+        self.assertEqual(False, issubclass(Child, ()))
+        self.assertEqual(True, issubclass(Super, (Child, (Super,))))
+
+        self.assertEqual(True, issubclass(NewChild, (NewChild,)))
+        self.assertEqual(True, issubclass(NewChild, (NewSuper,)))
+        self.assertEqual(False, issubclass(NewSuper, (NewChild,)))
+        self.assertEqual(True, issubclass(NewSuper, (NewChild, NewSuper)))
+        self.assertEqual(False, issubclass(NewChild, ()))
+        self.assertEqual(True, issubclass(NewSuper, (NewChild, (NewSuper,))))
+
+        self.assertEqual(True, issubclass(int, (long, (float, int))))
+        if test_support.have_unicode:
+            self.assertEqual(True, issubclass(str, (unicode, (Child, NewChild, basestring))))
+
+    def test_subclass_recursion_limit(self):
+        # make sure that issubclass raises RuntimeError before the C stack is
+        # blown
+        self.assertRaises(RuntimeError, blowstack, issubclass, str, str)
+
+    def test_isinstance_recursion_limit(self):
+        # make sure that issubclass raises RuntimeError before the C stack is
+        # blown
+        self.assertRaises(RuntimeError, blowstack, isinstance, '', str)
+
+def blowstack(fxn, arg, compare_to):
+    # Make sure that calling isinstance with a deeply nested tuple for its
+    # argument will raise RuntimeError eventually.
+    tuple_arg = (compare_to,)
+
+
+    if test_support.check_impl_detail(cpython=True):
+        RECURSION_LIMIT = sys.getrecursionlimit()
+    else:
+        # on non-CPython implementations, the maximum
+        # actual recursion limit might be higher, but
+        # probably not higher than 99999
+        #
+        RECURSION_LIMIT = 99999
+
+    for cnt in xrange(RECURSION_LIMIT+5):
+        tuple_arg = (tuple_arg,)
+        fxn(arg, tuple_arg)
+
+
+def test_main():
+    test_support.run_unittest(
+        TestIsInstanceExceptions,
+        TestIsSubclassExceptions,
+        TestIsInstanceIsSubclass
+    )
+
+
+if __name__ == '__main__':
+    test_main()


More information about the Pypy-commit mailing list