https://github.com/python/cpython/commit/da65f38a94c3da515ef7e5081cb5fe81ce97f98e
commit: da65f38a94c3da515ef7e5081cb5fe81ce97f98e
branch: main
author: Sergey Miryanov <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2025-11-02T16:34:49+05:30
summary:
gh-134786: raise error if `Py_TPFLAGS_MANAGED_WEAKREF` or
`Py_TPFLAGS_MANAGED_DICT` is used without `Py_TPFLAGS_HAVE_GC` set (#135863)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst
M Doc/c-api/typeobj.rst
M Doc/extending/newtypes.rst
M Doc/whatsnew/3.15.rst
M Include/object.h
M Lib/test/test_capi/test_type.py
M Modules/_testcapimodule.c
M Objects/typeobject.c
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 5b7cf0c45026a9..59c26a713e4d85 100644
--- a/Doc/c-api/typeobj.rst
+++ b/Doc/c-api/typeobj.rst
@@ -1260,7 +1260,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
This bit indicates that instances of the class have a
:attr:`~object.__dict__`
attribute, and that the space for the dictionary is managed by the VM.
- If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
+ If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
The type traverse function must call :c:func:`PyObject_VisitManagedDict`
and its clear function must call :c:func:`PyObject_ClearManagedDict`.
@@ -1278,6 +1278,8 @@ and :c:data:`PyType_Type` effectively act as defaults.)
This bit indicates that instances of the class should be weakly
referenceable.
+ If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
+
.. versionadded:: 3.12
**Inheritance:**
diff --git a/Doc/extending/newtypes.rst b/Doc/extending/newtypes.rst
index e3612f3a1875ca..26085b5cebd3ad 100644
--- a/Doc/extending/newtypes.rst
+++ b/Doc/extending/newtypes.rst
@@ -560,6 +560,8 @@ For an object to be weakly referenceable, the extension
type must set the
field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should
be left as zero.
+If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
+
Concretely, here is how the statically declared type object would look::
static PyTypeObject TrivialType = {
diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst
index f70345dd2b8d62..7338fb51964b8f 100644
--- a/Doc/whatsnew/3.15.rst
+++ b/Doc/whatsnew/3.15.rst
@@ -951,6 +951,14 @@ New features
(Contributed by Victor Stinner in :gh:`111489`.)
+Changed C APIs
+--------------
+
+* If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` or
:c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
+ flag is set then :c:macro:`Py_TPFLAGS_HAVE_GC` must be set too.
+ (Contributed by Sergey Miryanov in :gh:`134786`)
+
+
Porting to Python 3.15
----------------------
diff --git a/Include/object.h b/Include/object.h
index 7f4b35df3b6263..291e4f0a7ed2dd 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -529,7 +529,7 @@ given type object has a specified feature.
#define Py_TPFLAGS_INLINE_VALUES (1 << 2)
/* Placement of weakref pointers are managed by the VM, not by the type.
- * The VM will automatically set tp_weaklistoffset.
+ * The VM will automatically set tp_weaklistoffset. Implies Py_TPFLAGS_HAVE_GC.
*/
#define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3)
diff --git a/Lib/test/test_capi/test_type.py b/Lib/test/test_capi/test_type.py
index 15fb4a93e2ad74..93874fbee326dd 100644
--- a/Lib/test/test_capi/test_type.py
+++ b/Lib/test/test_capi/test_type.py
@@ -274,3 +274,10 @@ def test_extension_managed_dict_type(self):
obj.__dict__ = {'bar': 3}
self.assertEqual(obj.__dict__, {'bar': 3})
self.assertEqual(obj.bar, 3)
+
+ def test_extension_managed_weakref_nogc_type(self):
+ msg = ("type _testcapi.ManagedWeakrefNoGCType "
+ "has the Py_TPFLAGS_MANAGED_WEAKREF "
+ "flag but not Py_TPFLAGS_HAVE_GC flag")
+ with self.assertRaisesRegex(SystemError, msg):
+ _testcapi.create_managed_weakref_nogc_type()
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst
new file mode 100644
index 00000000000000..664e4d2db384ad
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-24-13-12-58.gh-issue-134786.MF0VVk.rst
@@ -0,0 +1,2 @@
+If :c:macro:`Py_TPFLAGS_MANAGED_DICT` and :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF`
+are used, then :c:macro:`Py_TPFLAGS_HAVE_GC` must be used as well.
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 4e73be20e1b709..e29b9ae354bc1d 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
Py_RETURN_NONE;
}
+
+typedef struct {
+ PyObject_HEAD
+} ManagedWeakrefNoGCObject;
+
+static void
+ManagedWeakrefNoGC_dealloc(PyObject *self)
+{
+ PyObject_ClearWeakRefs(self);
+ PyTypeObject *tp = Py_TYPE(self);
+ tp->tp_free(self);
+ Py_DECREF(tp);
+}
+
+static PyType_Slot ManagedWeakrefNoGC_slots[] = {
+ {Py_tp_dealloc, ManagedWeakrefNoGC_dealloc},
+ {0, 0}
+};
+
+static PyType_Spec ManagedWeakrefNoGC_spec = {
+ .name = "_testcapi.ManagedWeakrefNoGCType",
+ .basicsize = sizeof(ManagedWeakrefNoGCObject),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_MANAGED_WEAKREF),
+ .slots = ManagedWeakrefNoGC_slots,
+};
+
+static PyObject *
+create_managed_weakref_nogc_type(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ return PyType_FromSpec(&ManagedWeakrefNoGC_spec);
+}
+
+
static PyMethodDef TestMethods[] = {
{"set_errno", set_errno, METH_VARARGS},
{"test_config", test_config, METH_NOARGS},
@@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = {
{"test_atexit", test_atexit, METH_NOARGS},
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line),
METH_FASTCALL},
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
+ {"create_managed_weakref_nogc_type",
+ create_managed_weakref_nogc_type, METH_NOARGS},
{NULL, NULL} /* sentinel */
};
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 5841deb454da1f..d56950158073ea 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type)
type->tp_name);
return -1;
}
+ if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
+ PyErr_Format(PyExc_SystemError,
+ "type %s has the Py_TPFLAGS_MANAGED_DICT flag "
+ "but not Py_TPFLAGS_HAVE_GC flag",
+ type->tp_name);
+ return -1;
+ }
type->tp_dictoffset = -1;
}
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
@@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type)
type->tp_name);
return -1;
}
+ if (!(type->tp_flags & Py_TPFLAGS_HAVE_GC)) {
+ PyErr_Format(PyExc_SystemError,
+ "type %s has the Py_TPFLAGS_MANAGED_WEAKREF flag "
+ "but not Py_TPFLAGS_HAVE_GC flag",
+ type->tp_name);
+ return -1;
+ }
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
}
return 0;
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]