Drop what a deque with maxlen of zero is handed (#8567)

append and appendleft trimmed before pushing, and the test they used,
maxlen == len, holds for an empty deque whose bound is zero. The pop then
had nothing to remove and the item stayed:

    >>> d = deque(maxlen=0)
    >>> d.append(1)
    >>> list(d)
    [1]

Both now push first and trim after, which is the order CPython uses, so a
bound of zero drops what just arrived. extend, extendleft, insert, rotate,
the operators and the constructor were already right.

Assisted-by: Claude Code:claude-opus-5
This commit is contained in:
Luan Taraschi authored and GitHub committed 2026-08-22 12:42:40 +09:00
1 parent d86ec309b1
commit 003307131f
2 files changed
+53 -4

No files matched your search

+10 -4
View File
@@ -77,6 +77,10 @@ mod _collections {
fn borrow_deque_mut(&self) -> PyRwLockWriteGuard<'_, VecDeque<PyObjectRef>> {
self.deque.write()
}
fn is_over_maxlen(&self, deque: &VecDeque<PyObjectRef>) -> bool {
self.maxlen.is_some_and(|maxlen| deque.len() > maxlen)
}
}
#[pyclass(
@@ -96,20 +100,22 @@ mod _collections {
fn append(&self, obj: PyObjectRef) {
self.state.fetch_add(1);
let mut deque = self.borrow_deque_mut();
if self.maxlen == Some(deque.len()) {
deque.push_back(obj);
// Trim after pushing, so that a `maxlen` of zero drops what just
// arrived instead of popping from an empty deque and keeping it.
if self.is_over_maxlen(&deque) {
deque.pop_front();
}
deque.push_back(obj);
}
#[pymethod]
fn appendleft(&self, obj: PyObjectRef) {
self.state.fetch_add(1);
let mut deque = self.borrow_deque_mut();
if self.maxlen == Some(deque.len()) {
deque.push_front(obj);
if self.is_over_maxlen(&deque) {
deque.pop_back();
}
deque.push_front(obj);
}
#[pymethod]
@@ -108,3 +108,46 @@ if sys.implementation.name == "rustpython":
# until the allocator gives up, so it is left out of this check.
with assert_raises(MemoryError):
deque([0]) * sys.maxsize
# maxlen=0 keeps nothing, whichever end the item arrives at.
d = deque(maxlen=0)
d.append(1)
d.appendleft(2)
assert list(d) == []
assert len(d) == 0
assert d.maxlen == 0
d = deque(maxlen=0)
d.extend("abc")
d.extendleft("abc")
d += "abc"
assert list(d) == []
assert list(deque("abc", maxlen=0)) == []
assert list(deque("ab", maxlen=0) * 3) == []
assert list(deque("ab", maxlen=0) + deque("cd")) == []
d = deque("abc", maxlen=0)
d.rotate(1)
assert list(d) == []
assert_raises(IndexError, deque(maxlen=0).insert, 0, 1)
# A bounded deque still drops from the far end, and only once it is full.
d = deque(maxlen=1)
d.append(1)
assert list(d) == [1]
d.append(2)
assert list(d) == [2]
d.appendleft(3)
assert list(d) == [3]
d = deque("ab", maxlen=3)
d.append("c")
assert list(d) == ["a", "b", "c"]
d.append("d")
assert list(d) == ["b", "c", "d"]
d.appendleft("z")
assert list(d) == ["z", "b", "c"]