[pypy-svn] r57072 - in pypy/branch/isinstance-refactor/pypy/module/__builtin__: . test

arigo at codespeak.net arigo at codespeak.net
Thu Aug 7 19:02:01 CEST 2008


Author: arigo
Date: Thu Aug  7 19:02:00 2008
New Revision: 57072

Added:
   pypy/branch/isinstance-refactor/pypy/module/__builtin__/abstractinst.py   (contents, props changed)
   pypy/branch/isinstance-refactor/pypy/module/__builtin__/test/test_abstractinst.py   (contents, props changed)
Modified:
   pypy/branch/isinstance-refactor/pypy/module/__builtin__/__init__.py
   pypy/branch/isinstance-refactor/pypy/module/__builtin__/operation.py
Log:
The logic, more directly copied from CPython.


Modified: pypy/branch/isinstance-refactor/pypy/module/__builtin__/__init__.py
==============================================================================
--- pypy/branch/isinstance-refactor/pypy/module/__builtin__/__init__.py	(original)
+++ pypy/branch/isinstance-refactor/pypy/module/__builtin__/__init__.py	Thu Aug  7 19:02:00 2008
@@ -87,8 +87,8 @@
         'coerce'        : 'operation.coerce',
         'divmod'        : 'operation.divmod',
         '_issubtype'    : 'operation._issubtype',
-        'issubclass'    : 'operation.issubclass',
-        'isinstance'    : 'operation.isinstance',
+        'issubclass'    : 'abstractinst.app_issubclass',
+        'isinstance'    : 'abstractinst.app_isinstance',
         'getattr'       : 'operation.getattr',
         'setattr'       : 'operation.setattr',
         'delattr'       : 'operation.delattr',
@@ -151,6 +151,12 @@
                 # xxx hide the installer
                 space.delitem(self.w_dict, space.wrap(name))
                 del self.loaders[name]
+        # install the more general version of isinstance() & co. in the space
+        from pypy.module.__builtin__ import abstractinst as ab
+        space.abstract_isinstance_w = ab.abstract_isinstance_w.__get__(space)
+        space.abstract_issubclass_w = ab.abstract_issubclass_w.__get__(space)
+        space.abstract_isclass_w = ab.abstract_isclass_w.__get__(space)
+        space.abstract_getclass = ab.abstract_getclass.__get__(space)
 
     def startup(self, space):
         # install zipimport hook if --withmod-zipimport is used

