diff -r 9cd9ab75eade -r 7def7236ba5a src/org/python/modules/_collections/PyDeque.java --- a/src/org/python/modules/_collections/PyDeque.java Tue May 27 19:05:44 2014 +0000 +++ b/src/org/python/modules/_collections/PyDeque.java Sun Jun 08 11:18:05 2014 -0700 @@ -1,15 +1,18 @@ package org.python.modules._collections; +import org.python.core.ArgParser; import org.python.core.PyIterator; +import org.python.core.PyList; import org.python.core.PyObject; import org.python.core.PyTuple; import org.python.core.PyType; import org.python.core.Py; import org.python.core.PyException; -import org.python.core.PyBuiltinCallable; import org.python.core.ThreadState; +import org.python.expose.ExposedGet; import org.python.expose.ExposedMethod; import org.python.expose.ExposedNew; +import org.python.expose.ExposedSet; import org.python.expose.ExposedType; import org.python.expose.MethodType; @@ -23,18 +26,24 @@ * operations and incur O(n) memory movement costs for pop(0) and insert(0, v) operations which * change both the size and position of the underlying data representation. * - * collections.deque([iterable]) - returns a new deque object initialized left-to-right (using - * append()) with data from iterable. If iterable is not specified, the new deque is empty. + * collections.deque([iterable[, maxlen]]) - returns a new deque object initialized left-to-right + * (using append()) with data from iterable. If iterable is not specified, the new deque is empty. + * If maxlen is not specified or is None, deques may grow to an arbitrary length. Otherwise, the + * deque is bounded to the specified maximum length. Once a bounded length deque is full, when new + * items are added, a corresponding number of items are discarded from the opposite end. */ @ExposedType(name = "collections.deque") public class PyDeque extends PyObject { public static final PyType TYPE = PyType.fromClass(PyDeque.class); + private long state = 0; private int size = 0; - private Node header = new Node(null, null, null); - + private int maxlen = -1; + + private Node header = new Node(null, null, null); + public PyDeque() { this(TYPE); } @@ -47,17 +56,47 @@ @ExposedNew @ExposedMethod final void deque___init__(PyObject[] args, String[] kwds) { - if (kwds.length > 0) { - throw Py.TypeError("deque() does not take keyword arguments"); + ArgParser ap = new ArgParser("deque", args, kwds, new String[] {"iterable", "maxlen",}, 0); + + PyObject maxlenobj = ap.getPyObject(1, null); + if (maxlenobj != null) { + if (maxlenobj == Py.None) { + maxlen = -1; + } else { + maxlen = ap.getInt(1); + if (maxlen < 0) { + throw Py.ValueError("maxlen must be non-negative"); + } + } + } else { + maxlen = -1; } - int nargs = args.length; - if (nargs > 1) { - throw PyBuiltinCallable.DefaultInfo.unexpectedCall(nargs, false, "deque", 0, 1); - } - if (nargs == 0) { - return; + + PyObject iterable = ap.getPyObject(0, null); + if (iterable != null) { + deque_extend(iterable); } - deque_extend(args[0]); + } + + /** + * If maxlen is not specified or is None, deques may grow to an arbitrary length. + * Otherwise, the deque is bounded to the specified maximum length. + */ + @ExposedGet(name = "maxlen") + public PyObject getMaxlen() { + if (maxlen < 0) { + return Py.None; + } + return Py.newInteger(maxlen); + } + + @ExposedSet(name = "maxlen") + public void setMaxlen(PyObject o) { + // this has to be here because by default, if not defined, + // the Jython object model raise a TypeError, where we usually expect + // AttributeError here; due to a CPython 2.7 idiosyncracy that has + // since been fixed for 3.x in http://bugs.python.org/issue1687163 + throw Py.AttributeError("attribute 'maxlen' of 'collections.deque' objects is not writable"); } /** @@ -65,6 +104,15 @@ */ @ExposedMethod final void deque_append(PyObject obj) { + if (maxlen >= 0) { + assert (size <= maxlen); + if (maxlen == 0) { + // do nothing; this deque will always be empty + return; + } else if (size == maxlen) { + deque_popleft(); + } + } addBefore(obj, header); } @@ -72,7 +120,16 @@ * Add obj to the left side of the deque. */ @ExposedMethod - final void deque_appendleft(PyObject obj) { + final void deque_appendleft(PyObject obj) { + if (maxlen >= 0) { + assert (size <= maxlen); + if (maxlen == 0) { + // do nothing; this deque will always be empty + return; + } else if (size == maxlen) { + deque_pop(); + } + } addBefore(obj, header.right); } @@ -81,6 +138,7 @@ newNode.left.right = newNode; newNode.right.left = newNode; size++; + state++; return newNode; } @@ -96,6 +154,7 @@ node.right = null; node.data = null; node = right; + state++; } header.right = header.left = header; size = 0; @@ -107,9 +166,14 @@ */ @ExposedMethod final void deque_extend(PyObject iterable) { - for (PyObject item : iterable.asIterable()) { - deque_append(item); - } + // handle case where iterable == this + if (this == iterable) { + deque_extend(new PyList(iterable)); + } else { + for (PyObject item : iterable.asIterable()) { + deque_append(item); + } + } } /** @@ -119,8 +183,13 @@ */ @ExposedMethod final void deque_extendleft(PyObject iterable) { - for (PyObject item : iterable.asIterable()) { - deque_appendleft(item); + // handle case where iterable == this + if (this == iterable) { + deque_extendleft(new PyList(iterable)); + } else { + for (PyObject item : iterable.asIterable()) { + deque_appendleft(item); + } } } @@ -153,6 +222,7 @@ node.left = null; node.data = null; size--; + state++; return obj; } @@ -165,11 +235,12 @@ int n = size; Node tmp = header.right; boolean match = false; + long startState = state; for (int i = 0; i < n; i++) { if (tmp.data.equals(value)) { match = true; } - if (n != size) { + if (startState != state) { throw Py.IndexError("deque mutated during remove()."); } if (match) { @@ -181,6 +252,27 @@ } /** + * Count the number of deque elements equal to x. + */ + @ExposedMethod + final PyObject deque_count(PyObject x) { + int n = size; + int count = 0; + Node tmp = header.right; + long startState = state; + for (int i = 0; i < n; i++) { + if (tmp.data.equals(x)) { + count++; + } + if (startState != state) { + throw Py.RuntimeError("deque mutated during count()."); + } + tmp = tmp.right; + } + return Py.newInteger(count); + } + + /** * Rotate the deque n steps to the right. If n is negative, rotate to the * left. Rotating one step to the right is equivalent to: d.appendleft(d.pop()). */ @@ -210,6 +302,17 @@ } } + /** + * Reverse the elements of the deque in-place and then return None. + * @return Py.None + */ + @ExposedMethod + final PyObject deque_reverse() { + // TODO + return Py.None; + } + + @Override public String toString() { return deque_toString(); } @@ -220,18 +323,28 @@ if (!ts.enterRepr(this)) { return "[...]"; } + long startState = state; StringBuilder buf = new StringBuilder("deque").append("(["); for (Node tmp = header.right; tmp != header; tmp = tmp.right) { buf.append(tmp.data.__repr__().toString()); + if (startState != state) { + throw Py.RuntimeError("deque mutated during iteration."); + } if (tmp.right != header) { buf.append(", "); } } - buf.append("])"); + buf.append("]"); + if (maxlen >= 0) { + buf.append(", maxlen="); + buf.append(maxlen); + } + buf.append(")"); ts.exitRepr(this); return buf.toString(); } + @Override public int __len__() { return deque___len__(); } @@ -241,6 +354,7 @@ return size; } + @Override public boolean __nonzero__() { return deque___nonzero__(); } @@ -250,6 +364,7 @@ return size != 0; } + @Override public PyObject __finditem__(PyObject key) { try { return deque___getitem__(key); @@ -264,8 +379,9 @@ @ExposedMethod final PyObject deque___getitem__(PyObject index) { return getNode(index).data; - } + } + @Override public void __setitem__(PyObject index, PyObject value) { deque___setitem__(index, value); } @@ -275,8 +391,9 @@ Node node = getNode(index).right; removeNode(node.left); addBefore(value, node); - } + } + @Override public void __delitem__(PyObject key) { deque___delitem__(key); } @@ -314,6 +431,7 @@ return tmp; } + @Override public PyObject __iter__() { return deque___iter__(); } @@ -323,6 +441,7 @@ return new PyDequeIter(); } + @Override public synchronized PyObject __eq__(PyObject o) { return deque___eq__(o); } @@ -341,6 +460,7 @@ return (i < 0) ? Py.True : Py.False; } + @Override public synchronized PyObject __ne__(PyObject o) { return deque___ne__(o); } @@ -359,6 +479,7 @@ return (i < 0) ? Py.False : Py.True; } + @Override public synchronized PyObject __lt__(PyObject o) { return deque___lt__(o); } @@ -375,6 +496,7 @@ return __finditem__(i)._lt(o.__finditem__(i)); } + @Override public synchronized PyObject __le__(PyObject o) { return deque___le__(o); } @@ -391,6 +513,7 @@ return __finditem__(i)._le(o.__finditem__(i)); } + @Override public synchronized PyObject __gt__(PyObject o) { return deque___gt__(o); } @@ -407,6 +530,7 @@ return __finditem__(i)._gt(o.__finditem__(i)); } + @Override public synchronized PyObject __ge__(PyObject o) { return deque___ge__(o); } @@ -423,6 +547,17 @@ return __finditem__(i)._ge(o.__finditem__(i)); } + @Override + public synchronized PyObject __iadd__(PyObject o) { + return deque___iadd__(o); + } + + @ExposedMethod(type = MethodType.BINARY) + final synchronized PyObject deque___iadd__(PyObject o) { + deque_extend(o); + return this; + } + // Return value >= 0 is the index where the sequences differs. // -1: reached the end of o1 without a difference // -2: reached the end of both seqeunces without a difference @@ -445,6 +580,7 @@ return (ol1 < ol2) ? -1 : -3; } + @Override public int hashCode() { return deque_hashCode(); } @@ -454,6 +590,7 @@ throw Py.TypeError("deque objects are unhashable"); } + @Override public PyObject __reduce__() { return deque___reduce__(); } @@ -499,14 +636,15 @@ private class PyDequeIter extends PyIterator { private Node lastReturned = header; - private int itersize; + private long startState; public PyDequeIter() { - itersize = size; + startState = state; } - public PyObject __iternext__() { - if (itersize != size) { + @Override + public PyObject __iternext__() { + if (startState != state) { throw Py.RuntimeError("deque changed size during iteration"); } if (lastReturned.right != header) {