|
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.
|