summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libcollections.tex29
-rw-r--r--Lib/asynchat.py6
-rw-r--r--Lib/test/test_deque.py92
-rw-r--r--Modules/collectionsmodule.c120
4 files changed, 229 insertions, 18 deletions
diff --git a/Doc/lib/libcollections.tex b/Doc/lib/libcollections.tex
index 0378ea5882..2793095789 100644
--- a/Doc/lib/libcollections.tex
+++ b/Doc/lib/libcollections.tex
@@ -54,14 +54,24 @@ Deque objects support the following methods:
reversing the order of elements in the iterable argument.
\end{methoddesc}
+\begin{methoddesc}{left}{}
+ Return leftmost element from the deque.
+ If no elements are present, raises a \exception{IndexError}.
+\end{methoddesc}
+
\begin{methoddesc}{pop}{}
Remove and return an element from the right side of the deque.
- If no elements are present, raises a \exception{LookupError}.
+ If no elements are present, raises a \exception{IndexError}.
\end{methoddesc}
\begin{methoddesc}{popleft}{}
Remove and return an element from the left side of the deque.
- If no elements are present, raises a \exception{LookupError}.
+ If no elements are present, raises a \exception{IndexError}.
+\end{methoddesc}
+
+\begin{methoddesc}{right}{}
+ Return the rightmost element from the deque.
+ If no elements are present, raises a \exception{IndexError}.
\end{methoddesc}
\begin{methoddesc}{rotate}{n}
@@ -80,22 +90,27 @@ Example:
>>> from collections import deque
>>> d = deque('ghi') # make a new deque with three items
>>> for elem in d: # iterate over the deque's elements
- print elem.upper()
-
-
+... print elem.upper()
G
H
I
+
>>> d.append('j') # add a new entry to the right side
>>> d.appendleft('f') # add a new entry to the left side
>>> d # show the representation of the deque
deque(['f', 'g', 'h', 'i', 'j'])
+
>>> d.pop() # return and remove the rightmost item
'j'
>>> d.popleft() # return and remove the leftmost item
'f'
>>> list(d) # list the contents of the deque
['g', 'h', 'i']
+
+>>> d.left() # peek at leftmost item
+'g'
+>>> d.right() # peek at rightmost item
+'i'
>>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g']
>>> 'h' in d # search the deque
@@ -109,15 +124,15 @@ deque(['l', 'g', 'h', 'i', 'j', 'k'])
>>> d.rotate(-1) # left rotation
>>> d
deque(['g', 'h', 'i', 'j', 'k', 'l'])
+
>>> deque(reversed(d)) # make a new deque in reverse order
deque(['l', 'k', 'j', 'i', 'h', 'g'])
>>> d.clear() # empty the deque
>>> d.pop() # cannot pop from an empty deque
-
Traceback (most recent call last):
File "<pyshell#6>", line 1, in -toplevel-
d.pop()
-LookupError: pop from an empty deque
+IndexError: pop from an empty deque
>>> d.extendleft('abc') # extendleft() reverses the input order
>>> d
diff --git a/Lib/asynchat.py b/Lib/asynchat.py
index 4bfab302a1..2c0bc3e902 100644
--- a/Lib/asynchat.py
+++ b/Lib/asynchat.py
@@ -262,11 +262,7 @@ class fifo:
return self.list == []
def first (self):
- it = iter(self.list)
- try:
- return it.next()
- except StopIteration:
- raise IndexError
+ return self.list.left()
def push (self, data):
self.list.append(data)
diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py
index f3bc59f8ce..db9733e8ee 100644
--- a/Lib/test/test_deque.py
+++ b/Lib/test/test_deque.py
@@ -28,6 +28,23 @@ class TestBasic(unittest.TestCase):
self.assertEqual(right, range(150, 400))
self.assertEqual(list(d), range(50, 150))
+ def test_comparisons(self):
+ d = deque('xabc'); d.popleft()
+ for e in [d, deque('abc'), deque('ab'), deque(), list(d)]:
+ self.assertEqual(d==e, type(d)==type(e) and list(d)==list(e))
+ self.assertEqual(d!=e, not(type(d)==type(e) and list(d)==list(e)))
+
+ args = map(deque, ('', 'a', 'b', 'ab', 'ba', 'abc', 'xba', 'xabc', 'cba'))
+ for x in args:
+ for y in args:
+ self.assertEqual(x == y, list(x) == list(y), (x,y))
+ self.assertEqual(x != y, list(x) != list(y), (x,y))
+ self.assertEqual(x < y, list(x) < list(y), (x,y))
+ self.assertEqual(x <= y, list(x) <= list(y), (x,y))
+ self.assertEqual(x > y, list(x) > list(y), (x,y))
+ self.assertEqual(x >= y, list(x) >= list(y), (x,y))
+ self.assertEqual(cmp(x,y), cmp(list(x),list(y)), (x,y))
+
def test_extend(self):
d = deque('a')
self.assertRaises(TypeError, d.extend, 1)
@@ -40,6 +57,14 @@ class TestBasic(unittest.TestCase):
d.extendleft('bcd')
self.assertEqual(list(d), list(reversed('abcd')))
+ def test_leftright(self):
+ d = deque('superman')
+ self.assertEqual(d.left(), 's')
+ self.assertEqual(d.right(), 'n')
+ d = deque()
+ self.assertRaises(IndexError, d.left)
+ self.assertRaises(IndexError, d.right)
+
def test_rotate(self):
s = tuple('abcde')
n = len(s)
@@ -93,7 +118,7 @@ class TestBasic(unittest.TestCase):
self.assertEqual(len(d), 1)
d.pop()
self.assertEqual(len(d), 0)
- self.assertRaises(LookupError, d.pop)
+ self.assertRaises(IndexError, d.pop)
self.assertEqual(len(d), 0)
d.append('c')
self.assertEqual(len(d), 1)
@@ -104,8 +129,8 @@ class TestBasic(unittest.TestCase):
def test_underflow(self):
d = deque()
- self.assertRaises(LookupError, d.pop)
- self.assertRaises(LookupError, d.popleft)
+ self.assertRaises(IndexError, d.pop)
+ self.assertRaises(IndexError, d.popleft)
def test_clear(self):
d = deque(xrange(100))
@@ -374,6 +399,63 @@ class TestSubclass(unittest.TestCase):
#==============================================================================
+libreftest = """
+Example from the Library Reference: Doc/lib/libcollections.tex
+
+>>> from collections import deque
+>>> d = deque('ghi') # make a new deque with three items
+>>> for elem in d: # iterate over the deque's elements
+... print elem.upper()
+G
+H
+I
+>>> d.append('j') # add a new entry to the right side
+>>> d.appendleft('f') # add a new entry to the left side
+>>> d # show the representation of the deque
+deque(['f', 'g', 'h', 'i', 'j'])
+>>> d.pop() # return and remove the rightmost item
+'j'
+>>> d.popleft() # return and remove the leftmost item
+'f'
+>>> list(d) # list the contents of the deque
+['g', 'h', 'i']
+>>> d.left() # peek at leftmost item
+'g'
+>>> d.right() # peek at rightmost item
+'i'
+>>> list(reversed(d)) # list the contents of a deque in reverse
+['i', 'h', 'g']
+>>> 'h' in d # search the deque
+True
+>>> d.extend('jkl') # add multiple elements at once
+>>> d
+deque(['g', 'h', 'i', 'j', 'k', 'l'])
+>>> d.rotate(1) # right rotation
+>>> d
+deque(['l', 'g', 'h', 'i', 'j', 'k'])
+>>> d.rotate(-1) # left rotation
+>>> d
+deque(['g', 'h', 'i', 'j', 'k', 'l'])
+>>> deque(reversed(d)) # make a new deque in reverse order
+deque(['l', 'k', 'j', 'i', 'h', 'g'])
+>>> d.clear() # empty the deque
+>>> d.pop() # cannot pop from an empty deque
+Traceback (most recent call last):
+ File "<pyshell#6>", line 1, in -toplevel-
+ d.pop()
+IndexError: pop from an empty deque
+
+>>> d.extendleft('abc') # extendleft() reverses the input order
+>>> d
+deque(['c', 'b', 'a'])
+
+"""
+
+
+#==============================================================================
+
+__test__ = {'libreftest' : libreftest}
+
def test_main(verbose=None):
import sys
from test import test_sets
@@ -394,6 +476,10 @@ def test_main(verbose=None):
gc.collect()
counts[i] = sys.gettotalrefcount()
print counts
+
+ # doctests
+ from test import test_deque
+ test_support.run_doctest(test_deque, verbose)
if __name__ == "__main__":
test_main(verbose=True)
diff --git a/Modules/collectionsmodule.c b/Modules/collectionsmodule.c
index d534aa9f69..2b5cb105f0 100644
--- a/Modules/collectionsmodule.c
+++ b/Modules/collectionsmodule.c
@@ -34,6 +34,8 @@ typedef struct {
int len;
} dequeobject;
+PyTypeObject deque_type;
+
static PyObject *
deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
@@ -109,7 +111,7 @@ deque_pop(dequeobject *deque, PyObject *unused)
block *prevblock;
if (deque->len == 0) {
- PyErr_SetString(PyExc_LookupError, "pop from an empty deque");
+ PyErr_SetString(PyExc_IndexError, "pop from an empty deque");
return NULL;
}
item = deque->rightblock->data[deque->rightindex];
@@ -144,7 +146,7 @@ deque_popleft(dequeobject *deque, PyObject *unused)
block *prevblock;
if (deque->len == 0) {
- PyErr_SetString(PyExc_LookupError, "pop from an empty deque");
+ PyErr_SetString(PyExc_IndexError, "pop from an empty deque");
return NULL;
}
item = deque->leftblock->data[deque->leftindex];
@@ -175,6 +177,38 @@ deque_popleft(dequeobject *deque, PyObject *unused)
PyDoc_STRVAR(popleft_doc, "Remove and return the leftmost element.");
static PyObject *
+deque_right(dequeobject *deque, PyObject *unused)
+{
+ PyObject *item;
+
+ if (deque->len == 0) {
+ PyErr_SetString(PyExc_IndexError, "deque is empty");
+ return NULL;
+ }
+ item = deque->rightblock->data[deque->rightindex];
+ Py_INCREF(item);
+ return item;
+}
+
+PyDoc_STRVAR(right_doc, "Return the rightmost element.");
+
+static PyObject *
+deque_left(dequeobject *deque, PyObject *unused)
+{
+ PyObject *item;
+
+ if (deque->len == 0) {
+ PyErr_SetString(PyExc_IndexError, "deque is empty");
+ return NULL;
+ }
+ item = deque->leftblock->data[deque->leftindex];
+ Py_INCREF(item);
+ return item;
+}
+
+PyDoc_STRVAR(left_doc, "Return the leftmost element.");
+
+static PyObject *
deque_extend(dequeobject *deque, PyObject *iterable)
{
PyObject *it, *item;
@@ -467,6 +501,82 @@ deque_tp_print(PyObject *deque, FILE *fp, int flags)
return 0;
}
+static PyObject *
+deque_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *it1=NULL, *it2=NULL, *x, *y;
+ int i, b, vs, ws, minlen, cmp=-1;
+
+ if (v->ob_type != &deque_type || w->ob_type != &deque_type) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ /* Shortcuts */
+ vs = ((dequeobject *)v)->len;
+ ws = ((dequeobject *)w)->len;
+ if (op == Py_EQ) {
+ if (v == w)
+ Py_RETURN_TRUE;
+ if (vs != ws)
+ Py_RETURN_FALSE;
+ }
+ if (op == Py_NE) {
+ if (v == w)
+ Py_RETURN_FALSE;
+ if (vs != ws)
+ Py_RETURN_TRUE;
+ }
+
+ /* Search for the first index where items are different */
+ it1 = PyObject_GetIter(v);
+ if (it1 == NULL)
+ goto done;
+ it2 = PyObject_GetIter(w);
+ if (it2 == NULL)
+ goto done;
+ minlen = (vs < ws) ? vs : ws;
+ for (i=0 ; i < minlen ; i++) {
+ x = PyIter_Next(it1);
+ if (x == NULL)
+ goto done;
+ y = PyIter_Next(it2);
+ if (y == NULL) {
+ Py_DECREF(x);
+ goto done;
+ }
+ b = PyObject_RichCompareBool(x, y, Py_EQ);
+ if (b == 0) {
+ cmp = PyObject_RichCompareBool(x, y, op);
+ Py_DECREF(x);
+ Py_DECREF(y);
+ goto done;
+ }
+ Py_DECREF(x);
+ Py_DECREF(y);
+ if (b == -1)
+ goto done;
+ }
+ /* Elements are equal through minlen. The longest input is the greatest */
+ switch (op) {
+ case Py_LT: cmp = vs < ws; break;
+ case Py_LE: cmp = vs <= ws; break;
+ case Py_EQ: cmp = vs == ws; break;
+ case Py_NE: cmp = vs != ws; break;
+ case Py_GT: cmp = vs > ws; break;
+ case Py_GE: cmp = vs >= ws; break;
+ }
+
+done:
+ Py_XDECREF(it1);
+ Py_XDECREF(it2);
+ if (cmp == 1)
+ Py_RETURN_TRUE;
+ if (cmp == 0)
+ Py_RETURN_FALSE;
+ return NULL;
+}
+
static int
deque_init(dequeobject *deque, PyObject *args, PyObject *kwds)
{
@@ -509,6 +619,8 @@ static PyMethodDef deque_methods[] = {
METH_O, extend_doc},
{"extendleft", (PyCFunction)deque_extendleft,
METH_O, extendleft_doc},
+ {"left", (PyCFunction)deque_left,
+ METH_NOARGS, left_doc},
{"pop", (PyCFunction)deque_pop,
METH_NOARGS, pop_doc},
{"popleft", (PyCFunction)deque_popleft,
@@ -517,6 +629,8 @@ static PyMethodDef deque_methods[] = {
METH_NOARGS, reduce_doc},
{"__reversed__", (PyCFunction)deque_reviter,
METH_NOARGS, reversed_doc},
+ {"right", (PyCFunction)deque_right,
+ METH_NOARGS, right_doc},
{"rotate", (PyCFunction)deque_rotate,
METH_VARARGS, rotate_doc},
{NULL, NULL} /* sentinel */
@@ -553,7 +667,7 @@ PyTypeObject deque_type = {
deque_doc, /* tp_doc */
(traverseproc)set_traverse, /* tp_traverse */
(inquiry)deque_clear, /* tp_clear */
- 0, /* tp_richcompare */
+ (richcmpfunc)deque_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset*/
(getiterfunc)deque_iter, /* tp_iter */
0, /* tp_iternext */