Skip to content

Commit

Permalink
underlying_class is the class of instances of this type, so PyJavaTyp…
Browse files Browse the repository at this point in the history
…e 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.
  • Loading branch information
groves committed Jan 16, 2009
1 parent 5c64b5f commit bc2d54b
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 45 deletions.
31 changes: 29 additions & 2 deletions Lib/test/test_java_subclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -29,6 +29,22 @@ def call(pyself, extraarg):
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'''
Expand Down Expand Up @@ -89,6 +105,17 @@ class MultiJava(Dimension, Color):
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):
Expand Down
2 changes: 1 addition & 1 deletion src/org/python/core/PyArray.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static final PyObject array_new(PyNewWrapper new_, boolean init, PyType subtype,
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());
Expand Down
64 changes: 32 additions & 32 deletions src/org/python/core/PyJavaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public PyJavaType() {

@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
Expand All @@ -57,34 +57,35 @@ PyObject[] compute_mro() {
}

@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();
Expand All @@ -101,9 +102,9 @@ protected void init() {

// 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;
}

Expand All @@ -113,9 +114,9 @@ protected void init() {
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);
}
Expand Down Expand Up @@ -205,9 +206,9 @@ && lookup(nmethname) == null) {
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);
}
Expand Down Expand Up @@ -281,20 +282,20 @@ && lookup(nmethname) == null) {
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);
}
}
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,
Expand All @@ -307,10 +308,10 @@ public PyObject new_impl(boolean init,
} 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
Expand All @@ -323,16 +324,15 @@ public PyObject new_impl(boolean init,
}
}
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__");
Expand All @@ -344,10 +344,10 @@ public PyObject new_impl(boolean init,
}
}
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) {
Expand Down Expand Up @@ -391,8 +391,8 @@ public PyObject __call__() {
* 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
Expand Down
23 changes: 13 additions & 10 deletions src/org/python/core/PyType.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ public class PyType extends PyObject implements Serializable {
/** __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. */
Expand Down Expand Up @@ -202,12 +205,12 @@ public static PyObject newType(PyNewWrapper new_, PyType metatype, String name,
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 {
Expand Down Expand Up @@ -340,10 +343,11 @@ private static PyObject invoke_new_(PyObject new_, PyType type, boolean init, Py
}

/**
* 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 {
Expand Down Expand Up @@ -950,7 +954,7 @@ public static void addBuilder(Class<?> forClass, TypeBuilder builder) {
}
// 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());
}
}

Expand Down Expand Up @@ -985,9 +989,8 @@ private static PyType createType(Class<?> c) {
}

class_to_type.put(c, newtype);
newtype.underlying_class = c;
newtype.builtin = true;
newtype.init();
newtype.init(c);
return newtype;
}

Expand Down

0 comments on commit bc2d54b

Please sign in to comment.