]> granicus.if.org Git - python/commitdiff
* Fix ref counting in extend() and extendleft().
authorRaymond Hettinger <python@rcn.com>
Sat, 7 Feb 2004 02:45:22 +0000 (02:45 +0000)
committerRaymond Hettinger <python@rcn.com>
Sat, 7 Feb 2004 02:45:22 +0000 (02:45 +0000)
* Let deques support reversed().

Doc/lib/libcollections.tex
Lib/test/test_deque.py
Modules/collectionsmodule.c
Objects/enumobject.c

index ebb2079795403852756500dcd96d214d63ed3074..55ab43108649be79f15b6a0feb40c054ae203923 100644 (file)
@@ -60,7 +60,7 @@ Deque objects support the following methods:
 
 In addition to the above, deques support iteration, membership testing
 using the \keyword{in} operator, \samp{len(d)}, \samp{copy.copy(d)},
-\samp{copy.deepcopy(d)}, and pickling.
+\samp{copy.deepcopy(d)}, \samp{reversed(d)} and pickling.
 
 Example:
 
@@ -84,6 +84,8 @@ deque(['f', 'g', 'h', 'i', 'j'])
 'f'
 >>> list(d)                 # list the contents of the deque
 ['g', 'h', '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')         # extend() will append many elements at once
index 51ce7b0d672d67f43808a8c192518642f8fea62e..5f2417f7f531bf3fb64e316038f8b9fbe370bd15 100644 (file)
@@ -182,6 +182,10 @@ class TestBasic(unittest.TestCase):
         self.assertNotEqual(id(d), id(e))
         self.assertEqual(list(d), list(e))
 
+    def test_reversed(self):
+        for s in ('abcd', xrange(2000)):
+            self.assertEqual(list(reversed(deque(s))), list(reversed(s)))
+
 def R(seqn):
     'Regular generator'
     for i in seqn:
index 83be6ac1def2bb855f39bcb363df241e5104845a..3533fc5e2f279e2dfe0bbff253c70f92e4f04c59 100644 (file)
@@ -188,14 +188,16 @@ deque_extend(dequeobject *deque, PyObject *iterable)
                deque->len++;
                if (deque->rightindex == BLOCKLEN) {
                        block *b = newblock(deque->rightblock, NULL);
-                       if (b == NULL)
+                       if (b == NULL) {
+                               Py_DECREF(item);
+                               Py_DECREF(it);
                                return NULL;
+                       }
                        assert(deque->rightblock->rightlink == NULL);
                        deque->rightblock->rightlink = b;
                        deque->rightblock = b;
                        deque->rightindex = 0;
                }
-               Py_INCREF(item);
                deque->rightblock->data[deque->rightindex] = item;
        }
        Py_DECREF(it);
@@ -221,14 +223,16 @@ deque_extendleft(dequeobject *deque, PyObject *iterable)
                deque->len++;
                if (deque->leftindex == -1) {
                        block *b = newblock(NULL, deque->leftblock);
-                       if (b == NULL)
+                       if (b == NULL) {
+                               Py_DECREF(item);
+                               Py_DECREF(it);
                                return NULL;
+                       }
                        assert(deque->leftblock->leftlink == NULL);
                        deque->leftblock->leftlink = b;
                        deque->leftblock = b;
                        deque->leftindex = BLOCKLEN - 1;
                }
-               Py_INCREF(item);
                deque->leftblock->data[deque->leftindex] = item;
        }
        Py_DECREF(it);