Added: pypy/branch/isinstance-refactor/pypy/module/__builtin__/abstractinst.py
==============================================================================
--- (empty file)
+++ pypy/branch/isinstance-refactor/pypy/module/__builtin__/abstractinst.py	Thu Aug  7 19:02:00 2008
@@ -0,0 +1,166 @@
+"""
+Implementation of the 'abstract instance and subclasses' protocol:
+objects can return pseudo-classes as their '__class__' attribute, and
+pseudo-classes can have a '__bases__' attribute with a tuple of other
+pseudo-classes.  The standard built-in functions isinstance() and
+issubclass() follow and trust these attributes is they are present, in
+addition to checking for instances and subtypes in the normal way.
+"""
+
+from pypy.interpreter.error import OperationError
+from pypy.module.__builtin__.interp_classobj import W_ClassObject
+from pypy.module.__builtin__.interp_classobj import W_InstanceObject
+
+def _get_bases(space, w_cls):
+    """Returns 'cls.__bases__'.  Returns None if there is
+    no __bases__ or if cls.__bases__ is not a tuple.
+    """
+    try:
+        w_bases = space.getattr(w_cls, space.wrap('__bases__'))
+    except OperationError, e:
+        if not e.match(space, space.w_AttributeError):
+            raise       # propagate other errors
+        return None
+    if space.is_true(space.isinstance(w_bases, space.w_tuple)):
+        return w_bases
+    else:
+        return None
+
+def abstract_isclass_w(space, w_obj):
+    return _get_bases(space, w_obj) is not None
+
+def check_class(space, w_obj, msg):
+    if not abstract_isclass_w(space, w_obj):
+        raise OperationError(space.w_TypeError, space.wrap(msg))
+
+
+def abstract_getclass(space, w_obj):
+    try:
+        return space.getattr(w_obj, space.wrap('__class__'))
+    except OperationError, e:
+        if not e.match(space, space.w_AttributeError):
+            raise       # propagate other errors
+        return space.type(w_obj)
+
+
+def abstract_isinstance_w(space, w_obj, w_klass_or_tuple):
+    """Implementation for the full 'isinstance(obj, klass_or_tuple)'."""
+
+    # -- case (anything, type)
+    try:
+        w_result = space.isinstance(w_obj, w_klass_or_tuple)
+    except OperationError, e:   # if w_klass_or_tuple was not a type, ignore it
+        if not e.match(space, space.w_TypeError):
+            raise       # propagate other errors
+    else:
+        if space.is_true(w_result):
+            return True
+        # From now on we know that w_klass_or_tuple is indeed a type.
+        # Try also to compare it with obj.__class__, if this is not
+        # the same as type(obj).
+        try:
+            w_pretendtype = space.getattr(w_obj, space.wrap('__class__'))
+            if space.is_w(w_pretendtype, space.type(w_obj)):
+                return False     # common case: obj.__class__ is type(obj)
+            w_result = space.issubtype(w_pretendtype, w_klass_or_tuple)
+        except OperationError, e:
+            if e.async(space):
+                raise
+            return False      # ignore most exceptions
+        else:
+            return space.is_true(w_result)
+
+    # -- case (old-style instance, old-style class)
+    oldstyleclass = space.interpclass_w(w_klass_or_tuple)
+    if isinstance(oldstyleclass, W_ClassObject):
+        oldstyleinst = space.interpclass_w(w_obj)
+        if isinstance(oldstyleinst, W_InstanceObject):
+            return oldstyleinst.w_class.is_subclass_of(oldstyleclass)
+
+    # -- case (anything, tuple)
+    if space.is_true(space.isinstance(w_klass_or_tuple, space.w_tuple)):
+        for w_klass in space.unpacktuple(w_klass_or_tuple):
+            if abstract_isinstance_w(space, w_obj, w_klass):
+                return True
+        return False
+
+    # -- case (anything, abstract-class)
+    check_class(space, w_klass_or_tuple,
+                "isinstance() arg 2 must be a class, type,"
+                " or tuple of classes and types")
+    try:
+        w_abstractclass = space.getattr(w_obj, space.wrap('__class__'))
+    except OperationError, e:
+        if e.async(space):      # ignore most exceptions
+            raise
+        return False
+    else:
+        return _issubclass_recurse(space, w_abstractclass, w_klass_or_tuple)
+
+
+def _issubclass_recurse(space, w_derived, w_top):
+    """Internal helper for abstract cases.  Here, w_top cannot be a tuple."""
+    if space.is_w(w_derived, w_top):
+        return True
+    w_bases = _get_bases(space, w_derived)
+    if w_bases is not None:
+        for w_base in space.unpacktuple(w_bases):
+            if _issubclass_recurse(space, w_base, w_top):
+                return True
+    return False
+
+
+def abstract_issubclass_w(space, w_derived, w_klass_or_tuple):
+    """Implementation for the full 'issubclass(derived, klass_or_tuple)'."""
+
+    # -- case (type, type)
+    try:
+        w_result = space.issubtype(w_derived, w_klass_or_tuple)
+    except OperationError, e:   # if one of the args was not a type, ignore it
+        if not e.match(space, space.w_TypeError):
+            raise       # propagate other errors
+    else:
+        return space.is_true(w_result)
+
+    # -- case (old-style class, old-style class)
+    oldstylederived = space.interpclass_w(w_derived)
+    if isinstance(oldstylederived, W_ClassObject):
+        oldstyleklass = space.interpclass_w(w_klass_or_tuple)
+        if isinstance(oldstyleklass, W_ClassObject):
+            return oldstylederived.is_subclass_of(oldstyleklass)
+    else:
+        check_class(space, w_derived, "issubclass() arg 1 must be a class")
+    # from here on, we are sure that w_derived is a class-like object
+
+    # -- case (class-like-object, tuple-of-classes)
+    if space.is_true(space.isinstance(w_klass_or_tuple, space.w_tuple)):
+        for w_klass in space.unpacktuple(w_klass_or_tuple):
+            if abstract_issubclass_w(w_derived, w_klass):
+                return True
+        return False
+
+    # -- case (class-like-object, abstract-class)
+    check_class(space, w_klass_or_tuple,
+                "issubclass() arg 2 must be a class, type,"
+                " or tuple of classes and types")
+    return _issubclass_recurse(space, w_derived, w_klass_or_tuple)
+
+
+# ____________________________________________________________
+# App-level interface
+
+def issubclass(space, w_cls, w_klass_or_tuple):
+    """Check whether a class 'cls' is a subclass (i.e., a derived class) of
+another class.  When using a tuple as the second argument, check whether
+'cls' is a subclass of any of the classes listed in the tuple."""
+    return space.wrap(abstract_issubclass_w(space, w_cls, w_klass_or_tuple))
+
+def isinstance(space, w_obj, w_klass_or_tuple):
+    """Check whether an object is an instance of a class (or of a subclass
+thereof).  When using a tuple as the second argument, check whether 'obj'
+is an instance of any of the classes listed in the tuple."""
+    return space.wrap(abstract_isinstance_w(space, w_obj, w_klass_or_tuple))
+
+# avoid namespace pollution
+app_issubclass = issubclass; del issubclass
+app_isinstance = isinstance; del isinstance

