SF.net SVN: jython:[7245] trunk/jython/src/org/python/core

[email protected] Fri, 18 Mar 2011 02:25:17 +0000
Newsgroups gmane.comp.lang.jython.cvs
Message-ID <[email protected]>
Revision: 7245
          http://jython.svn.sourceforge.net/jython/?rev=7245&view=rev
Author:   pjenvey
Date:     2011-03-18 02:25:16 +0000 (Fri, 18 Mar 2011)

Log Message:
-----------
o add abc support. we differ from CPython in that type lacks the
__instance/subclasscheck__ methods as it would slow down isinstance/subclass
o somewhat fix our object.__init__/__new__ FIXMEs and disallow arbitrary args
to __new__ per 2.6. __init__ doesn't repeat this check yet but this is probably
enough

Modified Paths:
--------------
    trunk/jython/src/org/python/core/Py.java
    trunk/jython/src/org/python/core/PyObject.java
    trunk/jython/src/org/python/core/PyType.java

Modified: trunk/jython/src/org/python/core/Py.java
===================================================================
--- trunk/jython/src/org/python/core/Py.java	2011-03-18 01:26:55 UTC (rev 7244)
+++ trunk/jython/src/org/python/core/Py.java	2011-03-18 02:25:16 UTC (rev 7245)
@@ -86,6 +86,8 @@
     public static long TPFLAGS_HEAPTYPE = 1L << 9;
     /** Set if the type allows subclassing */
     public static long TPFLAGS_BASETYPE = 1L << 10;
+    /** Type is abstract and cannot be instantiated */
+    public static long TPFLAGS_IS_ABSTRACT = 1L << 20;
 
     /** Builtin types that are used to setup PyObject. */
     static final Set<Class<?>> BOOTSTRAP_TYPES = Generic.set();
@@ -1866,104 +1868,183 @@
                 new File(dir, name.substring(0, index)));
     }
 
