From: <cg...@us...> - 2009-01-16 08:11:53
|
Revision: 5935 http://jython.svn.sourceforge.net/jython/?rev=5935&view=rev Author: cgroves Date: 2009-01-16 08:11:48 +0000 (Fri, 16 Jan 2009) Log Message: ----------- underlying_class is the class of instances of this type, so PyJavaType shouldn't store the class it's wrapping in it. Instead, it should use the general javaProxy field on PyObject and leave underlying_class up to its bases. Without this, Python classes can't subclass a Java interface and a Python class subclassing a Java class or interface. Modified Paths: -------------- trunk/jython/Lib/test/test_java_subclasses.py trunk/jython/src/org/python/core/PyArray.java trunk/jython/src/org/python/core/PyJavaType.java trunk/jython/src/org/python/core/PyType.java Modified: trunk/jython/Lib/test/test_java_subclasses.py =================================================================== --- trunk/jython/Lib/test/test_java_subclasses.py 2009-01-15 03:36:09 UTC (rev 5934) +++ trunk/jython/Lib/test/test_java_subclasses.py 2009-01-16 08:11:48 UTC (rev 5935) @@ -5,8 +5,8 @@ from test import test_support -from java.lang import (Boolean, Class, ClassLoader, Integer, Object, Runnable, String, Thread, - ThreadGroup) +from java.lang import (Boolean, Class, ClassLoader, Comparable,Integer, Object, Runnable, String, + Thread, ThreadGroup) from java.util import Date, Hashtable, Vector from java.awt import Color, Component, Dimension, Rectangle @@ -29,6 +29,22 @@ self.fail("Shouldn't be callable with a no args") self.assertRaises(TypeError, Callbacker.callNoArg, PyBadCallback()) + def test_inheriting_from_python_and_java_interface(self): + calls = [] + class Runner(Runnable): + def run(self): + calls.append("Runner.run") + + class ComparableRunner(Comparable, Runner): + def compareTo(self, other): + calls.append("ComparableRunner.compareTo") + return 0 + + c = ComparableRunner() + c.compareTo(None) + c.run() + self.assertEquals(calls, ["ComparableRunner.compareTo", "Runner.run"]) + class TableModelTest(unittest.TestCase): def test_class_coercion(self): '''Python type instances coerce to a corresponding Java wrapper type in Object.getClass''' @@ -89,6 +105,17 @@ except TypeError: pass + class PyDim(Dimension): + pass + class PyDimRun(PyDim, Runnable): + pass + try: + class PyDimRunCol(PyDimRun, Color): + pass + self.fail("Shouldn't be able to subclass more than one concrete java class") + except TypeError: + pass + def test_multilevel_override(self): class SubDate(Date): def toString(self): Modified: trunk/jython/src/org/python/core/PyArray.java =================================================================== --- trunk/jython/src/org/python/core/PyArray.java 2009-01-15 03:36:09 UTC (rev 5934) +++ trunk/jython/src/org/python/core/PyArray.java 2009-01-16 08:11:48 UTC (rev 5935) @@ -96,7 +96,7 @@ typecode = obj.toString(); type = char2class(typecode.charAt(0)); } else if (obj instanceof PyJavaType) { - type = ((PyJavaType)obj).underlying_class; + type = ((PyJavaType)obj).getProxyType(); typecode = type.getName(); } else { throw Py.TypeError("array() argument 1 must be char, not " + obj.getType().fastGetName()); Modified: trunk/jython/src/org/python/core/PyJavaType.java =================================================================== --- trunk/jython/src/org/python/core/PyJavaType.java 2009-01-15 03:36:09 UTC (rev 5934) +++ trunk/jython/src/org/python/core/PyJavaType.java 2009-01-16 08:11:48 UTC (rev 5935) @@ -37,7 +37,7 @@ @Override public Class<?> getProxyType() { - return PyObject.class.isAssignableFrom(underlying_class) ? null : underlying_class; + return (Class<?>)javaProxy; } // Java types are ok with things being added and removed from their dicts as long as there isn't @@ -57,34 +57,35 @@ } @Override - protected void init() { - name = underlying_class.getName(); + protected void init(Class<?> forClass) { + name = forClass.getName(); // Strip the java fully qualified class name from Py classes in core if (name.startsWith("org.python.core.Py")) { name = name.substring("org.python.core.Py".length()).toLowerCase(); } dict = new PyStringMap(); - Class<?> baseClass = underlying_class.getSuperclass(); - if (PyObject.class.isAssignableFrom(underlying_class)) { + Class<?> baseClass = forClass.getSuperclass(); + if (PyObject.class.isAssignableFrom(forClass)) { // Non-exposed subclasses of PyObject use a simple linear mro to PyObject that ignores // their interfaces + underlying_class = forClass; computeLinearMro(baseClass); } else { - javaProxy = underlying_class; + javaProxy = forClass; objtype = PyType.fromClass(Class.class); // Wrapped Java types fill in their mro first using their base class and then all of // their interfaces. if (baseClass == null) { base = PyType.fromClass(PyObject.class); - } else if(underlying_class == Class.class) { + } else if (javaProxy == Class.class) { base = PyType.fromClass(PyType.class); } else { base = PyType.fromClass(baseClass); } - bases = new PyObject[1 + underlying_class.getInterfaces().length]; + bases = new PyObject[1 + forClass.getInterfaces().length]; bases[0] = base; for (int i = 1; i < bases.length; i++) { - bases[i] = PyType.fromClass(underlying_class.getInterfaces()[i - 1]); + bases[i] = PyType.fromClass(forClass.getInterfaces()[i - 1]); } Set<PyObject> seen = Generic.set(); List<PyObject> mros = Generic.list(); @@ -101,9 +102,9 @@ // PyReflected* can't call or access anything from non-public classes that aren't in // org.python.core - if (!Modifier.isPublic(underlying_class.getModifiers()) && + if (!Modifier.isPublic(forClass.getModifiers()) && !name.startsWith("org.python.core") && Options.respectJavaAccessibility) { - handleSuperMethodArgCollisions(); + handleSuperMethodArgCollisions(forClass); return; } @@ -113,9 +114,9 @@ Method[] methods; if (Options.respectJavaAccessibility) { // returns just the public methods - methods = underlying_class.getMethods(); + methods = forClass.getMethods(); } else { - methods = underlying_class.getDeclaredMethods(); + methods = forClass.getDeclaredMethods(); for (Method method : methods) { method.setAccessible(true); } @@ -205,9 +206,9 @@ Field[] fields; if (Options.respectJavaAccessibility) { // returns just the public fields - fields = underlying_class.getFields(); + fields = forClass.getFields(); } else { - fields = underlying_class.getDeclaredFields(); + fields = forClass.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); } @@ -281,11 +282,11 @@ Constructor<?>[] constructors; // No matter the security manager, trying to set the constructor on class to accessible // blows up - if (Options.respectJavaAccessibility || Class.class == underlying_class) { + if (Options.respectJavaAccessibility || Class.class == forClass) { // returns just the public constructors - constructors = underlying_class.getConstructors(); + constructors = forClass.getConstructors(); } else { - constructors = underlying_class.getDeclaredConstructors(); + constructors = forClass.getDeclaredConstructors(); for (Constructor<?> ctr : constructors) { ctr.setAccessible(true); } @@ -293,8 +294,8 @@ for (Constructor<?> ctr : constructors) { reflctr.addConstructor(ctr); } - if (PyObject.class.isAssignableFrom(underlying_class)) { - PyObject new_ = new PyNewWrapper(underlying_class, "__new__", -1, -1) { + if (PyObject.class.isAssignableFrom(forClass)) { + PyObject new_ = new PyNewWrapper(forClass, "__new__", -1, -1) { public PyObject new_impl(boolean init, PyType subtype, @@ -307,10 +308,10 @@ } else { dict.__setitem__("__init__", reflctr); } - for (Class<?> inner : underlying_class.getClasses()) { + for (Class<?> inner : forClass.getClasses()) { // Only add the class if there isn't something else with that name and it came from this // class - if (inner.getDeclaringClass() == underlying_class && + if (inner.getDeclaringClass() == forClass && dict.__finditem__(inner.getSimpleName()) == null) { // If this class is currently being loaded, any exposed types it contains won't have // set their builder in PyType yet, so add them to BOOTSTRAP_TYPES so they're @@ -323,16 +324,15 @@ } } for (Map.Entry<Class<?>, PyBuiltinMethod[]> entry : getCollectionProxies().entrySet()) { - if (entry.getKey() == underlying_class) { + if (entry.getKey() == forClass) { for (PyBuiltinMethod meth : entry.getValue()) { dict.__setitem__(meth.info.getName(), new PyMethodDescr(this, meth)); } } } - if (ClassDictInit.class.isAssignableFrom(underlying_class) - && underlying_class != ClassDictInit.class) { + if (ClassDictInit.class.isAssignableFrom(forClass) && forClass != ClassDictInit.class) { try { - Method m = underlying_class.getMethod("classDictInit", PyObject.class); + Method m = forClass.getMethod("classDictInit", PyObject.class); m.invoke(null, dict); // allow the class to override its name after it is loaded PyObject nameSpecified = dict.__finditem__("__name__"); @@ -344,10 +344,10 @@ } } if (baseClass != Object.class) { - has_set = getDescrMethod(underlying_class, "__set__", OO) != null - || getDescrMethod(underlying_class, "_doset", OO) != null; - has_delete = getDescrMethod(underlying_class, "__delete__", PyObject.class) != null - || getDescrMethod(underlying_class, "_dodel", PyObject.class) != null; + has_set = getDescrMethod(forClass, "__set__", OO) != null + || getDescrMethod(forClass, "_doset", OO) != null; + has_delete = getDescrMethod(forClass, "__delete__", PyObject.class) != null + || getDescrMethod(forClass, "_dodel", PyObject.class) != null; } else { // Pass __eq__ and __repr__ through to subclasses of Object PyBuiltinCallable equals = new PyBuiltinMethodNarrow("__eq__", 1, 1) { @@ -391,8 +391,8 @@ * drawback of failing when running in a security environment that didn't allow setting * accessibility, so this method replaced it. */ - private void handleSuperMethodArgCollisions() { - for (Class iface : underlying_class.getInterfaces()) { + private void handleSuperMethodArgCollisions(Class<?> forClass) { + for (Class<?> iface : forClass.getInterfaces()) { for (Method meth : iface.getMethods()) { if (!Modifier.isPublic(meth.getDeclaringClass().getModifiers())) { // Ignore methods from non-public interfaces as they're similarly bugged Modified: trunk/jython/src/org/python/core/PyType.java =================================================================== --- trunk/jython/src/org/python/core/PyType.java 2009-01-15 03:36:09 UTC (rev 5934) +++ trunk/jython/src/org/python/core/PyType.java 2009-01-16 08:11:48 UTC (rev 5935) @@ -45,7 +45,10 @@ /** __flags__, the type's options. */ private long tp_flags; - /** The underlying java class or null. */ + /** + * The Java Class instances of this type will be represented as, or null if it's determined by a + * base type. + */ protected Class<?> underlying_class; /** Whether it's a builtin type. */ @@ -202,12 +205,12 @@ PyType newtype; if (new_.for_type == metatype || metatype == PyType.fromClass(Class.class)) { newtype = new PyType(); // XXX set metatype - if(proxyClass != null) { - newtype.underlying_class = proxyClass; - } } else { newtype = new PyTypeDerived(metatype); } + if (proxyClass != null) { + newtype.javaProxy = proxyClass; + } if (dict instanceof PyStringMap) { dict = ((PyStringMap)dict).copy(); } else { @@ -340,10 +343,11 @@ } /** - * Called on builtin types after underlying_class has been set on them. Should fill in dict, - * name, mro, base and bases from the class. + * Called on builtin types for a particular class. Should fill in dict, name, mro, base and + * bases from the class. */ - protected void init() { + protected void init(Class<?> forClass) { + underlying_class = forClass; if (underlying_class == PyObject.class) { mro = new PyType[] {this}; } else { @@ -950,7 +954,7 @@ } // The types in Py.BOOTSTRAP_TYPES are initialized before their builders are assigned, // so do the work of addFromClass & fillFromClass after the fact - fromClass(builder.getTypeClass()).init(); + fromClass(builder.getTypeClass()).init(builder.getTypeClass()); } } @@ -985,9 +989,8 @@ } class_to_type.put(c, newtype); - newtype.underlying_class = c; newtype.builtin = true; - newtype.init(); + newtype.init(c); return newtype; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |