From 3743432302e9b31d4fe0db31485543a306057fc8 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 26 Jan 2016 21:44:16 -0800 Subject: [PATCH] Issue #26194: Fix undefined behavior for deque.insert() when len(d) == maxlen --- Doc/library/collections.rst | 3 +++ Lib/test/test_deque.py | 14 ++++++++++++++ Misc/NEWS | 4 ++++ Modules/_collectionsmodule.c | 7 +++++++ 4 files changed, 28 insertions(+) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 00d2916415c..e89da350230 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -477,6 +477,9 @@ or subtracting from an empty counter. Insert *x* into the deque at position *i*. + If the insertion causes a bounded deque to grow beyond *maxlen*, the + rightmost element is then removed to restore the size limit. + .. versionadded:: 3.5 diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index 87187161ab3..d2a463351bd 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -304,6 +304,20 @@ class TestBasic(unittest.TestCase): s.insert(i, 'Z') self.assertEqual(list(d), s) + def test_index_bug_26194(self): + data = 'ABC' + for i in range(len(data) + 1): + d = deque(data, len(data)) + d.insert(i, None) + s = list(data) + s.insert(i, None) + s.pop() + self.assertEqual(list(d), s) + if i < len(data): + self.assertIsNone(d[i]) + else: + self.assertTrue(None not in d) + def test_imul(self): for n in (-10, -1, 0, 1, 2, 10, 1000): d = deque() diff --git a/Misc/NEWS b/Misc/NEWS index 75df5325271..5e461f9205c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -17,6 +17,10 @@ Core and Builtins Python 3.5.1 to hide the exact implementation of atomic C types, to avoid compiler issues. +- Issue #26194: Deque.insert() gave odd results for bounded deques that had + reached their maximum size. Now, the insert will happen normally and then + any overflowing element will be truncated from the right side. + - Issue #25843: When compiling code, don't merge constants if they are equal but have a different types. For example, ``f1, f2 = lambda: 1, lambda: 1.0`` is now correctly compiled to two different functions: ``f1()`` returns ``1`` diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 1a334285c16..b9c4f3b9263 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -973,10 +973,17 @@ deque_insert(dequeobject *deque, PyObject *args) Py_ssize_t index; Py_ssize_t n = Py_SIZE(deque); PyObject *value; + PyObject *oldvalue; PyObject *rv; if (!PyArg_ParseTuple(args, "nO:insert", &index, &value)) return NULL; + if (deque->maxlen == Py_SIZE(deque)) { + if (index >= deque->maxlen || Py_SIZE(deque) == 0) + Py_RETURN_NONE; + oldvalue = deque_pop(deque, NULL); + Py_DECREF(oldvalue); + } if (index >= n) return deque_append(deque, value); if (index <= -n || index == 0)