gh-134786: raise error if Py_TPFLAGS_MANAGED_WEAKREF or Py_TPFLAGS_MANAGED_DICT is used without Py_TPFLAGS_HAVE_GC set (#135863)
This commit is contained in:
@@ -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__`
|
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.
|
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`
|
The type traverse function must call :c:func:`PyObject_VisitManagedDict`
|
||||||
and its clear function must call :c:func:`PyObject_ClearManagedDict`.
|
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
|
This bit indicates that instances of the class should be weakly
|
||||||
referenceable.
|
referenceable.
|
||||||
|
|
||||||
|
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` must also be set.
|
||||||
|
|
||||||
.. versionadded:: 3.12
|
.. versionadded:: 3.12
|
||||||
|
|
||||||
**Inheritance:**
|
**Inheritance:**
|
||||||
|
|||||||
@@ -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
|
field. The legacy :c:member:`~PyTypeObject.tp_weaklistoffset` field should
|
||||||
be left as zero.
|
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::
|
Concretely, here is how the statically declared type object would look::
|
||||||
|
|
||||||
static PyTypeObject TrivialType = {
|
static PyTypeObject TrivialType = {
|
||||||
|
|||||||
@@ -951,6 +951,14 @@ New features
|
|||||||
(Contributed by Victor Stinner in :gh:`111489`.)
|
(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
|
Porting to Python 3.15
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ given type object has a specified feature.
|
|||||||
#define Py_TPFLAGS_INLINE_VALUES (1 << 2)
|
#define Py_TPFLAGS_INLINE_VALUES (1 << 2)
|
||||||
|
|
||||||
/* Placement of weakref pointers are managed by the VM, not by the type.
|
/* 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)
|
#define Py_TPFLAGS_MANAGED_WEAKREF (1 << 3)
|
||||||
|
|
||||||
|
|||||||
@@ -274,3 +274,10 @@ class TypeTests(unittest.TestCase):
|
|||||||
obj.__dict__ = {'bar': 3}
|
obj.__dict__ = {'bar': 3}
|
||||||
self.assertEqual(obj.__dict__, {'bar': 3})
|
self.assertEqual(obj.__dict__, {'bar': 3})
|
||||||
self.assertEqual(obj.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()
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -2562,6 +2562,39 @@ toggle_reftrace_printer(PyObject *ob, PyObject *arg)
|
|||||||
Py_RETURN_NONE;
|
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[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
{"set_errno", set_errno, METH_VARARGS},
|
{"set_errno", set_errno, METH_VARARGS},
|
||||||
{"test_config", test_config, METH_NOARGS},
|
{"test_config", test_config, METH_NOARGS},
|
||||||
@@ -2656,6 +2689,8 @@ static PyMethodDef TestMethods[] = {
|
|||||||
{"test_atexit", test_atexit, METH_NOARGS},
|
{"test_atexit", test_atexit, METH_NOARGS},
|
||||||
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
|
{"code_offset_to_line", _PyCFunction_CAST(code_offset_to_line), METH_FASTCALL},
|
||||||
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
|
{"toggle_reftrace_printer", toggle_reftrace_printer, METH_O},
|
||||||
|
{"create_managed_weakref_nogc_type",
|
||||||
|
create_managed_weakref_nogc_type, METH_NOARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8898,6 +8898,13 @@ type_ready_preheader(PyTypeObject *type)
|
|||||||
type->tp_name);
|
type->tp_name);
|
||||||
return -1;
|
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;
|
type->tp_dictoffset = -1;
|
||||||
}
|
}
|
||||||
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
|
if (type->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF) {
|
||||||
@@ -8910,6 +8917,13 @@ type_ready_preheader(PyTypeObject *type)
|
|||||||
type->tp_name);
|
type->tp_name);
|
||||||
return -1;
|
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;
|
type->tp_weaklistoffset = MANAGED_WEAKREF_OFFSET;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user