From 4dfa8eccbf3b272bcd9c8bdcbcf2fba7d324c547 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Dec 2025 11:17:29 +0100 Subject: [PATCH 1/2] _PyType_GetBaseByToken_Borrow can't fail --- Include/internal/pycore_typeobject.h | 2 ++ Modules/_ctypes/ctypes.h | 3 ++- Objects/typeobject.c | 25 ++++++++++++++----------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_typeobject.h b/Include/internal/pycore_typeobject.h index 830e87f5d82789..abaa60890b55c8 100644 --- a/Include/internal/pycore_typeobject.h +++ b/Include/internal/pycore_typeobject.h @@ -155,6 +155,8 @@ extern int _PyType_CacheGetItemForSpecialization(PyHeapTypeObject *ht, PyObject // Precalculates count of non-unique slots and fills wrapperbase.name_count. extern int _PyType_InitSlotDefs(PyInterpreterState *interp); +// Like PyType_GetBaseByToken, but does not modify refcounts. +// Cannot fail; arguments must be valid. PyAPI_FUNC(int) _PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **result); diff --git a/Modules/_ctypes/ctypes.h b/Modules/_ctypes/ctypes.h index bb5b7b4ebd252c..478daecad55b94 100644 --- a/Modules/_ctypes/ctypes.h +++ b/Modules/_ctypes/ctypes.h @@ -608,7 +608,8 @@ PyStgInfo_FromAny(ctypes_state *state, PyObject *obj, StgInfo **result) return _stginfo_from_type(state, Py_TYPE(obj), result); } -/* A variant of PyStgInfo_FromType that doesn't need the state, +/* A variant of PyStgInfo_FromType that doesn't need the state + * and doesn't modify any refcounts, * so it can be called from finalization functions when the module * state is torn down. */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 222800fbb54829..502a19796ab3fc 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5879,21 +5879,13 @@ get_base_by_token_recursive(PyObject *bases, void *token) int _PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **result) { + assert(token != NULL); + assert(PyType_Check(type)); + if (result != NULL) { *result = NULL; } - if (token == NULL) { - PyErr_Format(PyExc_SystemError, - "PyType_GetBaseByToken called with token=NULL"); - return -1; - } - if (!PyType_Check(type)) { - PyErr_Format(PyExc_TypeError, - "expected a type, got a '%T' object", type); - return -1; - } - if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) { // No static type has a heaptype superclass, // which is ensured by type_ready_mro(). @@ -5939,6 +5931,17 @@ _PyType_GetBaseByToken_Borrow(PyTypeObject *type, void *token, PyTypeObject **re int PyType_GetBaseByToken(PyTypeObject *type, void *token, PyTypeObject **result) { + if (token == NULL) { + PyErr_Format(PyExc_SystemError, + "PyType_GetBaseByToken called with token=NULL"); + return -1; + } + if (!PyType_Check(type)) { + PyErr_Format(PyExc_TypeError, + "expected a type, got a '%T' object", type); + return -1; + } + int res = _PyType_GetBaseByToken_Borrow(type, token, result); if (res > 0 && result) { Py_INCREF(*result); From 37f8fbfea8a21a5d3a77a31d2fe1e33ecb45d612 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 10 Dec 2025 11:20:19 +0100 Subject: [PATCH 2/2] Add blurb --- .../next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst diff --git a/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst b/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst new file mode 100644 index 00000000000000..871a03a6fd1021 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-12-10-11-20-05.gh-issue-123241.oYg2n7.rst @@ -0,0 +1,2 @@ +Avoid reference count operations in garbage collection of :mod:`ctypes` +objects.