@@ -444,6 +448,9 @@ static PySequenceMethods deque_as_sequence = {
 /* deque object ********************************************************/
 
 static PyObject *deque_iter(dequeobject *deque);
+static PyObject *deque_reviter(dequeobject *deque);
+PyDoc_STRVAR(reversed_doc, 
+       "D.__reversed__() -- return a reverse iterator over the deque");
 
 static PyMethodDef deque_methods[] = {
        {"append",              (PyCFunction)deque_append,      
@@ -460,6 +467,8 @@ static PyMethodDef deque_methods[] = {
                METH_NOARGS,     popleft_doc},
        {"__reduce__",  (PyCFunction)deque_reduce,      
                METH_NOARGS,     reduce_doc},
+       {"__reversed__",        (PyCFunction)deque_reviter,     
+               METH_NOARGS,     reversed_doc},
        {"extend",              (PyCFunction)deque_extend,      
                METH_O,          extend_doc},
        {"extendleft",  (PyCFunction)deque_extendleft,  
@@ -608,6 +617,83 @@ PyTypeObject dequeiter_type = {
        0,
 };
 
+/*********************** Deque Reverse Iterator **************************/
+
+PyTypeObject dequereviter_type;
+
+static PyObject *
+deque_reviter(dequeobject *deque)
+{
+       dequeiterobject *it;
+
+       it = PyObject_New(dequeiterobject, &dequereviter_type);
+       if (it == NULL)
+               return NULL;
+       it->b = deque->rightblock;
+       it->index = deque->rightindex;
+       Py_INCREF(deque);
+       it->deque = deque;
+       it->len = deque->len;
+       return (PyObject *)it;
+}
+
+static PyObject *
+dequereviter_next(dequeiterobject *it)
+{
+       PyObject *item;
+       if (it->b == it->deque->leftblock && it->index < it->deque->leftindex)
+               return NULL;
+
+       if (it->len != it->deque->len) {
+               it->len = -1; /* Make this state sticky */
+               PyErr_SetString(PyExc_RuntimeError,
+                               "deque changed size during iteration");
+               return NULL;
+       }
+
+       item = it->b->data[it->index];
+       it->index--;
+       if (it->index == -1 && it->b->leftlink != NULL) {
+               it->b = it->b->leftlink;
+               it->index = BLOCKLEN - 1;
+       }
+       Py_INCREF(item);
+       return item;
+}
+
+PyTypeObject dequereviter_type = {
+       PyObject_HEAD_INIT(NULL)
+       0,                                      /* ob_size */
+       "deque_reverse_iterator",               /* tp_name */
+       sizeof(dequeiterobject),                /* tp_basicsize */
+       0,                                      /* tp_itemsize */
+       /* methods */
+       (destructor)dequeiter_dealloc,          /* tp_dealloc */
+       0,                                      /* tp_print */
+       0,                                      /* tp_getattr */
+       0,                                      /* tp_setattr */
+       0,                                      /* tp_compare */
+       0,                                      /* tp_repr */
+       0,                                      /* tp_as_number */
+       0,                                      /* tp_as_sequence */
+       0,                                      /* tp_as_mapping */
+       0,                                      /* tp_hash */
+       0,                                      /* tp_call */
+       0,                                      /* tp_str */
+       PyObject_GenericGetAttr,                /* tp_getattro */
+       0,                                      /* tp_setattro */
+       0,                                      /* tp_as_buffer */
+       Py_TPFLAGS_DEFAULT,                     /* tp_flags */
+       0,                                      /* tp_doc */
+       0,                                      /* tp_traverse */
+       0,                                      /* tp_clear */
+       0,                                      /* tp_richcompare */
+       0,                                      /* tp_weaklistoffset */
+       PyObject_SelfIter,                      /* tp_iter */
+       (iternextfunc)dequereviter_next,        /* tp_iternext */
+       0,
+};
+
 /* module level code ********************************************************/
 
 PyDoc_STRVAR(module_doc,
@@ -629,5 +715,8 @@ initcollections(void)
        if (PyType_Ready(&dequeiter_type) < 0)
                return; 
 
+       if (PyType_Ready(&dequereviter_type) < 0)
+               return;
+
        return;
 }
index 8b2e6e116ba10861b49af06f19f78e8559351aaf..7ed58da09e5808145a440e33eab2984523b392ad 100644 (file)
@@ -174,8 +174,7 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
        if (!PyArg_UnpackTuple(args, "reversed", 1, 1, &seq))
                return NULL;
 
-       /* Special case optimization for xrange and lists */
-       if (PyRange_Check(seq) || PyList_Check(seq))
+       if (PyObject_HasAttrString(seq, "__reversed__"))
                return PyObject_CallMethod(seq, "__reversed__", NULL);
 
        if (!PySequence_Check(seq)) {