From: <pj...@us...> - 2011-03-18 02:25:23
|
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. |