[Jython-checkins] jython: Various fixes for working with array.array objects. Fixes #1780, #2423, #2451
jim.baker
jython-checkins at python.org
Wed Jan 6 16:42:26 EST 2016
https://hg.python.org/jython/rev/49d498968f34
changeset: 7851:49d498968f34
user: Jim Baker <jim.baker at rackspace.com>
date: Wed Jan 06 14:42:21 2016 -0700
summary:
Various fixes for working with array.array objects. Fixes #1780, #2423, #2451
Now supports for array.array/jarray.array objects:
* Boxes primitive arrays when converting to Object[]
* Usage in varargs position
* Raises TypeError if attempt to hash
files:
ACKNOWLEDGMENTS | 2 +
Lib/test/test_array_jy.py | 84 +++++++++++--
src/org/python/core/PyArray.java | 26 ++++-
src/org/python/core/ReflectedArgs.java | 44 ++++--
4 files changed, 125 insertions(+), 31 deletions(-)
diff --git a/ACKNOWLEDGMENTS b/ACKNOWLEDGMENTS
--- a/ACKNOWLEDGMENTS
+++ b/ACKNOWLEDGMENTS
@@ -170,6 +170,8 @@
Stephen Drake
Jan Vorwerk
Eli Oxman
+ Robert Patrick
+ Kevin Edwards
Local Variables:
mode: indented-text
diff --git a/Lib/test/test_array_jy.py b/Lib/test/test_array_jy.py
--- a/Lib/test/test_array_jy.py
+++ b/Lib/test/test_array_jy.py
@@ -1,28 +1,40 @@
-# The jarray module is being phased out, with all functionality
-# now available in the array module.
-from __future__ import with_statement
+# The jarray module is mostly phased out, but it is still has one
+# constructor function - zeros - that is not directly available in the
+# array module.
+#
+# The equivalent of
+# jarray.zeros(n, typecode)
+# is
+# array.array(typecode, itertools.repeat(0, n))
+#
+# however itertools.repeat does not define __len__ and therefore we
+# have to first build out the zero sequence separately, then copy
+# over. This overhead could be significant for large arrays.
+
+import jarray
+import itertools
import os
import unittest
+from array import array
from test import test_support
-from array import array
+from java.lang import String as JString
+from java.util import Arrays
+
class ArrayJyTestCase(unittest.TestCase):
- def test_jarray(self): # until it is fully formally removed
- # While jarray is still being phased out, just flex the initializers.
- # The rest of the test for array will catch all the big problems.
- import jarray
+ def test_jarray(self):
from java.lang import String
- jarray.array(range(5), 'i')
- jarray.array([String("a"), String("b"), String("c")], String)
- jarray.zeros(5, 'i')
- jarray.zeros(5, String)
+
+ self.assertEqual(sum(jarray.array(range(5), 'i')), 10)
+ self.assertEqual(','.join(jarray.array([String("a"), String("b"), String("c")], String)), u'a,b,c')
+ self.assertEqual(sum(jarray.zeros(5, 'i')), 0)
+ self.assertEqual([x for x in jarray.zeros(5, String)], [None, None, None, None, None])
def test_java_object_arrays(self):
from array import zeros
from java.lang import String
from java.lang.reflect import Array
- from java.util import Arrays
jStringArr = array(String, [String("a"), String("b"), String("c")])
self.assert_(
Arrays.equals(jStringArr.typecode, 'java.lang.String'),
@@ -91,9 +103,53 @@
ar += Foo()
self.assertEqual('__radd__', ar)
+ def test_nonhashability(self):
+ "array.array objects are not hashable"
+ # http://bugs.jython.org/issue2451
+ a = array('b', itertools.repeat(0, 100))
+ with self.assertRaisesRegexp(TypeError, r"unhashable type: 'array.array'"):
+ hash(a)
+
+
+class ArrayConversionTestCase(unittest.TestCase):
+
+ # Covers bugs raised in
+ # http://bugs.jython.org/issue1780,
+ # http://bugs.jython.org/issue2423
+
+ def assertAsList(self, a, b):
+ self.assertEqual(Arrays.asList(a), b)
+
+ def test_boxing_conversion(self):
+ "array objects support boxing, as they did in Jython 2.1"
+ from java.lang import Integer
+
+ self.assertAsList(jarray.array([1, 2, 3, 4, 5], Integer), [1, 2, 3, 4, 5])
+ self.assertAsList(array(Integer, [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+ self.assertAsList(jarray.array([1, 2, 3, 4, 5], "i"), [1, 2, 3, 4, 5])
+ self.assertAsList(array("i", [1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+
+ def test_object_varargs(self):
+ "array.array objects can be used in the varargs position, with primitive boxing"
+ a = array('i', range(5, 10))
+ self.assertEqual(
+ 'arg 0=5, arg 1=6, arg 2=7, arg 3=8, arg 4=9',
+ JString.format('arg 0=%d, arg 1=%d, arg 2=%d, arg 3=%d, arg 4=%d', [5, 6, 7, 8, 9]))
+
+ def test_assignable_varargs(self):
+ "array.array objects can be used in the varargs position"
+ # modified from test case in http://bugs.jython.org/issue2423;
+ from java.lang import Class
+ from java.net import URL, URLClassLoader
+ params = jarray.array([URL], Class)
+ # URLClassLoader.addURL is protected, so workaround via reflection
+ method = URLClassLoader.getDeclaredMethod('addURL', params)
+ # and verify we got the right method after all
+ self.assertEqual(method.name, "addURL")
+
def test_main():
- tests = [ToFromfileTestCase, ArrayOpsTestCase]
+ tests = [ToFromfileTestCase, ArrayOpsTestCase, ArrayConversionTestCase]
if test_support.is_jython:
tests.extend([ArrayJyTestCase])
test_support.run_unittest(*tests)
diff --git a/src/org/python/core/PyArray.java b/src/org/python/core/PyArray.java
--- a/src/org/python/core/PyArray.java
+++ b/src/org/python/core/PyArray.java
@@ -209,6 +209,16 @@
return seq___eq__(o);
}
+ @Override
+ public int hashCode() {
+ return array___hash__();
+ }
+
+ @ExposedMethod
+ final int array___hash__() {
+ throw Py.TypeError(String.format("unhashable type: '%.200s'", getType().fastGetName()));
+ }
+
@ExposedMethod(type = MethodType.BINARY)
final PyObject array___lt__(PyObject o) {
return seq___lt__(o);
@@ -450,7 +460,10 @@
*/
@Override
public Object __tojava__(Class<?> c) {
- if (c == Object.class || (c.isArray() && c.getComponentType().isAssignableFrom(type))) {
+ boolean isArray = c.isArray();
+ Class componentType = c.getComponentType();
+
+ if (c == Object.class || (isArray && componentType.isAssignableFrom(type))) {
if (delegate.capacity != delegate.size) {
// when unboxing, need to shrink the array first, otherwise incorrect
// results to Java
@@ -459,9 +472,20 @@
return data;
}
}
+
+ // rebox: this array is made of primitives but converting to Object[]
+ if (isArray && componentType == Object.class) {
+ Object[] boxed = new Object[delegate.size];
+ for (int i = 0; i < delegate.size; i++) {
+ boxed[i] = Array.get(data, i);
+ }
+ return boxed;
+ }
+
if (c.isInstance(this)) {
return this;
}
+
return Py.NoConversion;
}
diff --git a/src/org/python/core/ReflectedArgs.java b/src/org/python/core/ReflectedArgs.java
--- a/src/org/python/core/ReflectedArgs.java
+++ b/src/org/python/core/ReflectedArgs.java
@@ -111,22 +111,7 @@
// if that's what they need to do ;)
if (isVarArgs) {
- if (pyArgs.length == 0 || !(pyArgs[pyArgs.length - 1] instanceof PySequenceList)) {
- int non_varargs_len = n - 1;
- if (pyArgs.length >= non_varargs_len) {
- PyObject[] boxedPyArgs = new PyObject[n];
- for (int i = 0; i < non_varargs_len; i++) {
- boxedPyArgs[i] = pyArgs[i];
- }
- int varargs_len = pyArgs.length - non_varargs_len;
- PyObject[] varargs = new PyObject[varargs_len];
- for (int i = 0; i < varargs_len; i++) {
- varargs[i] = pyArgs[non_varargs_len + i];
- }
- boxedPyArgs[non_varargs_len] = new PyList(varargs);
- pyArgs = boxedPyArgs;
- }
- }
+ pyArgs = ensureBoxedVarargs(pyArgs, n);
}
if (pyArgs.length != n) {
@@ -164,6 +149,33 @@
return true;
}
+ /* Boxes argument in the varargs position if not already boxed */
+ private PyObject[] ensureBoxedVarargs(PyObject[] pyArgs, int n) {
+ if (pyArgs.length == 0) {
+ return pyArgs;
+ }
+ PyObject lastArg = pyArgs[pyArgs.length - 1];
+ if (lastArg instanceof PySequenceList || lastArg instanceof PyArray) {
+ // FIXME also check if lastArg is sequence-like
+ return pyArgs; // will be boxed in an array once __tojava__ is called
+ }
+ int non_varargs_len = n - 1;
+ if (pyArgs.length < non_varargs_len) {
+ return pyArgs;
+ }
+ PyObject[] boxedPyArgs = new PyObject[n];
+ for (int i = 0; i < non_varargs_len; i++) {
+ boxedPyArgs[i] = pyArgs[i];
+ }
+ int varargs_len = pyArgs.length - non_varargs_len;
+ PyObject[] varargs = new PyObject[varargs_len];
+ for (int i = 0; i < varargs_len; i++) {
+ varargs[i] = pyArgs[non_varargs_len + i];
+ }
+ boxedPyArgs[non_varargs_len] = new PyList(varargs);
+ return boxedPyArgs;
+ }
+
public static int precedence(Class<?> arg) {
if (arg == Object.class) {
return 3000;
--
Repository URL: https://hg.python.org/jython
More information about the Jython-checkins
mailing list