Skip to content

Commit

Permalink
Add some tests for overriding attribute lookup and adding mapping met…
Browse files Browse the repository at this point in the history
…hods to wrapped Java types.
  • Loading branch information
groves committed Jan 17, 2009
1 parent d64fd0e commit 010e27c
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 30 deletions.
40 changes: 38 additions & 2 deletions Lib/test/test_java_integration.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import operator
import os
import unittest
import subprocess
Expand All @@ -17,7 +18,7 @@
from javax.swing.tree import TreePath

from org.python.core.util import FileUtil
from org.python.tests import BeanImplementation, Listenable
from org.python.tests import BeanImplementation, Child, Listenable, CustomizableMapHolder

class InstantiationTest(unittest.TestCase):
def test_cant_instantiate_abstract(self):
Expand Down Expand Up @@ -62,6 +63,15 @@ def __init__(bself):
self.assertEquals("name", bself.getName())
SubBean()

def test_inheriting_half_bean(self):
c = Child()
self.assertEquals("blah", c.value)
c.value = "bleh"
self.assertEquals("bleh", c.value)
self.assertEquals(7, c.id)
c.id = 16
self.assertEquals(16, c.id)


class SysIntegrationTest(unittest.TestCase):
def setUp(self):
Expand Down Expand Up @@ -338,6 +348,31 @@ def test_nonexistent_import_with_security(self):
"-J-Djava.security.manager", "-J-Djava.security.policy=%s" % policy, script]),
0)

class JavaWrapperCustomizationTest(unittest.TestCase):
def tearDown(self):
CustomizableMapHolder.clearAdditions()

def test_adding_item_access(self):
m = CustomizableMapHolder()
self.assertRaises(TypeError, operator.getitem, m, "initial")
CustomizableMapHolder.addGetitem()
self.assertEquals(m.held["initial"], m["initial"])
# dict would throw a KeyError here, but Map returns null for a missing key
self.assertEquals(None, m["nonexistent"])
self.assertRaises(TypeError, operator.setitem, m, "initial")
CustomizableMapHolder.addSetitem()
m["initial"] = 12
self.assertEquals(12, m["initial"])

def test_adding_attributes(self):
m = CustomizableMapHolder()
self.assertRaises(AttributeError, getattr, m, "initial")
CustomizableMapHolder.addGetattribute()
self.assertEquals(7, m.held["initial"], "Existing fields should still be accessible")
self.assertEquals(7, m.initial)
self.assertEquals(None, m.nonexistent, "Nonexistent fields should be passed on to the Map")


def test_main():
test_support.run_unittest(InstantiationTest,
BeanTest,
Expand All @@ -351,7 +386,8 @@ def test_main():
BigNumberTest,
JavaStringTest,
JavaDelegationTest,
SecurityManagerTest)
SecurityManagerTest,
JavaWrapperCustomizationTest)

if __name__ == "__main__":
test_main()
18 changes: 18 additions & 0 deletions src/org/python/core/PyBuiltinMethodNarrow.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package org.python.core;

