From: <cg...@us...> - 2009-01-17 20:35:14
|
Revision: 5940 http://jython.svn.sourceforge.net/jython/?rev=5940&view=rev Author: cgroves Date: 2009-01-17 20:35:09 +0000 (Sat, 17 Jan 2009) Log Message: ----------- Add some tests for overriding attribute lookup and adding mapping methods to wrapped Java types. Modified Paths: -------------- trunk/jython/Lib/test/test_java_integration.py trunk/jython/src/org/python/core/PyBuiltinMethodNarrow.java trunk/jython/src/org/python/core/PyJavaType.java trunk/jython/src/org/python/core/PyType.java Added Paths: ----------- trunk/jython/tests/java/org/python/tests/CustomizableMapHolder.java Modified: trunk/jython/Lib/test/test_java_integration.py =================================================================== --- trunk/jython/Lib/test/test_java_integration.py 2009-01-17 18:42:14 UTC (rev 5939) +++ trunk/jython/Lib/test/test_java_integration.py 2009-01-17 20:35:09 UTC (rev 5940) @@ -1,3 +1,4 @@ +import operator import os import unittest import subprocess @@ -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): @@ -62,7 +63,16 @@ 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): self.orig_stdout = sys.stdout @@ -338,6 +348,31 @@ "-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, @@ -351,7 +386,8 @@ BigNumberTest, JavaStringTest, JavaDelegationTest, - SecurityManagerTest) + SecurityManagerTest, + JavaWrapperCustomizationTest) if __name__ == "__main__": test_main() Modified: trunk/jython/src/org/python/core/PyBuiltinMethodNarrow.java =================================================================== --- trunk/jython/src/org/python/core/PyBuiltinMethodNarrow.java 2009-01-17 18:42:14 UTC (rev 5939) +++ trunk/jython/src/org/python/core/PyBuiltinMethodNarrow.java 2009-01-17 20:35:09 UTC (rev 5940) @@ -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)); } Modified: trunk/jython/src/org/python/core/PyJavaType.java =================================================================== --- trunk/jython/src/org/python/core/PyJavaType.java 2009-01-17 18:42:14 UTC (rev 5939) +++ trunk/jython/src/org/python/core/PyJavaType.java 2009-01-17 20:35:09 UTC (rev 5940) @@ -349,7 +349,7 @@ 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); } } } @@ -373,29 +373,26 @@ || 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)); + }); } } @@ -522,8 +519,8 @@ } 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(){ @@ -532,8 +529,8 @@ } 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(){ @@ -545,21 +542,21 @@ 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); @@ -570,14 +567,14 @@ 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())); } @@ -585,38 +582,38 @@ 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))); @@ -629,20 +626,20 @@ 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); Modified: trunk/jython/src/org/python/core/PyType.java =================================================================== --- trunk/jython/src/org/python/core/PyType.java 2009-01-17 18:42:14 UTC (rev 5939) +++ trunk/jython/src/org/python/core/PyType.java 2009-01-17 20:35:09 UTC (rev 5940) @@ -1067,6 +1067,15 @@ 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 " Added: trunk/jython/tests/java/org/python/tests/CustomizableMapHolder.java =================================================================== --- trunk/jython/tests/java/org/python/tests/CustomizableMapHolder.java (rev 0) +++ trunk/jython/tests/java/org/python/tests/CustomizableMapHolder.java 2009-01-17 20:35:09 UTC (rev 5940) @@ -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); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |