Index: src/org/python/core/AbstractArray.java =================================================================== --- src/org/python/core/AbstractArray.java (revision 5372) +++ src/org/python/core/AbstractArray.java (working copy) @@ -383,7 +383,7 @@ } throw new IndexOutOfBoundsException("start and stop must follow: 0 <= start <= stop <= " + - (this.size - 1) + ", but found start= " + start + " and stop=" + stop); + this.size + ", but found start= " + start + " and stop=" + stop); } /** Index: src/org/python/core/CollectionProxy.java =================================================================== --- src/org/python/core/CollectionProxy.java (revision 5372) +++ src/org/python/core/CollectionProxy.java (working copy) @@ -2,6 +2,7 @@ package org.python.core; +import java.util.Arrays; import java.util.Collection; import java.util.Dictionary; import java.util.Enumeration; @@ -198,43 +199,181 @@ public PyObject __finditem__(int key) { try { + if (key < 0) { + key += this.proxy.size(); + } return Py.java2py(this.proxy.get(key)); } catch (IndexOutOfBoundsException exc) { - return null; + throw Py.IndexError("index out of range: " + String.valueOf(key)); } } + protected PyObject __finditem__(int start, int stop, int step, int n) { + if(step > 0 && stop < start) { + stop = start; + } + PyObject[] newList = new PyObject[n]; + if(step == 1) { + for (int i = start; i < stop; i++) + { + newList[i-start] = Py.java2py(this.proxy.get(i)); + } + return new PyList(newList); + } + int j = 0; + for(int i = start; j < n; i += step) { + newList[j] = Py.java2py(this.proxy.get(i)); + j++; + } + return new PyList(newList); + } + public PyObject __finditem__(PyObject key) { if (key instanceof PyInteger) { return __finditem__(((PyInteger) key).getValue()); + } else if (key instanceof PySlice) { + PySlice slice = (PySlice)key; + int indices[] = slice.indicesEx(this.proxy.size()); + return __finditem__(indices[0], indices[1], indices[2], indices[3]); } else { - throw Py.TypeError("only integer keys accepted"); + throw Py.TypeError("only integer or slice keys accepted"); } } public void __setitem__(int key, PyObject value) { - this.proxy.set(key, Py.tojava(value, Object.class)); + if (key < 0) { + key += this.proxy.size(); + } + try { + this.proxy.set(key, Py.tojava(value, Object.class)); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } } public void __setitem__(PyObject key, PyObject value) { if (key instanceof PyInteger) { __setitem__(((PyInteger) key).getValue(), value); + } else if (key instanceof PySlice) { + PySlice slice = (PySlice)key; + int indices[] = slice.indicesEx(this.proxy.size()); + __setitem__(slice, indices[0], indices[1], indices[2], indices[3], value); } else { throw Py.TypeError("only integer keys accepted"); } } public void __delitem__(int key) { - this.proxy.remove(key); + if (key < 0) { + key += this.proxy.size(); + } + try { + this.proxy.remove(key); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } } + protected void __delitem__(int start, int stop, int step, int n) { + if(step == 1) { + for (int i = stop - 1; i >= start; i--) { + this.proxy.remove(i); + } + } else if(step > 1) { + for(int i = start; i < stop; i += step) { + this.proxy.remove(i); + i--; + stop--; + } + } else if(step < 0) { + for(int i = start; i >= 0 && i >= stop; i += step) { + this.proxy.remove(i); + } + } + } + public void __delitem__(PyObject key) { if (key instanceof PyInteger) { __delitem__(((PyInteger) key).getValue()); + } else if (key instanceof PySlice) { + PySlice slice = (PySlice)key; + int indices[] = slice.indicesEx(this.proxy.size()); + __delitem__(indices[0], indices[1], indices[2], indices[3]); } else { throw Py.TypeError("only integer keys accepted"); } } + + + + + protected void __setitem__(PySlice slice, int start, int stop, int step, int n, PyObject value) { + if(stop < start) { + stop = start; + } + if(value instanceof PySequence) { + PySequence sequence = (PySequence) value; + setslicePySequence(slice, start, stop, step, n, sequence); + } else if(value instanceof List) { + List list = (List)value.__tojava__(List.class); + if(list != null && list != Py.NoConversion) { + setsliceList(slice, start, stop, step, list); + } + } else { + setsliceIterable(slice, start, stop, step, n, value); + } + } + + protected void setslicePySequence(PySlice slice, int start, int stop, int step, int n, PySequence value) { + if(slice.step != Py.None) { + if(n != value.__len__()) { + throw Py.ValueError("attempt to assign sequence of size " + value.__len__() + " to extended slice of size " + n); + } + } + if(step == 1) { + PyObject[] otherArray; + + if(value instanceof PySequenceList) { + otherArray = ((PySequenceList)value).getArray(); + } else { + otherArray = Py.unpackSequence(value, value.__len__()); + } + List sub = this.proxy.subList(start, stop); + sub.clear(); + sub.addAll( Arrays.asList(otherArray) ); + } else if(step != 0) { + for (int i = 0, j = start; i < n; i++, j += step) { + this.proxy.set(j, value.pyget(i)); + } + } + } + + protected void setsliceList(PySlice slice, int start, int stop, int step, List value) { + if(step != 1) { + throw Py.TypeError("setslice with java.util.List and step != 1 not supported yet"); + } + int n = value.size(); + for(int i = 0; i < n; i++) { + this.proxy.add(i + start, value.get(i)); + } + } + + protected void setsliceIterable(PySlice slice, int start, int stop, int step, int n, PyObject value) { + PyObject[] seq; + try { + seq = Py.make_array(value); + } catch (PyException pye) { + if (Py.matchException(pye, Py.TypeError)) { + throw Py.TypeError("can only assign an iterable"); + } + throw pye; + } + setslicePySequence(slice, start, stop, step, n, new PyList(seq)); + } + + + + } class MapProxy extends CollectionProxy { Index: src/org/python/core/PyList.java =================================================================== --- src/org/python/core/PyList.java (revision 5372) +++ src/org/python/core/PyList.java (working copy) @@ -114,7 +114,9 @@ protected void delRange(int start, int stop, int step) { if(step == 1) { - remove(start, stop); + if ( stop > start ) { + remove(start, stop); + } } else if(step > 1) { for(int i = start; i < stop; i += step) { remove(i); @@ -164,13 +166,7 @@ otherArray = otherArray.clone(); } list.replaceSubArray(start, stop, otherArray, 0, n); - } else if(step > 1) { - int n = value.__len__(); - for(int i = 0, j = 0; i < n; i++, j += step) { - list.pyset(j + start, value.pyget(i)); - } - } else if(step < 0) { - int n = value.__len__(); + } else if(step != 0) { if(value == this) { PyList newseq = new PyList(); PyObject iter = value.__iter__(); @@ -179,7 +175,8 @@ } value = newseq; } - for(int i = 0, j = list.size() - 1; i < n; i++, j += step) { + int n = value.__len__(); + for (int i = 0, j = start; i < n; i++, j += step) { list.pyset(j, value.pyget(i)); } } Index: test_CollectionProxy.py =================================================================== --- test_CollectionProxy.py (revision 0) +++ test_CollectionProxy.py (revision 0) @@ -0,0 +1,153 @@ +from java.util import ArrayList + +from copy import copy + +import unittest +import test.test_support + +class CollectionProxyTest(unittest.TestCase): + def _perform_op(self, value, op_func): + """ + Perform an operation + + value - the value to operate on + op_func - the function that applies the operation to value + + Returns: + the result of calling op_func, OR the exception that was raised in op_func + """ + try: + return op_func(value) + except Exception, e: + return type( e ) + + + def _list_op_test(self, initial_value, op_func): + """ + Tests a list operation + + Ensures that performing an operation on: + - a python list + - a java.util.List instance + + givens the same result in both cases + """ + # Take a copy of the initial value + py_list = copy(initial_value) + + # Produce the same value as a java.util.ArrayList + j_list = ArrayList() + j_list.addAll(initial_value) + + # Perform the operation on both + py_result = self._perform_op(py_list, op_func) + j_result = self._perform_op(j_list, op_func) + + # Compare the results + if py_result != j_result: + print 'RESULTS DIFFER:' + print py_result + print j_result + self.assert_(py_result == j_result) + + # Compare the lists + j_list = list(j_list) + if py_list != j_list: + print 'LISTS DIFFER:' + print py_list + print j_list + self.assert_( py_list == j_list ) + + + + + def test_get_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + return lambda xs: xs[index] + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + + def test_get_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k): + return lambda xs: xs[i:j:k] + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k ) ) + + + + def test_set_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + def _f(xs): + xs[index] = 100 + return _f + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + def test_set_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k, v): + def _f(xs): + xs[i:j:k] = v + return _f + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k, [] ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, range(0,2) ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, range(0,4) ) ) + self._list_op_test( initial_value, make_op_func( i, j, k, xrange(0,2) ) ) + + + + def test_del_integer(self): + initial_value = range(0,5) + + def make_op_func(index): + def _f(xs): + del xs[index] + return _f + + for i in xrange( -7, 7 ): + self._list_op_test( initial_value, make_op_func( i ) ) + + + def test_del_slice(self): + initial_value = range(0,10) + + def make_op_func(i, j, k): + def _f(xs): + del xs[i:j:k] + return _f + + for i in xrange( -12, 12 ): + for j in xrange( -12, 12 ): + for k in xrange( -12, 12 ): + self._list_op_test( initial_value, make_op_func( i, j, k ) ) + + + + + + + +def test_main(): + test.test_support.run_unittest(CollectionProxyTest) + +if __name__ == "__main__": + test_main()