public abstract class PyBuiltinMethodNarrow extends PyBuiltinMethod {
/**
* Creates a method for the given name that takes no arguments.
*/
protected PyBuiltinMethodNarrow(String name) {
this(name, 0);
}

/**
* Creates a method for the <code>name<code> that takes exactly <code>numArgs</code> arguments.
*/
protected PyBuiltinMethodNarrow(String name, int numArgs) {
this(name, numArgs, numArgs);
}


/**
* Creates a method for the given name that takes at least <code>minArgs</code> and at most
* <code>maxArgs</code> arguments.
*/
protected PyBuiltinMethodNarrow(String name, int minArgs, int maxArgs) {
super(null, new DefaultInfo(name, minArgs, maxArgs));
}
Expand Down
53 changes: 25 additions & 28 deletions src/org/python/core/PyJavaType.java
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ public PyObject new_impl(boolean init,
for (Map.Entry<Class<?>, PyBuiltinMethod[]> entry : getCollectionProxies().entrySet()) {
if (entry.getKey() == forClass) {
for (PyBuiltinMethod meth : entry.getValue()) {
dict.__setitem__(meth.info.getName(), new PyMethodDescr(this, meth));
addMethod(meth);
}
}
}
Expand All @@ -373,29 +373,26 @@ public PyObject new_impl(boolean init,
|| getDescrMethod(forClass, "_dodel", PyObject.class) != null;
} else {
// Pass __eq__ and __repr__ through to subclasses of Object
PyBuiltinCallable equals = new PyBuiltinMethodNarrow("__eq__", 1, 1) {
addMethod(new PyBuiltinMethodNarrow("__eq__", 1) {
@Override
public PyObject __call__(PyObject o) {
Object proxy = self.getJavaProxy();
Object oAsJava = o.__tojava__(proxy.getClass());
return proxy.equals(oAsJava) ? Py.True : Py.False;
}
};
dict.__setitem__("__eq__", new PyMethodDescr(this, equals));
PyBuiltinCallable hash = new PyBuiltinMethodNarrow("__hash__", 0, 0) {
});
addMethod(new PyBuiltinMethodNarrow("__hash__") {
@Override
public PyObject __call__() {
return Py.newInteger(self.getJavaProxy().hashCode());
}
};
dict.__setitem__("__hash__", new PyMethodDescr(this, hash));
PyBuiltinCallable repr = new PyBuiltinMethodNarrow("__repr__", 0, 0) {
});
addMethod(new PyBuiltinMethodNarrow("__repr__") {
@Override
public PyObject __call__() {
return Py.newString(self.getJavaProxy().toString());
}
};
dict.__setitem__("__repr__", new PyMethodDescr(this, repr));
});
}
}

Expand Down Expand Up @@ -522,8 +519,8 @@ public PyObject __iternext__() {
}

private static class ListMethod extends PyBuiltinMethodNarrow {
protected ListMethod(String name, int minArgs, int maxArgs) {
super(name, minArgs, maxArgs);
protected ListMethod(String name, int numArgs) {
super(name, numArgs);
}

protected List<Object> asList(){
Expand All @@ -532,8 +529,8 @@ protected List<Object> asList(){
}

private static class MapMethod extends PyBuiltinMethodNarrow {
protected MapMethod(String name, int minArgs, int maxArgs) {
super(name, minArgs, maxArgs);
protected MapMethod(String name, int numArgs) {
super(name, numArgs);
}

protected Map<Object, Object> asMap(){
Expand All @@ -545,21 +542,21 @@ private static Map<Class<?>, PyBuiltinMethod[]> getCollectionProxies() {
if (collectionProxies == null) {
collectionProxies = Generic.map();

PyBuiltinMethodNarrow iterableProxy = new PyBuiltinMethodNarrow("__iter__", 0, 0) {
PyBuiltinMethodNarrow iterableProxy = new PyBuiltinMethodNarrow("__iter__") {
public PyObject __call__() {
return new IteratorIter(((Iterable)self.getJavaProxy()));
}
};
collectionProxies.put(Iterable.class, new PyBuiltinMethod[] {iterableProxy});

PyBuiltinMethodNarrow lenProxy = new PyBuiltinMethodNarrow("__len__", 0, 0) {
PyBuiltinMethodNarrow lenProxy = new PyBuiltinMethodNarrow("__len__") {
@Override
public PyObject __call__() {
return Py.newInteger(((Collection<?>)self.getJavaProxy()).size());
}
};

PyBuiltinMethodNarrow containsProxy = new PyBuiltinMethodNarrow("__contains__", 1, 1) {
PyBuiltinMethodNarrow containsProxy = new PyBuiltinMethodNarrow("__contains__") {
@Override
public PyObject __call__(PyObject obj) {
Object other = obj.__tojava__(Object.class);
Expand All @@ -570,53 +567,53 @@ public PyObject __call__(PyObject obj) {
collectionProxies.put(Collection.class, new PyBuiltinMethod[] {lenProxy,
containsProxy});

PyBuiltinMethodNarrow iteratorProxy = new PyBuiltinMethodNarrow("__iter__", 0, 0) {
PyBuiltinMethodNarrow iteratorProxy = new PyBuiltinMethodNarrow("__iter__") {
public PyObject __call__() {
return new IteratorIter(((Iterator)self.getJavaProxy()));
}
};
collectionProxies.put(Iterator.class, new PyBuiltinMethod[] {iteratorProxy});

PyBuiltinMethodNarrow enumerationProxy = new PyBuiltinMethodNarrow("__iter__", 0, 0) {
PyBuiltinMethodNarrow enumerationProxy = new PyBuiltinMethodNarrow("__iter__") {
public PyObject __call__() {
return new EnumerationIter(((Enumeration)self.getJavaProxy()));
}
};
collectionProxies.put(Enumeration.class, new PyBuiltinMethod[] {enumerationProxy});

// Map doesn't extend Collection, so it needs its own version of len, iter and contains
PyBuiltinMethodNarrow mapLenProxy = new MapMethod("__len__", 0, 0) {
PyBuiltinMethodNarrow mapLenProxy = new MapMethod("__len__", 0) {
@Override
public PyObject __call__() {
return Py.java2py(asMap().size());
}
};
PyBuiltinMethodNarrow mapIterProxy = new MapMethod("__iter__", 0, 0) {
PyBuiltinMethodNarrow mapIterProxy = new MapMethod("__iter__", 0) {
@Override
public PyObject __call__() {
return new IteratorIter(asMap().keySet());
}
};
PyBuiltinMethodNarrow mapContainsProxy = new MapMethod("__contains__", 1, 1) {
PyBuiltinMethodNarrow mapContainsProxy = new MapMethod("__contains__", 1) {
public PyObject __call__(PyObject obj) {
Object other = obj.__tojava__(Object.class);
return asMap().containsKey(other) ? Py.True : Py.False;
}
};
PyBuiltinMethodNarrow mapGetProxy = new MapMethod("__getitem__", 1, 1) {
PyBuiltinMethodNarrow mapGetProxy = new MapMethod("__getitem__", 1) {
@Override
public PyObject __call__(PyObject key) {
return Py.java2py(asMap().get(Py.tojava(key, Object.class)));
}
};
PyBuiltinMethodNarrow mapPutProxy = new MapMethod("__setitem__", 2, 2) {
PyBuiltinMethodNarrow mapPutProxy = new MapMethod("__setitem__", 2) {
@Override
public PyObject __call__(PyObject key, PyObject value) {
return Py.java2py(asMap().put(Py.tojava(key, Object.class),
Py.tojava(value, Object.class)));
}
};
PyBuiltinMethodNarrow mapRemoveProxy = new MapMethod("__delitem__", 1, 1) {
PyBuiltinMethodNarrow mapRemoveProxy = new MapMethod("__delitem__", 1) {
@Override
public PyObject __call__(PyObject key) {
return Py.java2py(asMap().remove(Py.tojava(key, Object.class)));
Expand All @@ -629,20 +626,20 @@ public PyObject __call__(PyObject key) {
mapPutProxy,
mapRemoveProxy});

PyBuiltinMethodNarrow listGetProxy = new ListMethod("__getitem__", 1, 1) {
PyBuiltinMethodNarrow listGetProxy = new ListMethod("__getitem__", 1) {
@Override
public PyObject __call__(PyObject key) {
return new ListIndexDelegate(asList()).checkIdxAndGetItem(key);
}
};
PyBuiltinMethodNarrow listSetProxy = new ListMethod("__setitem__", 2, 2) {
PyBuiltinMethodNarrow listSetProxy = new ListMethod("__setitem__", 2) {
@Override
public PyObject __call__(PyObject key, PyObject value) {
new ListIndexDelegate(asList()).checkIdxAndSetItem(key, value);
return Py.None;
}
};
PyBuiltinMethodNarrow listRemoveProxy = new ListMethod("__delitem__", 1, 1) {
PyBuiltinMethodNarrow listRemoveProxy = new ListMethod("__delitem__", 1) {
@Override
public PyObject __call__(PyObject key) {
new ListIndexDelegate(asList()).checkIdxAndDelItem(key);
Expand Down
9 changes: 9 additions & 0 deletions src/org/python/core/PyType.java
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,15 @@ public void __setattr__(String name, PyObject value) {
type___setattr__(name, value);
}

/**
* Adds the given method to this type's dict under its name in its descriptor. If there's an
* existing item in the dict, it's replaced.
*/
public void addMethod(PyBuiltinMethod meth) {
PyMethodDescr pmd = meth.makeDescriptor(this);
dict.__setitem__(pmd.getName(), pmd);
}

protected void checkSetattr() {
if (builtin) {
throw Py.TypeError(String.format("can't set attributes of built-in/extension type "
Expand Down
75 changes: 75 additions & 0 deletions tests/java/org/python/tests/CustomizableMapHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.python.tests;

import java.util.Map;

import org.python.core.Py;
import org.python.core.PyBuiltinMethod;
import org.python.core.PyBuiltinMethodNarrow;
import org.python.core.PyException;
import org.python.core.PyObject;
import org.python.core.PyType;
import org.python.util.Generic;


public class CustomizableMapHolder {

public Map<String, Integer> held = Generic.map();

{
held.put("initial", 7);
}

public static void clearAdditions() {
PyObject dict = PyType.fromClass(CustomizableMapHolder.class).fastGetDict();
for (String name : new String[] {"__getitem__", "__setitem__", "__getattribute__"}) {
if (dict.__finditem__(name) != null) {
dict.__delitem__(name);
}
}
}

public static void addGetitem() {
PyBuiltinMethod meth = new PyBuiltinMethodNarrow("__getitem__", 1) {
@Override
public PyObject __call__(PyObject arg) {
CustomizableMapHolder inst = Py.tojava(self, CustomizableMapHolder.class);
String key = Py.tojava(arg, String.class);
return Py.java2py(inst.held.get(key));
}
};
PyType.fromClass(CustomizableMapHolder.class).addMethod(meth);
}

public static void addSetitem() {
PyBuiltinMethod meth = new PyBuiltinMethodNarrow("__setitem__", 2) {
@Override
public PyObject __call__(PyObject arg1, PyObject arg2) {
CustomizableMapHolder inst = Py.tojava(self, CustomizableMapHolder.class);
String key = Py.tojava(arg1, String.class);
Integer val = Py.tojava(arg2, Integer.class);
inst.held.put(key, val);
return Py.None;
}
};
PyType.fromClass(CustomizableMapHolder.class).addMethod(meth);
}

public static void addGetattribute() {
final PyObject objectGetattribute = PyObject.TYPE.__getattr__("__getattribute__");
PyBuiltinMethod meth = new PyBuiltinMethodNarrow("__getattribute__", 1) {
@Override
public PyObject __call__(PyObject name) {
try {
return objectGetattribute.__call__(self, name);
} catch (PyException pye) {
if (!Py.matchException(pye, Py.AttributeError)) {
throw pye;
}
}
CustomizableMapHolder inst = Py.tojava(self, CustomizableMapHolder.class);
return Py.java2py(inst.held.get(Py.tojava(name, String.class)));
}
};
PyType.fromClass(CustomizableMapHolder.class).addMethod(meth);
}
}

0 comments on commit 010e27c

Please sign in to comment.