-    private static boolean abstract_issubclass(PyObject derived, PyObject cls) {
-        if (derived == cls) {
+    public static boolean isInstance(PyObject inst, PyObject cls) {
+        // Quick test for an exact match
+        if (inst.getType() == cls) {
             return true;
         }
-        PyObject bases = derived.__findattr__("__bases__");
-        if (bases == null) {
+
+        if (cls instanceof PyTuple) {
+            ThreadState threadState = Py.getThreadState();
+            threadState.enterRecursiveCall(" in __subclasscheck__");
+            try {
+                for (PyObject item : cls.asIterable()) {
+                    if (isInstance(inst, item)) {
+                        return true;
+                    }
+                }
+            } finally {
+                threadState.leaveRecursiveCall();
+            }
             return false;
         }
-        for (int i = 0; i < bases.__len__(); i++) {
-            if (abstract_issubclass(bases.__getitem__(i), cls)) {
-                return true;
-            }
+
+        PyObject checkerResult;
+        if ((checkerResult = dispatchToChecker(inst, cls, "__instancecheck__")) != null) {
+            return checkerResult.__nonzero__();
         }
-        return false;
-    }
 
-    public static boolean isInstance(PyObject inst, PyObject cls) {
-        return recursiveIsInstance(inst, cls, 0);
+        return recursiveIsInstance(inst, cls);
     }
 
-    private static boolean recursiveIsInstance(PyObject inst, PyObject cls, int recursionDepth) {
+    static boolean recursiveIsInstance(PyObject inst, PyObject cls) {
         if (cls instanceof PyClass && inst instanceof PyInstance) {
-            PyClass inClass = (PyClass)inst.fastGetClass();
-            return inClass.isSubClass((PyClass)cls);
-        } else if (cls instanceof PyType) {
+            PyClass inClass = ((PyInstance) inst).fastGetClass();
+            return inClass.isSubClass((PyClass) cls);
+        }
+        if (cls instanceof PyType) {
             PyType instType = inst.getType();
-            PyType type = (PyType)cls;
+            PyType type = (PyType) cls;
 
             // equiv. to PyObject_TypeCheck
             if (instType == type || instType.isSubType(type)) {
                 return true;
             }
 
-            PyObject c = inst.__findattr__("__class__");
-            if (c != null && c != instType && c instanceof PyType) {
-                return ((PyType)c).isSubType(type);
+            PyObject instCls = inst.__findattr__("__class__");
+            if (instCls != null && instCls != instType && instCls instanceof PyType) {
+                return ((PyType) instCls).isSubType(type);
             }
             return false;
-        } else if (cls instanceof PyTuple) {
-            if (recursionDepth > Py.getSystemState().getrecursionlimit()) {
-                throw Py.RuntimeError("nest level of tuple too deep");
-            }
+        }
+        
+        checkClass(cls, "isinstance() arg 2 must be a class, type, or tuple of classes and types");
+        PyObject instCls = inst.__findattr__("__class__");
+        if (instCls == null) {
+            return false;
+        }
+        return abstractIsSubClass(instCls, cls);
+    }
 
-            for (PyObject tupleItem : ((PyTuple)cls).getArray()) {
-                if (recursiveIsInstance(inst, tupleItem, recursionDepth + 1)) {
-                    return true;
+    public static boolean isSubClass(PyObject derived, PyObject cls) {
+        if (cls instanceof PyTuple) {
+            ThreadState threadState = Py.getThreadState();
+            threadState.enterRecursiveCall(" in __subclasscheck__");
+            try {
+                for (PyObject item : cls.asIterable()) {
+                    if (isSubClass(derived, item)) {
+                        return true;
+                    }
                 }
+            } finally {
+                threadState.leaveRecursiveCall();
             }
             return false;
-        } else {
-            if (cls.__findattr__("__bases__") == null) {
-                throw Py.TypeError("isinstance() arg 2 must be a class, type, or tuple of "
-                                   + "classes and types");
-            }
+        }
 
-            PyObject icls = inst.__findattr__("__class__");
-            if (icls == null) {
-                return false;
-            }
-            return abstract_issubclass(icls, cls);
+        PyObject checkerResult;
+        if ((checkerResult = dispatchToChecker(derived, cls, "__subclasscheck__")) != null) {
+            return checkerResult.__nonzero__();
         }
-    }
 
-    public static boolean isSubClass(PyObject derived,PyObject cls) {
-        return isSubClass(derived, cls, 0);
+        return recursiveIsSubClass(derived, cls);
     }
 
-    private static boolean isSubClass(PyObject derived, PyObject cls, int recursionDepth) {
+    static boolean recursiveIsSubClass(PyObject derived, PyObject cls) {
         if (derived instanceof PyType && cls instanceof PyType) {
             if (derived == cls) {
                 return true;
             }
             return ((PyType) derived).isSubType((PyType) cls);
-        } else if (cls instanceof PyClass && derived instanceof PyClass) {
+        }
+        if (derived instanceof PyClass && cls instanceof PyClass) {
             return ((PyClass) derived).isSubClass((PyClass) cls);
-        } else if (cls.getClass() == PyTuple.class) {
-            if (recursionDepth > Py.getSystemState().getrecursionlimit()) {
-                throw Py.RuntimeError("nest level of tuple too deep");
+        }
+
+        checkClass(derived, "issubclass() arg 1 must be a class");
+        checkClass(cls, "issubclass() arg 2 must be a class or tuple of classes");
+        return abstractIsSubClass(derived, cls);
+    }
+
+    private static boolean abstractIsSubClass(PyObject derived, PyObject cls) {
+        while (true) {
+            if (derived == cls) {
+                return true;
             }
-            for (int i = 0; i < cls.__len__(); i++) {
-                if (isSubClass(derived, cls.__getitem__(i), recursionDepth + 1)) {
+
+            PyTuple bases = abstractGetBases(derived);
+            if (bases == null) {
+                return false;
+            }
+
+            int basesSize = bases.size();
+            if (basesSize == 0) {
+                return false;
+            }
+            if (basesSize == 1) {
+                // Avoid recursivity in the single inheritance case
+                derived = bases.pyget(0);
+                continue;
+            }
+
+            for (PyObject base : bases.asIterable()) {
+                if (abstractIsSubClass(base, cls)) {
                     return true;
                 }
             }
             return false;
-        } else {
-            if (derived.__findattr__("__bases__") == null) {
-                throw Py.TypeError(
-                        "issubclass() arg 1 must be a class");
-            }
-            if (cls.__findattr__("__bases__") == null) {
-                throw Py.TypeError(
-                        "issubclass() arg 2 must be a class, type," + " or tuple of classes and types");
-            }
-            return abstract_issubclass(derived, cls);
         }
     }
 
+    /**
+     * Attempt to dispatch an isinstance/issubclass call to cls's associated
+     * __instance/subclasscheck__.
+     *
+     * @param checkerArg the argument to call the checker with
+     * @param cls a Python class
+     * @param checkerName the checker name
+     * @return null if cls provides no checker, otherwise the result of calling the
+     * checker
+     */
+    private static PyObject dispatchToChecker(PyObject checkerArg, PyObject cls,
+                                              String checkerName) {
+        PyObject checker = cls.__findattr__(checkerName);
+        if (checker == null) {
+            return null;
+        }
+
+        PyObject result;
+        ThreadState threadState = Py.getThreadState();
+        threadState.enterRecursiveCall(" in " + checkerName);
+        try {
+            result = checker.__call__(checkerArg);
+        } finally {
+            threadState.leaveRecursiveCall();
+        }
+        return result;
+    }
+
+    /**
+     * Return the __bases__ of cls. Returns null if no valid __bases__ are found.
+     */
+    private static PyTuple abstractGetBases(PyObject cls) {
+        PyObject bases = cls.__findattr__("__bases__");
+        if (bases instanceof PyTuple) {
+            return (PyTuple) bases;
+        }
+        return null;
+    }
+
+    /**
+     * Throw a TypeError with the specified message if cls does not appear to be a Python
+     * class.
+     */
+    private static void checkClass(PyObject cls, String message) {
+        if (abstractGetBases(cls) == null) {
+            throw Py.TypeError(message);
+        }
+    }
+
     static PyObject[] make_array(PyObject iterable) {
         // Special-case the common tuple and list cases, for efficiency
         if (iterable instanceof PySequenceList) {

Modified: trunk/jython/src/org/python/core/PyObject.java
===================================================================
--- trunk/jython/src/org/python/core/PyObject.java	2011-03-18 01:26:55 UTC (rev 7244)
+++ trunk/jython/src/org/python/core/PyObject.java	2011-03-18 02:25:16 UTC (rev 7245)
@@ -8,6 +8,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.python.expose.ExposedClassMethod;
 import org.python.expose.ExposedDelete;
 import org.python.expose.ExposedGet;
 import org.python.expose.ExposedMethod;
@@ -74,17 +75,32 @@
         objtype = (PyType)this;
     }
 
-    //XXX: in CPython object.__new__ has a doc string...
     @ExposedNew
+    static final PyObject object___new__(PyNewWrapper new_, boolean init, PyType subtype,
+                                         PyObject[] args, String[] keywords) {
+        // don't allow arguments if the default object.__init__() is about to be called
+        PyObject[] where = new PyObject[1];
+        subtype.lookup_where("__init__", where);
+        if (where[0] == TYPE && args.length > 0) {
+            throw Py.TypeError("object.__new__() takes no parameters");
+        }
+
+        if (subtype.isAbstract()) {
+            // Compute ", ".join(sorted(type.__abstractmethods__)) into methods
+            PyObject sorted =
+                    Py.getSystemState().getBuiltins().__getitem__(Py.newString("sorted"));
+            PyString methods =
+                    Py.newString(", ")
+                    .join(sorted.__call__(subtype.getAbstractmethods()));
+            throw Py.TypeError(String.format("Can't instantiate abstract class %s with abstract "
+                                             + "methods %s", subtype.fastGetName(), methods));
+        }
+        
+        return new_.for_type == subtype ? new PyObject() : new PyObjectDerived(subtype);
+    }
+
     @ExposedMethod(doc = BuiltinDocs.object___init___doc)
     final void object___init__(PyObject[] args, String[] keywords) {
-        // XXX: attempted fix for object(foo=1), etc
-        // XXX: this doesn't work for metaclasses, for some reason
-        /*
-        if (args.length > 0) {
-            throw Py.TypeError("default __new__ takes no parameters");
-        }
-        */
     }
 
     @ExposedGet(name = "__class__")
@@ -3974,10 +3990,15 @@
     }
 
     public PyTuple __getnewargs__() {
-        //default is empty tuple
+        // default is empty tuple
         return new PyTuple();
     }
 
+    @ExposedClassMethod(doc = BuiltinDocs.object___subclasshook___doc)
+    public static PyObject object___subclasshook__(PyType type, PyObject subclass) {
+        return Py.NotImplemented;
+    }
+
     /* arguments' conversion helpers */
 
     public static class ConversionException extends Exception {

Modified: trunk/jython/src/org/python/core/PyType.java
===================================================================
--- trunk/jython/src/org/python/core/PyType.java	2011-03-18 01:26:55 UTC (rev 7244)
+++ trunk/jython/src/org/python/core/PyType.java	2011-03-18 02:25:16 UTC (rev 7245)
@@ -120,7 +120,7 @@
     }
 
     @ExposedNew
-    public static PyObject type___new__(PyNewWrapper new_, boolean init, PyType subtype,
+    static final PyObject type___new__(PyNewWrapper new_, boolean init, PyType subtype,
                                         PyObject[] args, String[] keywords) {
         // Special case: type(x) should return x.getType()
         if (args.length == 1 && keywords.length == 0) {
@@ -150,6 +150,18 @@
         return newType(new_, subtype, name, bases, dict);
     }
 
+    @ExposedMethod(doc = BuiltinDocs.type___init___doc)
+    final void type___init__(PyObject[] args, String[] kwds) {
+        if (kwds.length > 0) {
+            throw Py.TypeError("type.__init__() takes no keyword arguments");
+        }
+
+        if (args.length != 1 && args.length != 3) {
+            throw Py.TypeError("type.__init__() takes 1 or 3 arguments");
+        }
+        object___init__(Py.EmptyObjects, Py.NoKeywords);
+    }
+
     public static PyObject newType(PyNewWrapper new_, PyType metatype, String name, PyTuple bases,
                                    PyObject dict) {
         PyObject[] tmpBases = bases.getArray();
@@ -741,6 +753,10 @@
         tp_flags = isBaseType ? tp_flags | Py.TPFLAGS_BASETYPE : tp_flags & ~Py.TPFLAGS_BASETYPE;
     }
 
+    boolean isAbstract() {
+        return (tp_flags & Py.TPFLAGS_IS_ABSTRACT) != 0;
+    }
+
     private void mro_internal() {
         if (getType() == TYPE) {
             mro = computeMro();
@@ -1684,6 +1700,26 @@
         throw Py.TypeError(String.format("can't delete %s.__module__", name));
     }
 
+    @ExposedGet(name = "__abstractmethods__")
+    public PyObject getAbstractmethods() {
+        PyObject result = dict.__finditem__("__abstractmethods__");
+        if (result == null) {
+            noAttributeError("__abstractmethods__");
+        }
+        return result;
+    }
+
+    @ExposedSet(name = "__abstractmethods__")
+    public void setAbstractmethods(PyObject value) {
+        // __abstractmethods__ should only be set once on a type, in abc.ABCMeta.__new__,
+        // so this function doesn't do anything special to update subclasses
+        dict.__setitem__("__abstractmethods__", value);
+        postSetattr("__abstractmethods__");
+        tp_flags = value.__nonzero__()
+                ? tp_flags | Py.TPFLAGS_IS_ABSTRACT
+                : tp_flags & ~Py.TPFLAGS_IS_ABSTRACT;
+    }
+    
     public int getNumSlots() {
         return numSlots;
     }


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.

------------------------------------------------------------------------------
Colocation vs. Managed Hosting
A question and answer guide to determining the best fit
for your organization - today and in the future.
http://p.sf.net/sfu/internap-sfd2d