]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Add a remove() method to collections.deque objects.
authorRaymond Hettinger <python@rcn.com>
Fri, 18 Mar 2005 21:20:23 +0000 (21:20 +0000)
committerRaymond Hettinger <python@rcn.com>
Fri, 18 Mar 2005 21:20:23 +0000 (21:20 +0000)
Doc/lib/libcollections.tex
Lib/test/test_deque.py
Misc/NEWS
Modules/collectionsmodule.c

index 84cc507cb8de2343907fc482151bf74e90b387b8..51603aa23373cf622ce4e5aaee89d033a2acb125 100644 (file)
@@ -64,6 +64,12 @@ Deque objects support the following methods:
    If no elements are present, raises a \exception{IndexError}.   
 \end{methoddesc}
 
+\begin{methoddesc}{remove}{value}
+   Removed the first occurrence of \var{value}.  If not found,
+   raises a \exception{ValueError}.
+   \versionadded{2.5}
+\end{methoddesc}
+
 \begin{methoddesc}{rotate}{n}
    Rotate the deque \var{n} steps to the right.  If \var{n} is
    negative, rotate to the left.  Rotating one step to the right
index 0a6c1f97ff7a04cb07cd7fc8b961008bf3f70b65..f498124cef00a523d434d4be545630253eca7c62 100644 (file)
@@ -14,6 +14,17 @@ def fail():
     raise SyntaxError
     yield 1
 
+class BadCmp:
+    def __eq__(self, other):
+        raise RuntimeError
+
+class MutateCmp:
+    def __init__(self, deque):
+        self.deque = deque
+    def __eq__(self, other):
+        self.deque.clear()
+        return True
+
 class TestBasic(unittest.TestCase):
 
     def test_basics(self):
@@ -197,6 +208,30 @@ class TestBasic(unittest.TestCase):
         d.clear()               # clear an emtpy deque
         self.assertEqual(list(d), [])
 
+    def test_remove(self):
+        d = deque('abcdefghcij')
+        d.remove('c')
+        self.assertEqual(d, deque('abdefghcij'))
+        d.remove('c')
+        self.assertEqual(d, deque('abdefghij'))
+        self.assertRaises(ValueError, d.remove, 'c')
+        self.assertEqual(d, deque('abdefghij'))
+
+        # Handle comparision errors
+        d = deque(['a', 'b', BadCmp(), 'c'])
+        e = deque(d)
+        self.assertRaises(RuntimeError, d.remove, 'c')
+        for x, y in zip(d, e):
+            # verify that original order and values are retained.
+            self.assert_(x is y)
+
+        # Handle evil mutator
+        d = deque(['ab'])
+        d.extend([MutateCmp(d), 'c'])
+        e = deque(d)
+        self.assertRaises(IndexError, d.remove, 'c')
+        self.assertEqual(d, deque())
+
     def test_repr(self):
         d = deque(xrange(200))
         e = eval(repr(d))
index bfeda8332639f64bbf26bff33b825ee3c73c0ab2..9e3ffa4f4aa49039e147cbe26be25c65af043782 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -49,6 +49,8 @@ Core and builtins
 Extension Modules
 -----------------
 
+- collections.deque objects now support a remove() method.
+
 - operator.itemgetter() and operator.attrgetter() now support retrieving
   multiple fields.  This provides direct support for sorting on multiple
   keys (primary, secondary, etc).
index ed10999f01a08cffbc8a30019bc65a3b3492bc14..49c486fd59cfc234b8d4032c9b783377afb2d440 100644 (file)
@@ -368,6 +368,41 @@ deque_len(dequeobject *deque)
        return deque->len;
 }
 
+static PyObject *
+deque_remove(dequeobject *deque, PyObject *value)
+{
+       int i, n=deque->len;
+
+       for (i=0 ; i<n ; i++) {
+               PyObject *item = deque->leftblock->data[deque->leftindex];
+               int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
+               if (cmp > 0) {
+                       PyObject *tgt;
+                       if (deque->len != n) {
+                               PyErr_SetString(PyExc_IndexError, 
+                                       "deque mutated during remove().");
+                               return NULL;
+                       }
+                       tgt = deque_popleft(deque, NULL);
+                       assert (tgt != NULL);
+                       Py_DECREF(tgt);
+                       if (_deque_rotate(deque, i) == -1)
+                               return NULL;
+                       Py_RETURN_NONE;
+               }
+               else if (cmp < 0) {
+                       _deque_rotate(deque, i);
+                       return NULL;
+               }
+               _deque_rotate(deque, -1);
+       }
+       PyErr_SetString(PyExc_ValueError, "deque.remove(x): x not in deque");
+       return NULL;
+}
+
+PyDoc_STRVAR(remove_doc,
+"D.remove(value) -- remove first occurrence of value.");
+
 static int
 deque_clear(dequeobject *deque)
 {
@@ -764,7 +799,7 @@ static PyMethodDef deque_methods[] = {
                METH_NOARGS,     copy_doc},
        {"extend",              (PyCFunction)deque_extend,
                METH_O,          extend_doc},
-       {"extendleft",  (PyCFunction)deque_extendleft,
+       {"extendleft",          (PyCFunction)deque_extendleft,
                METH_O,          extendleft_doc},
        {"pop",                 (PyCFunction)deque_pop,
                METH_NOARGS,     pop_doc},
@@ -772,6 +807,8 @@ static PyMethodDef deque_methods[] = {
                METH_NOARGS,     popleft_doc},
        {"__reduce__",  (PyCFunction)deque_reduce,
                METH_NOARGS,     reduce_doc},
+       {"remove",              (PyCFunction)deque_remove,
+               METH_O,          remove_doc},
        {"__reversed__",        (PyCFunction)deque_reviter,
                METH_NOARGS,     reversed_doc},
        {"rotate",              (PyCFunction)deque_rotate,