Modified: pypy/branch/isinstance-refactor/pypy/module/__builtin__/operation.py
==============================================================================
--- pypy/branch/isinstance-refactor/pypy/module/__builtin__/operation.py	(original)
+++ pypy/branch/isinstance-refactor/pypy/module/__builtin__/operation.py	Thu Aug  7 19:02:00 2008
@@ -220,95 +220,3 @@
 function).  Note that classes are callable."""
     return space.callable(w_object)
 
-
-
-def _recursive_issubclass(space, w_cls, w_klass_or_tuple): # returns interp-level bool
-    if space.is_w(w_cls, w_klass_or_tuple):
-        return True
-    try:
-        w_bases = space.getattr(w_cls, space.wrap("__bases__"))
-    except OperationError, e:
-        if e.match(space, space.w_AttributeError):
-            return False
-        else:
-            raise
-    w_iterator = space.iter(w_bases)
-    while True:
-        try:
-            w_base = space.next(w_iterator)
-        except OperationError, e:
-            if not e.match(space, space.w_StopIteration):
-                raise
-            break
-        if _recursive_issubclass(space, w_base, w_klass_or_tuple):
-            return True
-    return False
-
-def _issubclass(space, w_cls, w_klass_or_tuple, check_cls, depth): # returns interp-level bool
-    if depth == 0:
-        # XXX overzealous test compliance hack
-        raise OperationError(space.w_RuntimeError, space.wrap("maximum recursion depth exceeded"))
-    if space.is_true(space.issubtype(space.type(w_klass_or_tuple), space.w_tuple)):
-        w_iter = space.iter(w_klass_or_tuple)
-        while True:
-            try:
-                w_klass = space.next(w_iter)
-            except OperationError, e:
-                if not e.match(space, space.w_StopIteration):
-                   raise
-                break
-            if _issubclass(space, w_cls, w_klass, True, depth - 1):
-                return True
-        return False
-
-    try:
-        return space.is_true(space.issubtype(w_cls, w_klass_or_tuple))
-    except OperationError, e:
-        if e.match(space, space.w_TypeError):
-            w_bases = space.wrap('__bases__')
-            if check_cls:
-                try:
-                    space.getattr(w_cls, w_bases)
-                except OperationError, e:
-                    if not e.match(space, space.w_AttributeError):
-                        raise
-                    raise OperationError(space.w_TypeError, space.wrap('arg 1 must be a class or type'))
-            try:
-                space.getattr(w_klass_or_tuple, w_bases)
-            except OperationError, e:
-                if not e.match(space, space.w_AttributeError):
-                    raise
-                raise OperationError(space.w_TypeError, space.wrap('arg 2 must be a class or type or a tuple thereof'))
-            return _recursive_issubclass(space, w_cls, w_klass_or_tuple)
-        else:
-            raise
-
-
-def issubclass(space, w_cls, w_klass_or_tuple):
-    """Check whether a class 'cls' is a subclass (i.e., a derived class) of
-another class.  When using a tuple as the second argument, check whether
-'cls' is a subclass of any of the classes listed in the tuple."""
-    return space.wrap(issubclass_w(space, w_cls, w_klass_or_tuple))
-
-def issubclass_w(space, w_cls, w_klass_or_tuple):
-    return _issubclass(space, w_cls, w_klass_or_tuple, True, space.sys.recursionlimit)
-
-
-def isinstance(space, w_obj, w_klass_or_tuple):
-    """Check whether an object is an instance of a class (or of a subclass
-thereof).  When using a tuple as the second argument, check whether 'obj'
-is an instance of any of the classes listed in the tuple."""
-    w_objtype = space.type(w_obj)
-    if issubclass_w(space, w_objtype, w_klass_or_tuple):
-        return space.w_True
-    try:
-        w_objcls = space.getattr(w_obj, space.wrap("__class__"))
-    except OperationError, e:
-        if e.match(space, space.w_AttributeError):
-            return space.w_False
-        else:
-            raise
-    if space.is_w(w_objcls, w_objtype):
-        return space.w_False
-    else:
-        return space.wrap(_issubclass(space, w_objcls, w_klass_or_tuple, False, space.sys.recursionlimit))

Added: pypy/branch/isinstance-refactor/pypy/module/__builtin__/test/test_abstractinst.py
==============================================================================
--- (empty file)
+++ pypy/branch/isinstance-refactor/pypy/module/__builtin__/test/test_abstractinst.py	Thu Aug  7 19:02:00 2008
@@ -0,0 +1,182 @@
+from pypy.module.__builtin__.abstractinst import *
+
+
+class TestAbstractInst:
+
+    def test_abstract_isclass(self):
+        space = self.space
+        w_B1, w_B2, w_B3, w_X, w_Y = space.unpacktuple(space.appexec([], """():
+            class X(object): pass
+            class Y: pass
+            B1, B2, B3 = X(), X(), X()
+            B2.__bases__ = (42,)
+            B3.__bases__ = 'spam'
+            return B1, B2, B3, X, Y
+        """))
+        assert abstract_isclass_w(space, space.w_int) is True
+        assert abstract_isclass_w(space, w_B1) is False
+        assert abstract_isclass_w(space, w_B2) is True
+        assert abstract_isclass_w(space, w_B3) is False
+        assert abstract_isclass_w(space, w_X) is True
+        assert abstract_isclass_w(space, w_Y) is True
+
+    def test_abstract_getclass(self):
+        space = self.space
+        w_x, w_y, w_A, w_MyInst = space.unpacktuple(space.appexec([], """():
+            class MyInst(object):
+                def __init__(self, myclass):
+                    self.myclass = myclass
+                def __class__(self):
+                    if self.myclass is None:
+                        raise AttributeError
+                    return self.myclass
+                __class__ = property(__class__)
+            A = object()
+            x = MyInst(A)
+            y = MyInst(None)
+            return x, y, A, MyInst
+        """))
+        w_42 = space.wrap(42)
+        assert space.is_w(abstract_getclass(space, w_42), space.w_int)
+        assert space.is_w(abstract_getclass(space, w_x), w_A)
+        assert space.is_w(abstract_getclass(space, w_y), w_MyInst)
+        assert space.is_w(abstract_getclass(space, w_MyInst), space.w_type)
+
+
+class AppTestAbstractInst:
+
+    def test_abstract_isinstance(self):
+        class MyBaseInst(object):
+            pass
+        class MyInst(MyBaseInst):
+            def __init__(self, myclass):
+                self.myclass = myclass
+            def __class__(self):
+                if self.myclass is None:
+                    raise AttributeError
+                return self.myclass
+            __class__ = property(__class__)
+        class MyInst2(MyBaseInst):
+            pass
+        class MyClass(object):
+            pass
+
+        A = MyClass()
+        x = MyInst(A)
+        assert x.__class__ is A
+        assert isinstance(x, MyInst)
+        assert isinstance(x, MyBaseInst)
+        assert not isinstance(x, MyInst2)
+        raises(TypeError, isinstance, x, A)      # A has no __bases__
+        A.__bases__ = "hello world"
+        raises(TypeError, isinstance, x, A)      # A.__bases__ is not tuple
+
+        class Foo(object):
+            pass
+        class SubFoo1(Foo):
+            pass
+        class SubFoo2(Foo):
+            pass
+        y = MyInst(SubFoo1)
+        assert isinstance(y, MyInst)
+        assert isinstance(y, MyBaseInst)
+        assert not isinstance(y, MyInst2)
+        assert isinstance(y, SubFoo1)
+        assert isinstance(y, Foo)
+        assert not isinstance(y, SubFoo2)
+
+        z = MyInst(None)
+        assert isinstance(z, MyInst)
+        assert isinstance(z, MyBaseInst)
+        assert not isinstance(z, MyInst2)
+        assert not isinstance(z, SubFoo1)
+
+        assert isinstance(y, ((), MyInst2, SubFoo1))
+        assert isinstance(y, (MyBaseInst, (SubFoo2,)))
+        assert not isinstance(y, (MyInst2, SubFoo2))
+        assert not isinstance(z, ())
+
+        class Foo(object):
+            pass
+        class Bar:
+            pass
+        u = MyInst(Foo)
+        assert isinstance(u, MyInst)
+        assert isinstance(u, MyBaseInst)
+        assert not isinstance(u, MyInst2)
+        assert isinstance(u, Foo)
+        assert not isinstance(u, Bar)
+        v = MyInst(Bar)
+        assert isinstance(v, MyInst)
+        assert isinstance(v, MyBaseInst)
+        assert not isinstance(v, MyInst2)
+        assert not isinstance(v, Foo)
+        assert isinstance(v, Bar)
+
+        BBase = MyClass()
+        BSub1 = MyClass()
+        BSub2 = MyClass()
+        BBase.__bases__ = ()
+        BSub1.__bases__ = (BBase,)
+        BSub2.__bases__ = (BBase,)
+        x = MyInst(BSub1)
+        assert isinstance(x, BSub1)
+        assert isinstance(x, BBase)
+        assert not isinstance(x, BSub2)
+        assert isinstance(x, (BSub2, (), (BSub1,)))
+
+        del BBase.__bases__
+        assert isinstance(x, BSub1)
+        raises(TypeError, isinstance, x, BBase)
+        assert not isinstance(x, BSub2)
+
+        BBase.__bases__ = "foobar"
+        assert isinstance(x, BSub1)
+        raises(TypeError, isinstance, x, BBase)
+        assert not isinstance(x, BSub2)
+
+    def test_abstract_issubclass(self):
+        class MyBaseInst(object):
+            pass
+        class MyInst(MyBaseInst):
+            pass
+        class MyInst2(MyBaseInst):
+            pass
+        class MyClass(object):
+            pass
+
+        assert issubclass(MyInst, MyBaseInst)
+        assert issubclass(MyInst2, MyBaseInst)
+        assert issubclass(MyBaseInst, MyBaseInst)
+        assert not issubclass(MyBaseInst, MyInst)
+        assert not issubclass(MyInst, MyInst2)
+
+        BBase = MyClass()
+        BSub1 = MyClass()
+        BSub2 = MyClass()
+        BBase.__bases__ = ()
+        BSub1.__bases__ = (BBase,)
+        BSub2.__bases__ = (BBase,)
+        assert issubclass(BSub1, BBase)
+        assert issubclass(BBase, BBase)
+        assert not issubclass(BBase, BSub1)
+        assert not issubclass(BSub1, BSub2)
+        assert not issubclass(MyInst, BSub1)
+        assert not issubclass(BSub1, MyInst)
+
+        del BBase.__bases__
+        raises(TypeError, issubclass, BSub1, BBase)
+        raises(TypeError, issubclass, BBase, BBase)
+        raises(TypeError, issubclass, BBase, BSub1)
+        assert not issubclass(BSub1, BSub2)
+        assert not issubclass(MyInst, BSub1)
+        assert not issubclass(BSub1, MyInst)
+
+        BBase.__bases__ = 42
+        raises(TypeError, issubclass, BSub1, BBase)
+        raises(TypeError, issubclass, BBase, BBase)
+        raises(TypeError, issubclass, BBase, BSub1)
+        assert not issubclass(BSub1, BSub2)
+        assert not issubclass(MyInst, BSub1)
+        assert not issubclass(BSub1, MyInst)
+



More information about the Pypy-commit mailing list