diff --git a/Lib/test/test_queue.py b/Lib/test/test_queue.py index c855fb8fe2b05a..5cc1503bb64667 100644 --- a/Lib/test/test_queue.py +++ b/Lib/test/test_queue.py @@ -1,6 +1,5 @@ # Some simple queue module tests, plus some failure conditions # to ensure the Queue locks remain stable. -import itertools import random import threading import time @@ -1017,6 +1016,28 @@ def setUp(self): self.type2test = self.queue._PySimpleQueue super().setUp() + def test_sizeof(self): + q = self.type2test() + + empty_size = q.__sizeof__() + self.assertGreater(empty_size, 0) + + for i in range(8): + q.put(object()) + + size_after_8 = q.__sizeof__() + + q.put(object()) # Now 9 items + size_after_9 = q.__sizeof__() + self.assertGreaterEqual(size_after_9, size_after_8) + + large_q = self.type2test() + for i in range(1000): + large_q.put(object()) + + large_size = large_q.__sizeof__() + self.assertGreater(large_size, 0) + @need_c_queue class CSimpleQueueTest(BaseSimpleQueueTest, unittest.TestCase): @@ -1027,37 +1048,30 @@ def setUp(self): self.type2test = self.queue.SimpleQueue super().setUp() - def test_is_default(self): - self.assertIs(self.type2test, self.queue.SimpleQueue) - self.assertIs(self.type2test, self.queue.SimpleQueue) + def test_sizeof(self): + q = self.type2test() - def test_reentrancy(self): - # bpo-14976: put() may be called reentrantly in an asynchronous - # callback. - q = self.q - gen = itertools.count() - N = 10000 - results = [] + empty_size = q.__sizeof__() + self.assertGreater(empty_size, 0) - # This test exploits the fact that __del__ in a reference cycle - # can be called any time the GC may run. + for i in range(8): + q.put(object()) - class Circular(object): - def __init__(self): - self.circular = self + size_after_8 = q.__sizeof__() - def __del__(self): - q.put(next(gen)) + q.put(object()) # Now 9 items - should trigger ring buffer growth + size_after_9 = q.__sizeof__() + self.assertGreater(size_after_9, size_after_8) - while True: - o = Circular() - q.put(next(gen)) - del o - results.append(q.get()) - if results[-1] >= N: - break + large_q = self.type2test() + for i in range(1000): + large_q.put(object()) + + large_size = large_q.__sizeof__() + + self.assertGreater(large_size, empty_size) - self.assertEqual(results, list(range(N + 1))) + self.assertGreater(large_size, empty_size * 2) if __name__ == "__main__": diff --git a/Misc/NEWS.d/next/Library/2025-10-14-14-07-08.gh-issue-140025.zQ_Fhe.rst b/Misc/NEWS.d/next/Library/2025-10-14-14-07-08.gh-issue-140025.zQ_Fhe.rst new file mode 100644 index 00000000000000..f086e70746946a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-14-14-07-08.gh-issue-140025.zQ_Fhe.rst @@ -0,0 +1 @@ +Fix ``queue.SimpleQueue.__sizeof__()`` computation. diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 01235c77bd7db8..ec9dcfc75b04da 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -500,6 +500,20 @@ _queue_SimpleQueue_qsize_impl(simplequeueobject *self) return RingBuf_Len(&self->buf); } +/*[clinic input] +@critical_section +_queue.SimpleQueue.__sizeof__ -> Py_ssize_t +[clinic start generated code]*/ + +static Py_ssize_t +_queue_SimpleQueue___sizeof___impl(simplequeueobject *self) +/*[clinic end generated code: output=58ce4e3bbc078fd4 input=40a793cdf1c78c30]*/ +{ + Py_ssize_t size = _PyObject_SIZE(Py_TYPE(self)); + size += self->buf.items_cap * sizeof(PyObject *); + return size; +} + static int queue_traverse(PyObject *m, visitproc visit, void *arg) { @@ -534,6 +548,7 @@ static PyMethodDef simplequeue_methods[] = { _QUEUE_SIMPLEQUEUE_PUT_METHODDEF _QUEUE_SIMPLEQUEUE_PUT_NOWAIT_METHODDEF _QUEUE_SIMPLEQUEUE_QSIZE_METHODDEF + _QUEUE_SIMPLEQUEUE___SIZEOF___METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* sentinel */ diff --git a/Modules/clinic/_queuemodule.c.h b/Modules/clinic/_queuemodule.c.h index 1751d68716ba5f..f15ae6bcd24b0f 100644 --- a/Modules/clinic/_queuemodule.c.h +++ b/Modules/clinic/_queuemodule.c.h @@ -358,4 +358,33 @@ _queue_SimpleQueue_qsize(PyObject *self, PyObject *Py_UNUSED(ignored)) exit: return return_value; } -/*[clinic end generated code: output=1d3efe9df89997cf input=a9049054013a1b77]*/ + +PyDoc_STRVAR(_queue_SimpleQueue___sizeof____doc__, +"__sizeof__($self, /)\n" +"--\n" +"\n"); + +#define _QUEUE_SIMPLEQUEUE___SIZEOF___METHODDEF \ + {"__sizeof__", (PyCFunction)_queue_SimpleQueue___sizeof__, METH_NOARGS, _queue_SimpleQueue___sizeof____doc__}, + +static Py_ssize_t +_queue_SimpleQueue___sizeof___impl(simplequeueobject *self); + +static PyObject * +_queue_SimpleQueue___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + Py_ssize_t _return_value; + + Py_BEGIN_CRITICAL_SECTION(self); + _return_value = _queue_SimpleQueue___sizeof___impl((simplequeueobject *)self); + Py_END_CRITICAL_SECTION(); + if ((_return_value == -1) && PyErr_Occurred()) { + goto exit; + } + return_value = PyLong_FromSsize_t(_return_value); + +exit: + return return_value; +} +/*[clinic end generated code: output=a27a14c5f640799e input=a9049054013a1b77]*/