From bfe8dfdee5e014f887de25f985dd9afff978c023 Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Tue, 23 Jun 2026 10:57:39 -0400 Subject: [PATCH 1/4] gh-152017: Fix `deque_clear` to no longer alters exceptions --- Lib/test/test_collections.py | 17 +++++++++++++++++ Modules/_collectionsmodule.c | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index b1b2dd2ca5ca0db..244248cb91de776 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -15,6 +15,7 @@ from test.support.import_helper import import_fresh_module import types import unittest +import _testcapi from collections import namedtuple, Counter, OrderedDict, _count_elements from collections import UserDict, UserString, UserList @@ -2475,6 +2476,22 @@ def test_symmetric_difference(self): pp ^= qq self.assertEqual(pp, r) +################################################################################ +### Deque +################################################################################ + +class TestDeque(unittest.TestCase): + + def test_deque_oom(self): + for n in range(1, 80): + d = deque(range(200)) + _testcapi.set_nomemory(n, 0) + try: + d.copy() + _testcapi.remove_mem_hooks() + break + except MemoryError: + _testcapi.remove_mem_hooks() def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(collections)) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index e96a546a818d3d7..c2e8741735b3f86 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -746,9 +746,10 @@ deque_clear(PyObject *self) adversary could cause it to never terminate). */ + PyObject *old_exc = PyErr_GetRaisedException(); b = newblock(deque); + PyErr_SetRaisedException(old_exc); if (b == NULL) { - PyErr_Clear(); goto alternate_method; } From 40dd821cd0c870ed09c97699e7765ebc1c606685 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:02:29 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2026-06-23-15-02-27.gh-issue-152017.wH89hr.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2026-06-23-15-02-27.gh-issue-152017.wH89hr.rst diff --git a/Misc/NEWS.d/next/Library/2026-06-23-15-02-27.gh-issue-152017.wH89hr.rst b/Misc/NEWS.d/next/Library/2026-06-23-15-02-27.gh-issue-152017.wH89hr.rst new file mode 100644 index 000000000000000..b903e39d6215054 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-23-15-02-27.gh-issue-152017.wH89hr.rst @@ -0,0 +1 @@ +:class:`collections.deque` no longer suppresses :exc:`MemoryError` in certain cases. From 9fc21a3e23b12e1d48540bfc6d66b4dfb826311e Mon Sep 17 00:00:00 2001 From: Brij <97006829+bkap123@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:31:51 -0400 Subject: [PATCH 3/4] remove test --- Lib/test/test_collections.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 244248cb91de776..b1b2dd2ca5ca0db 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -15,7 +15,6 @@ from test.support.import_helper import import_fresh_module import types import unittest -import _testcapi from collections import namedtuple, Counter, OrderedDict, _count_elements from collections import UserDict, UserString, UserList @@ -2476,22 +2475,6 @@ def test_symmetric_difference(self): pp ^= qq self.assertEqual(pp, r) -################################################################################ -### Deque -################################################################################ - -class TestDeque(unittest.TestCase): - - def test_deque_oom(self): - for n in range(1, 80): - d = deque(range(200)) - _testcapi.set_nomemory(n, 0) - try: - d.copy() - _testcapi.remove_mem_hooks() - break - except MemoryError: - _testcapi.remove_mem_hooks() def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(collections)) From 576adb56d2a48d1ea590b5eca800d66797f6328c Mon Sep 17 00:00:00 2001 From: Brij <97006829+brijkapadia@users.noreply.github.com> Date: Tue, 27 Oct 2026 22:16:40 -0400 Subject: [PATCH 4/4] make cheap --- Modules/_collectionsmodule.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index 3e20a79044216a0..a471501ae18d7d2 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -175,17 +175,11 @@ struct dequeobject { static inline block * newblock(dequeobject *deque) { - block *b; if (deque->numfreeblocks) { deque->numfreeblocks--; return deque->freeblocks[deque->numfreeblocks]; } - b = PyMem_Malloc(sizeof(block)); - if (b != NULL) { - return b; - } - PyErr_NoMemory(); - return NULL; + return PyMem_Malloc(sizeof(block)); } static inline void @@ -212,6 +206,7 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds) b = newblock(deque); if (b == NULL) { + PyErr_NoMemory(); Py_DECREF(deque); return NULL; } @@ -343,6 +338,7 @@ deque_append_lock_held(dequeobject *deque, PyObject *item, Py_ssize_t maxlen) if (deque->rightindex == BLOCKLEN - 1) { block *b = newblock(deque); if (b == NULL) { + PyErr_NoMemory(); Py_DECREF(item); return -1; } @@ -392,6 +388,7 @@ deque_appendleft_lock_held(dequeobject *deque, PyObject *item, if (deque->leftindex == 0) { block *b = newblock(deque); if (b == NULL) { + PyErr_NoMemory(); Py_DECREF(item); return -1; } @@ -744,9 +741,7 @@ deque_clear(PyObject *self) adversary could cause it to never terminate). */ - PyObject *old_exc = PyErr_GetRaisedException(); b = newblock(deque); - PyErr_SetRaisedException(old_exc); if (b == NULL) { goto alternate_method; } @@ -858,6 +853,7 @@ deque_inplace_repeat_impl(dequeobject *deque, Py_ssize_t n) if (deque->rightindex == BLOCKLEN - 1) { block *b = newblock(deque); if (b == NULL) { + PyErr_NoMemory(); Py_SET_SIZE(deque, Py_SIZE(deque) + i); return NULL; } @@ -978,8 +974,10 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) if (leftindex == 0) { if (b == NULL) { b = newblock(deque); - if (b == NULL) + if (b == NULL) { + PyErr_NoMemory(); goto done; + } } b->rightlink = leftblock; CHECK_END(leftblock->leftlink); @@ -1022,8 +1020,10 @@ _deque_rotate(dequeobject *deque, Py_ssize_t n) if (rightindex == BLOCKLEN - 1) { if (b == NULL) { b = newblock(deque); - if (b == NULL) + if (b == NULL) { + PyErr_NoMemory(); goto done; + } } b->leftlink = rightblock; CHECK_END(rightblock->rightlink);