From: <zy...@us...> - 2010-04-25 17:46:40
|
Revision: 7045 http://jython.svn.sourceforge.net/jython/?rev=7045&view=rev Author: zyasoft Date: 2010-04-25 17:46:34 +0000 (Sun, 25 Apr 2010) Log Message: ----------- Added support for __copy__, __deepcopy__ of Java objects (fixes #1551); and (somewhat) related serialization of PyCode objects (fixes #1601); some unit testing; bumped bytecode magic Modified Paths: -------------- trunk/jython/Lib/test/test_java_integration.py trunk/jython/src/org/python/core/CompilerFlags.java trunk/jython/src/org/python/core/PyFunctionTable.java trunk/jython/src/org/python/core/PyJavaType.java trunk/jython/src/org/python/core/imp.java Modified: trunk/jython/Lib/test/test_java_integration.py =================================================================== --- trunk/jython/Lib/test/test_java_integration.py 2010-04-24 17:30:40 UTC (rev 7044) +++ trunk/jython/Lib/test/test_java_integration.py 2010-04-25 17:46:34 UTC (rev 7045) @@ -1,3 +1,4 @@ +import copy import operator import os import unittest @@ -5,6 +6,7 @@ import sys import re +from collections import deque from test import test_support from java.lang import (ClassCastException, ExceptionInInitializerError, String, Runnable, System, @@ -469,19 +471,78 @@ self.assertRaises(TypeError, __import__, "org.python.tests.mro.ConfusedOnImport") self.assertRaises(TypeError, GetitemAdder.addPostdefined) + +def roundtrip_serialization(obj): + """Returns a deep copy of an object, via serializing it + + see http://weblogs.java.net/blog/emcmanus/archive/2007/04/cloning_java_ob.html + """ + output = ByteArrayOutputStream() + serializer = CloneOutput(output) + serializer.writeObject(obj) + serializer.close() + input = ByteArrayInputStream(output.toByteArray()) + unserializer = CloneInput(input, serializer) # to get the list of classes seen, in order + return unserializer.readObject() + +class CloneOutput(ObjectOutputStream): + def __init__(self, output): + ObjectOutputStream.__init__(self, output) + self.classQueue = deque() + + def annotateClass(self, c): + self.classQueue.append(c) + + def annotateProxyClass(self, c): + self.classQueue.append(c) + +class CloneInput(ObjectInputStream): + + def __init__(self, input, output): + ObjectInputStream.__init__(self, input); + self.output = output; + + def resolveClass(self, obj_stream_class): + c = self.output.classQueue.popleft() + return c + + def resolveProxyClass(self, interfaceNames): + return self.output.classQueue.popleft() + + class SerializationTest(unittest.TestCase): def test_java_serialization(self): date_list = [Date(), Date()] - output = ByteArrayOutputStream() - serializer = ObjectOutputStream(output) - serializer.writeObject(date_list) - serializer.close() + self.assertEqual(date_list, roundtrip_serialization(date_list)) - input = ByteArrayInputStream(output.toByteArray()) - unserializer = ObjectInputStream(input) - self.assertEqual(date_list, unserializer.readObject()) + def test_java_serialization_pycode(self): + def universal_answer(): + return 42 + + serialized_code = roundtrip_serialization(universal_answer.func_code) + self.assertEqual(eval(serialized_code), universal_answer()) + + +class CopyTest(unittest.TestCase): + + def test_copy(self): + fruits = ArrayList(["apple", "banana"]) + fruits_copy = copy.copy(fruits) + self.assertEqual(fruits, fruits_copy) + self.assertNotEqual(id(fruits), id(fruits_copy)) + + def test_deepcopy(self): + items = ArrayList([ArrayList(["apple", "banana"]), + ArrayList(["trs80", "vic20"])]) + items_copy = copy.deepcopy(items) + self.assertEqual(items, items_copy) + self.assertNotEqual(id(items), id(items_copy)) + self.assertNotEqual(id(items[0]), id(items_copy[0])) + self.assertNotEqual(id(items[1]), id(items_copy[1])) + + class UnicodeTest(unittest.TestCase): def test_unicode_conversion(self): @@ -505,6 +566,7 @@ SecurityManagerTest, JavaWrapperCustomizationTest, SerializationTest, + CopyTest, UnicodeTest) if __name__ == "__main__": Modified: trunk/jython/src/org/python/core/CompilerFlags.java =================================================================== --- trunk/jython/src/org/python/core/CompilerFlags.java 2010-04-24 17:30:40 UTC (rev 7044) +++ trunk/jython/src/org/python/core/CompilerFlags.java 2010-04-25 17:46:34 UTC (rev 7045) @@ -6,11 +6,12 @@ package org.python.core; +import java.io.Serializable; import java.util.Set; import org.python.Version; -public class CompilerFlags { +public class CompilerFlags implements Serializable { // These flags don't mean anything to the code, only to the compiler public static final int PyCF_SOURCE_IS_UTF8 = 0x0100; public static final int PyCF_DONT_IMPLY_DEDENT = 0x0200; Modified: trunk/jython/src/org/python/core/PyFunctionTable.java =================================================================== --- trunk/jython/src/org/python/core/PyFunctionTable.java 2010-04-24 17:30:40 UTC (rev 7044) +++ trunk/jython/src/org/python/core/PyFunctionTable.java 2010-04-25 17:46:34 UTC (rev 7045) @@ -1,6 +1,8 @@ // Copyright (c) Corporation for National Research Initiatives package org.python.core; +import java.io.Serializable; + /** * An entry point for class that implements several function calls. * <P> @@ -9,6 +11,6 @@ * @see PyTableCode */ -public abstract class PyFunctionTable { +public abstract class PyFunctionTable implements Serializable { abstract public PyObject call_function(int index, PyFrame frame, ThreadState ts); } Modified: trunk/jython/src/org/python/core/PyJavaType.java =================================================================== --- trunk/jython/src/org/python/core/PyJavaType.java 2010-04-24 17:30:40 UTC (rev 7044) +++ trunk/jython/src/org/python/core/PyJavaType.java 2010-04-25 17:46:34 UTC (rev 7045) @@ -1,5 +1,15 @@ package org.python.core; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InvalidClassException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; @@ -10,9 +20,11 @@ import java.util.Enumeration; import java.util.EventListener; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.Queue; import org.python.core.util.StringUtil; import org.python.util.Generic; @@ -578,8 +590,113 @@ } }); } + + // TODO consider adding support for __copy__ of immutable Java objects + // (__deepcopy__ should just work since it uses serialization) + + if(forClass == Cloneable.class) { + addMethod(new PyBuiltinMethodNarrow("__copy__") { + @Override + public PyObject __call__() { + Object obj = self.getJavaProxy(); + Method clone; + // we could specialize so that for well known objects like collections, + // we don't use reflection to get around the fact that Object#clone is protected (but most subclasses are not); + // also we can potentially cache the method handle in the proxy instead of looking it up each time + try { + clone = obj.getClass().getMethod("clone"); + Object copy; + copy = clone.invoke(obj); + return Py.java2py(copy); + } catch (Exception ex) { + throw Py.TypeError("Could not copy Java object"); + } + } + }); + } + + if(forClass == Serializable.class) { + addMethod(new PyBuiltinMethodNarrow("__deepcopy__") { + @Override + public PyObject __call__(PyObject memo) { + Object obj = self.getJavaProxy(); + try { + Object copy = cloneX(obj); + return Py.java2py(copy); + } catch (Exception ex) { + throw Py.TypeError("Could not copy Java object"); + } + } + }); + + } } + // cloneX, CloneOutput, CloneInput are verbatim from Eamonn McManus' + // http://weblogs.java.net/blog/emcmanus/archive/2007/04/cloning_java_ob.html + // blog post on deep cloning through serialization - + // just what we need for __deepcopy__ support of Java objects + + private static <T> T cloneX(T x) throws IOException, ClassNotFoundException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + CloneOutput cout = new CloneOutput(bout); + cout.writeObject(x); + byte[] bytes = bout.toByteArray(); + + ByteArrayInputStream bin = new ByteArrayInputStream(bytes); + CloneInput cin = new CloneInput(bin, cout); + + @SuppressWarnings("unchecked") // thanks to Bas de Bakker for the tip! + T clone = (T) cin.readObject(); + return clone; + } + + private static class CloneOutput extends ObjectOutputStream { + Queue<Class<?>> classQueue = new LinkedList<Class<?>>(); + + CloneOutput(OutputStream out) throws IOException { + super(out); + } + + @Override + protected void annotateClass(Class<?> c) { + classQueue.add(c); + } + + @Override + protected void annotateProxyClass(Class<?> c) { + classQueue.add(c); + } + } + + private static class CloneInput extends ObjectInputStream { + private final CloneOutput output; + + CloneInput(InputStream in, CloneOutput output) throws IOException { + super(in); + this.output = output; + } + + @Override + protected Class<?> resolveClass(ObjectStreamClass osc) + throws IOException, ClassNotFoundException { + Class<?> c = output.classQueue.poll(); + String expected = osc.getName(); + String found = (c == null) ? null : c.getName(); + if (!expected.equals(found)) { + throw new InvalidClassException("Classes desynchronized: " + + "found " + found + " when expecting " + expected); + } + return c; + } + + @Override + protected Class<?> resolveProxyClass(String[] interfaceNames) + throws IOException, ClassNotFoundException { + return output.classQueue.poll(); + } + } + /** * Private, protected or package protected classes that implement public interfaces or extend * public classes can't have their implementations of the methods of their supertypes called Modified: trunk/jython/src/org/python/core/imp.java =================================================================== --- trunk/jython/src/org/python/core/imp.java 2010-04-24 17:30:40 UTC (rev 7044) +++ trunk/jython/src/org/python/core/imp.java 2010-04-25 17:46:34 UTC (rev 7045) @@ -21,7 +21,7 @@ private static final String UNKNOWN_SOURCEFILE = "<unknown>"; - private static final int APIVersion = 27; + private static final int APIVersion = 28; public static final int NO_MTIME = -1; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |