Index: src/org/python/core/AbstractArray.java =================================================================== --- src/org/python/core/AbstractArray.java (revision 5597) +++ 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 5597) +++ 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; @@ -17,6 +18,9 @@ if (object == null) return NoProxy; + if (object instanceof Vector) { + return new VectorProxy(((Vector) object)); + } if (object instanceof List) { return new ListProxy(((List) object)); } @@ -31,9 +35,6 @@ } - if (object instanceof Vector) { - return new VectorProxy(((Vector) object)); - } if (object instanceof Enumeration) { return new EnumerationProxy(((Enumeration) object)); } @@ -112,10 +113,13 @@ } } -class VectorProxy extends CollectionProxy { - Vector proxy; - public VectorProxy(Vector proxy) { + + +class ListProxy extends CollectionProxy { + List proxy; + + public ListProxy(List proxy) { this.proxy = proxy; } @@ -124,71 +128,222 @@ } public PyObject __finditem__(int key) { + int k = key < 0 ? key + this.proxy.size() : key; try { - return Py.java2py(this.proxy.elementAt(key)); - } catch (ArrayIndexOutOfBoundsException exc) { - return null; + return Py.java2py(this.proxy.get(k)); + } catch (IndexOutOfBoundsException exc) { + 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) { + int k = key < 0 ? key + this.proxy.size() : key; + try { + this.proxy.set(k, 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) { - this.proxy.setElementAt(Py.tojava(value, Object.class), - ((PyInteger) key).getValue()); + __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) { + int k = key < 0 ? key + this.proxy.size() : key; + try { + this.proxy.remove(k); + } 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) { - this.proxy.removeElementAt(((PyInteger) key).getValue()); + __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"); } } -} -class DictionaryProxy extends CollectionProxy { - Dictionary proxy; - public DictionaryProxy(Dictionary proxy) { - this.proxy = proxy; + + + 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); + } } - public int __len__() { - return this.proxy.size(); + 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[] srcArray; + Object[] jArray; + + if(value instanceof PySequenceList) { + srcArray = ((PySequenceList)value).getArray(); + } else { + srcArray = Py.unpackSequence(value, value.__len__()); + } + jArray = new Object[srcArray.length]; + for (int i = 0; i < srcArray.length; i++) { + jArray[i] = Py.tojava(srcArray[i], Object.class); + } + List sub = this.proxy.subList(start, stop); + sub.clear(); + this.proxy.addAll( start, Arrays.asList(jArray) ); + } else if(step != 0) { + for (int i = 0, j = start; i < n; i++, j += step) { + this.proxy.set(j, Py.tojava(value.pyget(i), Object.class)); + } + } } - public PyObject __finditem__(int key) { - throw Py.TypeError("loop over non-sequence"); + 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)); + } } - public PyObject __finditem__(PyObject key) { - return Py.java2py(this.proxy.get(Py.tojava(key, Object.class))); + 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)); } +} - public void __setitem__(PyObject key, PyObject value) { - this.proxy.put(Py.tojava(key, Object.class), Py.tojava(value, - Object.class)); + + + +class VectorProxy extends ListProxy { + public VectorProxy(Vector proxy) { + super(proxy); } - public void __delitem__(PyObject key) { - this.proxy.remove(Py.tojava(key, Object.class)); + public PyObject __finditem__(int key) { + int k = key < 0 ? key + this.proxy.size() : key; + try { + return Py.java2py(((Vector)this.proxy).elementAt(k)); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("index out of range: " + String.valueOf(key)); + } } + + public void __setitem__(int key, PyObject value) { + int k = key < 0 ? key + this.proxy.size() : key; + try { + ((Vector)this.proxy).setElementAt(Py.tojava(value, Object.class), k); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } + } + + public void __delitem__(int key) { + int k = key < 0 ? key + this.proxy.size() : key; + try { + ((Vector)this.proxy).removeElementAt(k); + } catch (IndexOutOfBoundsException exc) { + throw Py.IndexError("assignment index out of range: " + String.valueOf(key)); + } + } } -class ListProxy extends CollectionProxy { - List proxy; - public ListProxy(List proxy) { + + + +class DictionaryProxy extends CollectionProxy { + Dictionary proxy; + + public DictionaryProxy(Dictionary proxy) { this.proxy = proxy; } @@ -197,46 +352,22 @@ } public PyObject __finditem__(int key) { - try { - return Py.java2py(this.proxy.get(key)); - } catch (IndexOutOfBoundsException exc) { - return null; - } + throw Py.TypeError("loop over non-sequence"); } public PyObject __finditem__(PyObject key) { - if (key instanceof PyInteger) { - return __finditem__(((PyInteger) key).getValue()); - } else { - throw Py.TypeError("only integer keys accepted"); - } + return Py.java2py(this.proxy.get(Py.tojava(key, Object.class))); } - public void __setitem__(int key, PyObject value) { - this.proxy.set(key, Py.tojava(value, Object.class)); - } - public void __setitem__(PyObject key, PyObject value) { - if (key instanceof PyInteger) { - __setitem__(((PyInteger) key).getValue(), value); - } else { - throw Py.TypeError("only integer keys accepted"); - } + this.proxy.put(Py.tojava(key, Object.class), Py.tojava(value, + Object.class)); } - public void __delitem__(int key) { - this.proxy.remove(key); - } - public void __delitem__(PyObject key) { - if (key instanceof PyInteger) { - __delitem__(((PyInteger) key).getValue()); - } else { - throw Py.TypeError("only integer keys accepted"); - } + this.proxy.remove(Py.tojava(key, Object.class)); } } - class MapProxy extends CollectionProxy { Map proxy; Index: src/org/python/core/PyInstance.java =================================================================== --- src/org/python/core/PyInstance.java (revision 5597) +++ src/org/python/core/PyInstance.java (working copy) @@ -10,6 +10,10 @@ public class PyInstance extends PyObject { + // Collection proxy invoke no method found error code - return an error + // code instead of using slower exceptions + public static PyObject collectionProxyNoMethodError = new PyObject(); + // xxx doc, final name public transient PyClass instclass; @@ -251,6 +255,61 @@ return __findattr__("__index__") != null; } + + public PyObject iinvoke_collectionProxy(String name) { + PyObject f = ifindlocal(name); + if (f == null) { + f = ifindclass(name, false); + if (f != null) { + if (f instanceof PyFunction) { + return f.__call__(this); + } else { + f = f.__get__(this, instclass); + } + } + } + if (f == null) f = ifindfunction(name); + if (f == null) return collectionProxyNoMethodError; + return f.__call__(); + } + + public PyObject iinvoke_collectionProxy(String name, PyObject arg1) { + PyObject f = ifindlocal(name); + if (f == null) { + f = ifindclass(name, false); + if (f != null) { + if (f instanceof PyFunction) { + return f.__call__(this, arg1); + } else { + f = f.__get__(this, instclass); + } + } + } + if (f == null) f = ifindfunction(name); + if (f == null) return collectionProxyNoMethodError; + return f.__call__(arg1); + } + + public PyObject iinvoke_collectionProxy(String name, PyObject arg1, PyObject arg2) { + PyObject f = ifindlocal(name); + if (f == null) { + f = ifindclass(name, false); + if (f != null) { + if (f instanceof PyFunction) { + return f.__call__(this, arg1, arg2); + } else { + f = f.__get__(this, instclass); + } + } + } + if (f == null) f = ifindfunction(name); + if (f == null) return collectionProxyNoMethodError; + return f.__call__(arg1, arg2); + } + + + + public PyObject invoke(String name) { PyObject f = ifindlocal(name); if (f == null) { @@ -548,16 +607,18 @@ } catch (PyException exc) { } if (meth == null) { - // Copied form __len__() - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - return proxy.__len__() != 0 ? true : false; - } try { meth = __findattr__("__len__"); } catch (PyException exc) { } - if (meth == null) - return true; + if (meth == null) { + // Copied form __len__() + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + return proxy.__len__() != 0 ? true : false; + } else { + return true; + } + } } PyObject ret = meth.__call__(); @@ -573,22 +634,21 @@ } public int __len__() { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - return proxy.__len__(); + PyObject ret = iinvoke_collectionProxy("__len__"); + if (ret == collectionProxyNoMethodError) { + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + return proxy.__len__(); + } else { + noAttributeError("__len__"); + } } - - PyObject ret = invoke("__len__"); if (ret instanceof PyInteger) return ((PyInteger)ret).getValue(); throw Py.TypeError("__len__() should return an int"); } public PyObject __finditem__(int key) { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - return proxy.__finditem__(key); - } return __finditem__(new PyInteger(key)); } @@ -614,13 +674,9 @@ } public PyObject __finditem__(PyObject key) { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - return proxy.__finditem__(key); - } - + PyObject ret = null; try { - return invoke("__getitem__", key); + ret = iinvoke_collectionProxy("__getitem__", key); } catch (PyException e) { if (Py.matchException(e, Py.IndexError)) return null; @@ -628,36 +684,64 @@ return null; throw e; } + + if (ret == collectionProxyNoMethodError) { + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + return proxy.__finditem__(key); + } else { + noAttributeError("__getitem__"); + } + } + + return ret; } public PyObject __getitem__(PyObject key) { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - PyObject ret = proxy.__finditem__(key); - if (ret == null) { - throw Py.KeyError(key.toString()); + PyObject ret = iinvoke_collectionProxy("__getitem__", key); + + if (ret == collectionProxyNoMethodError) { + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + ret = proxy.__finditem__(key); + if (ret == null) { + throw Py.KeyError(key.toString()); + } + return ret; + } else { + noAttributeError("__getitem__"); } - return ret; } - return invoke("__getitem__", key); + + return ret; } public void __setitem__(PyObject key, PyObject value) { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - proxy.__setitem__(key, value); - return; + PyObject ret = iinvoke_collectionProxy("__setitem__", key, value); + + if (ret == collectionProxyNoMethodError) { + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + proxy.__setitem__(key, value); + return; + } else { + noAttributeError("__setitem__"); + } } - invoke("__setitem__", key, value); } public void __delitem__(PyObject key) { - CollectionProxy proxy = getCollection(); - if (proxy != CollectionProxy.NoProxy) { - proxy.__delitem__(key); - return; + PyObject ret = iinvoke_collectionProxy("__delitem__", key); + + if (ret == collectionProxyNoMethodError) { + CollectionProxy proxy = getCollection(); + if (proxy != CollectionProxy.NoProxy) { + proxy.__delitem__(key); + return; + } else { + noAttributeError("__delitem__"); + } } - invoke("__delitem__", key); } public PyObject __getslice__(PyObject start, PyObject stop, PyObject step) { @@ -688,16 +772,18 @@ } public PyObject __iter__() { - PyObject iter = getCollectionIter(); - if (iter != null) { - return iter; - } PyObject func = __findattr__("__iter__"); if (func != null) return func.__call__(); func = __findattr__("__getitem__"); - if (func == null) - return super.__iter__(); + if (func == null) { + PyObject iter = getCollectionIter(); + if (iter != null) { + return iter; + } else { + return super.__iter__(); + } + } return new PySequenceIter(this); } Index: src/org/python/core/PyList.java =================================================================== --- src/org/python/core/PyList.java (revision 5597) +++ 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,245 @@ +from java.util import ArrayList, Vector + +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) + + # Produce the same value as a java.util.Vector + j_vec = Vector() + j_vec.addAll(initial_value) + + # Perform the operation on both + py_result = self._perform_op(py_list, op_func) + jlist_result = self._perform_op(j_list, op_func) + jvec_result = self._perform_op(j_vec, op_func) + + # Compare the results of the python list and the ArrayList + if py_result != jlist_result: + print 'Python AND ArrayList RESULTS DIFFER:' + print py_result + print jlist_result + self.assert_(py_result == jlist_result) + + # Compare the results of the python list and the Vector + if py_result != jvec_result: + print 'Python AND Vector RESULTS DIFFER:' + print py_result + print jvec_result + self.assert_(py_result == jvec_result) + + # Compare the python list and the ArrayList + j_list = list(j_list) + if py_list != j_list: + print 'Python and ArrayList DIFFER:' + print py_list + print j_list + self.assert_( py_list == j_list ) + + # Compare the python list and the Vector + j_vec = list(j_vec) + if py_list != j_vec: + print 'Python and Vector DIFFER:' + print py_list + print j_vec + self.assert_( py_list == j_vec ) + + + + + 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_len(self): + jlist = ArrayList() + jlist.addAll( range(0, 10) ) + + self.assert_( len( jlist ) == 10 ) + + + def test_iter(self): + jlist = ArrayList() + jlist.addAll( range(0, 10) ) + + i = iter( jlist ) + + x = list( i ) + + self.assert_( x == range(0, 10) ) + + + + def test_override_len(self): + class MyList (ArrayList): + def __len__(self): + return self.size() + 1; + + + m = MyList() + m.addAll( range(0,10) ) + + self.assert_( len( m ) == 11 ) + + + def test_override_iter(self): + class MyList (ArrayList): + def __iter__(self): + return iter( self.subList( 0, self.size() - 1 ) ); + + + m = MyList() + m.addAll( range(0,10) ) + i = iter( m ) + x = list( i ) + + self.assert_( x == range( 0, 9 ) ) + + + def test_override_getsetdelitem(self): + # Create an ArrayList subclass that provides some silly overrides for get/set/del item + class MyList (ArrayList): + def __getitem__(self, key): + return self.get( key ) * 2; + + def __setitem__(self, key, value): + return self.set( key, value * 2 ); + + def __delitem__(self, key): + self.add( 84 ) + + + m = MyList() + m.addAll( range(0,10) ) + + self.assert_( m[1] == 2 ) + self.assert_( m.get( 1 ) == 1 ) + + m[0] = 3 + self.assert_( m.get( 0 ) == 6 ) + self.assert_( m[0] == 12 ) + + del m[0] + self.assert_( m.size() == 11 ) + self.assert_( m.get( 10 ) == 84 ) + + + + + + +def test_main(): + test.test_support.run_unittest(CollectionProxyTest) + +if __name__ == "__main__": + test_main()