gh-107073: Make PyObject_VisitManagedDict() public (#108763)

Make PyObject_VisitManagedDict() and PyObject_ClearManagedDict()
functions public in Python 3.13 C API.

* Rename _PyObject_VisitManagedDict() to PyObject_VisitManagedDict().
* Rename _PyObject_ClearManagedDict() to PyObject_ClearManagedDict().
* Document these functions.
This commit is contained in:
Victor Stinner 2023-10-02 19:24:08 +02:00 committed by GitHub
parent 6387b5313c
commit fc2cb86d21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 77 additions and 24 deletions

View File

@ -489,3 +489,21 @@ Object Protocol
:c:macro:`Py_TPFLAGS_ITEMS_AT_END` set. :c:macro:`Py_TPFLAGS_ITEMS_AT_END` set.
.. versionadded:: 3.12 .. versionadded:: 3.12
.. c:function:: int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
Visit the managed dictionary of *obj*.
This function must only be called in a traverse function of the type which
has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set.
.. versionadded:: 3.13
.. c:function:: void PyObject_ClearManagedDict(PyObject *obj)
Clear the managed dictionary of *obj*.
This function must only be called in a traverse function of the type which
has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set.
.. versionadded:: 3.13

View File

@ -1131,6 +1131,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
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` should also be set.
The type traverse function must call :c:func:`PyObject_VisitManagedDict`
and its clear function must call :c:func:`PyObject_ClearManagedDict`.
.. versionadded:: 3.12 .. versionadded:: 3.12
**Inheritance:** **Inheritance:**
@ -1368,6 +1371,23 @@ and :c:data:`PyType_Type` effectively act as defaults.)
debugging aid you may want to visit it anyway just so the :mod:`gc` module's debugging aid you may want to visit it anyway just so the :mod:`gc` module's
:func:`~gc.get_referents` function will include it. :func:`~gc.get_referents` function will include it.
Heap types (:c:macro:`Py_TPFLAGS_HEAPTYPE`) must visit their type with::
Py_VISIT(Py_TYPE(self));
It is only needed since Python 3.9. To support Python 3.8 and older, this
line must be conditionnal::
#if PY_VERSION_HEX >= 0x03090000
Py_VISIT(Py_TYPE(self));
#endif
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
:c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
:c:func:`PyObject_VisitManagedDict` like this::
PyObject_VisitManagedDict((PyObject*)self, visit, arg);
.. warning:: .. warning::
When implementing :c:member:`~PyTypeObject.tp_traverse`, only the When implementing :c:member:`~PyTypeObject.tp_traverse`, only the
members that the instance *owns* (by having :term:`strong references members that the instance *owns* (by having :term:`strong references
@ -1451,6 +1471,12 @@ and :c:data:`PyType_Type` effectively act as defaults.)
so that *self* knows the contained object can no longer be used. The so that *self* knows the contained object can no longer be used. The
:c:func:`Py_CLEAR` macro performs the operations in a safe order. :c:func:`Py_CLEAR` macro performs the operations in a safe order.
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
:c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
:c:func:`PyObject_ClearManagedDict` like this::
PyObject_ClearManagedDict((PyObject*)self);
Note that :c:member:`~PyTypeObject.tp_clear` is not *always* called Note that :c:member:`~PyTypeObject.tp_clear` is not *always* called
before an instance is deallocated. For example, when reference counting before an instance is deallocated. For example, when reference counting
is enough to determine that an object is no longer used, the cyclic garbage is enough to determine that an object is no longer used, the cyclic garbage
@ -1801,7 +1827,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
field is ``NULL`` then no :attr:`~object.__dict__` gets created for instances. field is ``NULL`` then no :attr:`~object.__dict__` gets created for instances.
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
:c:member:`~PyTypeObject.tp_dict` field, then :c:member:`~PyTypeObject.tp_flags` field, then
:c:member:`~PyTypeObject.tp_dictoffset` will be set to ``-1``, to indicate :c:member:`~PyTypeObject.tp_dictoffset` will be set to ``-1``, to indicate
that it is unsafe to use this field. that it is unsafe to use this field.

View File

@ -2123,7 +2123,7 @@ Porting to Python 3.12
The use of ``tp_dictoffset`` and ``tp_weaklistoffset`` is still The use of ``tp_dictoffset`` and ``tp_weaklistoffset`` is still
supported, but does not fully support multiple inheritance supported, but does not fully support multiple inheritance
(:gh:`95589`), and performance may be worse. (:gh:`95589`), and performance may be worse.
Classes declaring :c:macro:`Py_TPFLAGS_MANAGED_DICT` should call Classes declaring :c:macro:`Py_TPFLAGS_MANAGED_DICT` must call
:c:func:`!_PyObject_VisitManagedDict` and :c:func:`!_PyObject_ClearManagedDict` :c:func:`!_PyObject_VisitManagedDict` and :c:func:`!_PyObject_ClearManagedDict`
to traverse and clear their instance's dictionaries. to traverse and clear their instance's dictionaries.
To clear weakrefs, call :c:func:`PyObject_ClearWeakRefs`, as before. To clear weakrefs, call :c:func:`PyObject_ClearWeakRefs`, as before.

View File

@ -995,6 +995,12 @@ New Features
references) now supports the :ref:`Limited API <limited-c-api>`. references) now supports the :ref:`Limited API <limited-c-api>`.
(Contributed by Victor Stinner in :gh:`108634`.) (Contributed by Victor Stinner in :gh:`108634`.)
* Add :c:func:`PyObject_VisitManagedDict` and
:c:func:`PyObject_ClearManagedDict` functions which must be called by the
traverse and clear functions of a type using
:c:macro:`Py_TPFLAGS_MANAGED_DICT` flag.
(Contributed by Victor Stinner in :gh:`107073`.)
Porting to Python 3.13 Porting to Python 3.13
---------------------- ----------------------

View File

@ -444,8 +444,8 @@ PyAPI_FUNC(int) _PyTrash_cond(PyObject *op, destructor dealloc);
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj); PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg); PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj); PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
#define TYPE_MAX_WATCHERS 8 #define TYPE_MAX_WATCHERS 8

View File

@ -0,0 +1,3 @@
Add :c:func:`PyObject_VisitManagedDict` and :c:func:`PyObject_ClearManagedDict`
functions which must be called by the traverse and clear functions of a type
using :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag. Patch by Victor Stinner.

View File

@ -816,7 +816,7 @@ FutureObj_clear(FutureObj *fut)
Py_CLEAR(fut->fut_source_tb); Py_CLEAR(fut->fut_source_tb);
Py_CLEAR(fut->fut_cancel_msg); Py_CLEAR(fut->fut_cancel_msg);
Py_CLEAR(fut->fut_cancelled_exc); Py_CLEAR(fut->fut_cancelled_exc);
_PyObject_ClearManagedDict((PyObject *)fut); PyObject_ClearManagedDict((PyObject *)fut);
return 0; return 0;
} }
@ -834,7 +834,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
Py_VISIT(fut->fut_source_tb); Py_VISIT(fut->fut_source_tb);
Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancel_msg);
Py_VISIT(fut->fut_cancelled_exc); Py_VISIT(fut->fut_cancelled_exc);
_PyObject_VisitManagedDict((PyObject *)fut, visit, arg); PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
return 0; return 0;
} }
@ -2181,7 +2181,7 @@ TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
Py_VISIT(fut->fut_source_tb); Py_VISIT(fut->fut_source_tb);
Py_VISIT(fut->fut_cancel_msg); Py_VISIT(fut->fut_cancel_msg);
Py_VISIT(fut->fut_cancelled_exc); Py_VISIT(fut->fut_cancelled_exc);
_PyObject_VisitManagedDict((PyObject *)fut, visit, arg); PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
return 0; return 0;
} }

View File

@ -805,13 +805,13 @@ static int
heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg) heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
{ {
Py_VISIT(Py_TYPE(self)); Py_VISIT(Py_TYPE(self));
return _PyObject_VisitManagedDict((PyObject *)self, visit, arg); return PyObject_VisitManagedDict((PyObject *)self, visit, arg);
} }
static int static int
heapmanaged_clear(HeapCTypeObject *self) heapmanaged_clear(HeapCTypeObject *self)
{ {
_PyObject_ClearManagedDict((PyObject *)self); PyObject_ClearManagedDict((PyObject *)self);
return 0; return 0;
} }
@ -819,7 +819,7 @@ static void
heapmanaged_dealloc(HeapCTypeObject *self) heapmanaged_dealloc(HeapCTypeObject *self)
{ {
PyTypeObject *tp = Py_TYPE(self); PyTypeObject *tp = Py_TYPE(self);
_PyObject_ClearManagedDict((PyObject *)self); PyObject_ClearManagedDict((PyObject *)self);
PyObject_GC_UnTrack(self); PyObject_GC_UnTrack(self);
PyObject_GC_Del(self); PyObject_GC_Del(self);
Py_DECREF(tp); Py_DECREF(tp);

View File

@ -2923,7 +2923,7 @@ settrace_to_error(PyObject *self, PyObject *list)
static PyObject * static PyObject *
clear_managed_dict(PyObject *self, PyObject *obj) clear_managed_dict(PyObject *self, PyObject *obj)
{ {
_PyObject_ClearManagedDict(obj); PyObject_ClearManagedDict(obj);
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -5649,7 +5649,7 @@ _PyObject_FreeInstanceAttributes(PyObject *self)
} }
int int
_PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
{ {
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
@ -5672,7 +5672,7 @@ _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
} }
void void
_PyObject_ClearManagedDict(PyObject *obj) PyObject_ClearManagedDict(PyObject *obj)
{ {
PyTypeObject *tp = Py_TYPE(obj); PyTypeObject *tp = Py_TYPE(obj);
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {

View File

@ -1835,7 +1835,7 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
assert(base->tp_dictoffset == 0); assert(base->tp_dictoffset == 0);
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
assert(type->tp_dictoffset == -1); assert(type->tp_dictoffset == -1);
int err = _PyObject_VisitManagedDict(self, visit, arg); int err = PyObject_VisitManagedDict(self, visit, arg);
if (err) { if (err) {
return err; return err;
} }
@ -1905,7 +1905,7 @@ subtype_clear(PyObject *self)
__dict__ slots (as in the case 'self.__dict__ is self'). */ __dict__ slots (as in the case 'self.__dict__ is self'). */
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) { if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) { if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
_PyObject_ClearManagedDict(self); PyObject_ClearManagedDict(self);
} }
} }
else if (type->tp_dictoffset != base->tp_dictoffset) { else if (type->tp_dictoffset != base->tp_dictoffset) {

View File

@ -200,7 +200,7 @@ typevar_dealloc(PyObject *self)
Py_XDECREF(tv->evaluate_bound); Py_XDECREF(tv->evaluate_bound);
Py_XDECREF(tv->constraints); Py_XDECREF(tv->constraints);
Py_XDECREF(tv->evaluate_constraints); Py_XDECREF(tv->evaluate_constraints);
_PyObject_ClearManagedDict(self); PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -216,7 +216,7 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg)
Py_VISIT(tv->evaluate_bound); Py_VISIT(tv->evaluate_bound);
Py_VISIT(tv->constraints); Py_VISIT(tv->constraints);
Py_VISIT(tv->evaluate_constraints); Py_VISIT(tv->evaluate_constraints);
_PyObject_VisitManagedDict(self, visit, arg); PyObject_VisitManagedDict(self, visit, arg);
return 0; return 0;
} }
@ -227,7 +227,7 @@ typevar_clear(typevarobject *self)
Py_CLEAR(self->evaluate_bound); Py_CLEAR(self->evaluate_bound);
Py_CLEAR(self->constraints); Py_CLEAR(self->constraints);
Py_CLEAR(self->evaluate_constraints); Py_CLEAR(self->evaluate_constraints);
_PyObject_ClearManagedDict((PyObject *)self); PyObject_ClearManagedDict((PyObject *)self);
return 0; return 0;
} }
@ -744,7 +744,7 @@ paramspec_dealloc(PyObject *self)
Py_DECREF(ps->name); Py_DECREF(ps->name);
Py_XDECREF(ps->bound); Py_XDECREF(ps->bound);
_PyObject_ClearManagedDict(self); PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -757,7 +757,7 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg)
Py_VISIT(Py_TYPE(self)); Py_VISIT(Py_TYPE(self));
paramspecobject *ps = (paramspecobject *)self; paramspecobject *ps = (paramspecobject *)self;
Py_VISIT(ps->bound); Py_VISIT(ps->bound);
_PyObject_VisitManagedDict(self, visit, arg); PyObject_VisitManagedDict(self, visit, arg);
return 0; return 0;
} }
@ -765,7 +765,7 @@ static int
paramspec_clear(paramspecobject *self) paramspec_clear(paramspecobject *self)
{ {
Py_CLEAR(self->bound); Py_CLEAR(self->bound);
_PyObject_ClearManagedDict((PyObject *)self); PyObject_ClearManagedDict((PyObject *)self);
return 0; return 0;
} }
@ -1026,7 +1026,7 @@ typevartuple_dealloc(PyObject *self)
typevartupleobject *tvt = (typevartupleobject *)self; typevartupleobject *tvt = (typevartupleobject *)self;
Py_DECREF(tvt->name); Py_DECREF(tvt->name);
_PyObject_ClearManagedDict(self); PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self); PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
@ -1165,14 +1165,14 @@ static int
typevartuple_traverse(PyObject *self, visitproc visit, void *arg) typevartuple_traverse(PyObject *self, visitproc visit, void *arg)
{ {
Py_VISIT(Py_TYPE(self)); Py_VISIT(Py_TYPE(self));
_PyObject_VisitManagedDict(self, visit, arg); PyObject_VisitManagedDict(self, visit, arg);
return 0; return 0;
} }
static int static int
typevartuple_clear(PyObject *self) typevartuple_clear(PyObject *self)
{ {
_PyObject_ClearManagedDict(self); PyObject_ClearManagedDict(self);
return 0; return 0;
} }