diff --git a/CHANGES.txt b/CHANGES.txt index 3408738..3a03ec4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,10 @@ Revision history ---------------- +unreleased + * Fix PDeque.remove() and PDeque.reverse() dropping the maxlen of a bounded + deque, which silently unbounded the result so later appends could grow it + past its declared maximum length. + 0.21.0, 2026-01-11 * Drop official support for Python 3.8 and 3.9, these versions are no longer part of the test matrix. * Add Python 3.13 and 3.14 to test matrix and re-establish test coverage. diff --git a/pyrsistent/_pdeque.py b/pyrsistent/_pdeque.py index 0f25936..4ce7620 100644 --- a/pyrsistent/_pdeque.py +++ b/pyrsistent/_pdeque.py @@ -272,13 +272,13 @@ def remove(self, elem): pdeque([1, 2]) """ try: - return PDeque(self._left_list.remove(elem), self._right_list, self._length - 1) + return PDeque(self._left_list.remove(elem), self._right_list, self._length - 1, self._maxlen) except ValueError: # Value not found in left list, try the right list try: # This is severely inefficient with a double reverse, should perhaps implement a remove_last()? return PDeque(self._left_list, - self._right_list.reverse().remove(elem).reverse(), self._length - 1) + self._right_list.reverse().remove(elem).reverse(), self._length - 1, self._maxlen) except ValueError as e: raise ValueError('{0} not found in PDeque'.format(elem)) from e @@ -294,7 +294,7 @@ def reverse(self): >>> reversed(pdeque([1, 2, 3])) pdeque([3, 2, 1]) """ - return PDeque(self._right_list, self._left_list, self._length) + return PDeque(self._right_list, self._left_list, self._length, self._maxlen) __reversed__ = reverse def rotate(self, steps): diff --git a/tests/deque_test.py b/tests/deque_test.py index 7798a75..30b1823 100644 --- a/tests/deque_test.py +++ b/tests/deque_test.py @@ -169,6 +169,14 @@ def test_remove(): assert pdeque([1, 2, 3, 3, 4, 5, 4, 6]).remove(4) == pdeque([1, 2, 3, 3, 5, 4, 6]) +def test_remove_preserves_maxlen(): + # A bounded deque must stay bounded after a remove, or later appends grow + # it past maxlen. + x = pdeque([1, 2, 3], maxlen=3).remove(2) + assert x.maxlen == 3 + assert x.append(4).append(5) == pdeque([3, 4, 5], maxlen=3) + + def test_remove_element_missing(): with pytest.raises(ValueError): pdeque().remove(2) @@ -182,6 +190,12 @@ def test_reverse(): assert pdeque().reverse() == pdeque() +def test_reverse_preserves_maxlen(): + x = pdeque([1, 2, 3], maxlen=3).reverse() + assert x.maxlen == 3 + assert x.append(4) == pdeque([2, 1, 4], maxlen=3) + + def test_rotate_right(): assert pdeque([1, 2, 3, 4, 5]).rotate(2) == pdeque([4, 5, 1, 2, 3]) assert pdeque([1, 2]).rotate(0) == pdeque([1, 2])