Skip to content

Commit fc2cb86

Browse files
authored
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.
1 parent 6387b53 commit fc2cb86

File tree

12 files changed

+77
-24
lines changed

12 files changed

+77
-24
lines changed

Diff for: Doc/c-api/object.rst

+18
Original file line numberDiff line numberDiff line change
@@ -489,3 +489,21 @@ Object Protocol
489489
:c:macro:`Py_TPFLAGS_ITEMS_AT_END` set.
490490
491491
.. versionadded:: 3.12
492+
493+
.. c:function:: int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
494+
495+
Visit the managed dictionary of *obj*.
496+
497+
This function must only be called in a traverse function of the type which
498+
has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set.
499+
500+
.. versionadded:: 3.13
501+
502+
.. c:function:: void PyObject_ClearManagedDict(PyObject *obj)
503+
504+
Clear the managed dictionary of *obj*.
505+
506+
This function must only be called in a traverse function of the type which
507+
has the :c:macro:`Py_TPFLAGS_MANAGED_DICT` flag set.
508+
509+
.. versionadded:: 3.13

Diff for: Doc/c-api/typeobj.rst

+27-1
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
11311131

11321132
If this flag is set, :c:macro:`Py_TPFLAGS_HAVE_GC` should also be set.
11331133

1134+
The type traverse function must call :c:func:`PyObject_VisitManagedDict`
1135+
and its clear function must call :c:func:`PyObject_ClearManagedDict`.
1136+
11341137
.. versionadded:: 3.12
11351138

11361139
**Inheritance:**
@@ -1368,6 +1371,23 @@ and :c:data:`PyType_Type` effectively act as defaults.)
13681371
debugging aid you may want to visit it anyway just so the :mod:`gc` module's
13691372
:func:`~gc.get_referents` function will include it.
13701373

1374+
Heap types (:c:macro:`Py_TPFLAGS_HEAPTYPE`) must visit their type with::
1375+
1376+
Py_VISIT(Py_TYPE(self));
1377+
1378+
It is only needed since Python 3.9. To support Python 3.8 and older, this
1379+
line must be conditionnal::
1380+
1381+
#if PY_VERSION_HEX >= 0x03090000
1382+
Py_VISIT(Py_TYPE(self));
1383+
#endif
1384+
1385+
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
1386+
:c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
1387+
:c:func:`PyObject_VisitManagedDict` like this::
1388+
1389+
PyObject_VisitManagedDict((PyObject*)self, visit, arg);
1390+
13711391
.. warning::
13721392
When implementing :c:member:`~PyTypeObject.tp_traverse`, only the
13731393
members that the instance *owns* (by having :term:`strong references
@@ -1451,6 +1471,12 @@ and :c:data:`PyType_Type` effectively act as defaults.)
14511471
so that *self* knows the contained object can no longer be used. The
14521472
:c:func:`Py_CLEAR` macro performs the operations in a safe order.
14531473

1474+
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
1475+
:c:member:`~PyTypeObject.tp_flags` field, the traverse function must call
1476+
:c:func:`PyObject_ClearManagedDict` like this::
1477+
1478+
PyObject_ClearManagedDict((PyObject*)self);
1479+
14541480
Note that :c:member:`~PyTypeObject.tp_clear` is not *always* called
14551481
before an instance is deallocated. For example, when reference counting
14561482
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.)
18011827
field is ``NULL`` then no :attr:`~object.__dict__` gets created for instances.
18021828

18031829
If the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit is set in the
1804-
:c:member:`~PyTypeObject.tp_dict` field, then
1830+
:c:member:`~PyTypeObject.tp_flags` field, then
18051831
:c:member:`~PyTypeObject.tp_dictoffset` will be set to ``-1``, to indicate
18061832
that it is unsafe to use this field.
18071833

Diff for: Doc/whatsnew/3.12.rst

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

Diff for: Doc/whatsnew/3.13.rst

+6
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,12 @@ New Features
995995
references) now supports the :ref:`Limited API <limited-c-api>`.
996996
(Contributed by Victor Stinner in :gh:`108634`.)
997997

998+
* Add :c:func:`PyObject_VisitManagedDict` and
999+
:c:func:`PyObject_ClearManagedDict` functions which must be called by the
1000+
traverse and clear functions of a type using
1001+
:c:macro:`Py_TPFLAGS_MANAGED_DICT` flag.
1002+
(Contributed by Victor Stinner in :gh:`107073`.)
1003+
9981004
Porting to Python 3.13
9991005
----------------------
10001006

Diff for: Include/cpython/object.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,8 @@ PyAPI_FUNC(int) _PyTrash_cond(PyObject *op, destructor dealloc);
444444

445445
PyAPI_FUNC(void *) PyObject_GetItemData(PyObject *obj);
446446

447-
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
448-
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *obj);
447+
PyAPI_FUNC(int) PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg);
448+
PyAPI_FUNC(void) PyObject_ClearManagedDict(PyObject *obj);
449449

450450
#define TYPE_MAX_WATCHERS 8
451451

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

Diff for: Modules/_asynciomodule.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ FutureObj_clear(FutureObj *fut)
816816
Py_CLEAR(fut->fut_source_tb);
817817
Py_CLEAR(fut->fut_cancel_msg);
818818
Py_CLEAR(fut->fut_cancelled_exc);
819-
_PyObject_ClearManagedDict((PyObject *)fut);
819+
PyObject_ClearManagedDict((PyObject *)fut);
820820
return 0;
821821
}
822822

@@ -834,7 +834,7 @@ FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
834834
Py_VISIT(fut->fut_source_tb);
835835
Py_VISIT(fut->fut_cancel_msg);
836836
Py_VISIT(fut->fut_cancelled_exc);
837-
_PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
837+
PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
838838
return 0;
839839
}
840840

@@ -2181,7 +2181,7 @@ TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
21812181
Py_VISIT(fut->fut_source_tb);
21822182
Py_VISIT(fut->fut_cancel_msg);
21832183
Py_VISIT(fut->fut_cancelled_exc);
2184-
_PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
2184+
PyObject_VisitManagedDict((PyObject *)fut, visit, arg);
21852185
return 0;
21862186
}
21872187

Diff for: Modules/_testcapi/heaptype.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -805,21 +805,21 @@ static int
805805
heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
806806
{
807807
Py_VISIT(Py_TYPE(self));
808-
return _PyObject_VisitManagedDict((PyObject *)self, visit, arg);
808+
return PyObject_VisitManagedDict((PyObject *)self, visit, arg);
809809
}
810810

811811
static int
812812
heapmanaged_clear(HeapCTypeObject *self)
813813
{
814-
_PyObject_ClearManagedDict((PyObject *)self);
814+
PyObject_ClearManagedDict((PyObject *)self);
815815
return 0;
816816
}
817817

818818
static void
819819
heapmanaged_dealloc(HeapCTypeObject *self)
820820
{
821821
PyTypeObject *tp = Py_TYPE(self);
822-
_PyObject_ClearManagedDict((PyObject *)self);
822+
PyObject_ClearManagedDict((PyObject *)self);
823823
PyObject_GC_UnTrack(self);
824824
PyObject_GC_Del(self);
825825
Py_DECREF(tp);

Diff for: Modules/_testcapimodule.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -2923,7 +2923,7 @@ settrace_to_error(PyObject *self, PyObject *list)
29232923
static PyObject *
29242924
clear_managed_dict(PyObject *self, PyObject *obj)
29252925
{
2926-
_PyObject_ClearManagedDict(obj);
2926+
PyObject_ClearManagedDict(obj);
29272927
Py_RETURN_NONE;
29282928
}
29292929

Diff for: Objects/dictobject.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -5649,7 +5649,7 @@ _PyObject_FreeInstanceAttributes(PyObject *self)
56495649
}
56505650

56515651
int
5652-
_PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
5652+
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
56535653
{
56545654
PyTypeObject *tp = Py_TYPE(obj);
56555655
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
@@ -5672,7 +5672,7 @@ _PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
56725672
}
56735673

56745674
void
5675-
_PyObject_ClearManagedDict(PyObject *obj)
5675+
PyObject_ClearManagedDict(PyObject *obj)
56765676
{
56775677
PyTypeObject *tp = Py_TYPE(obj);
56785678
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {

Diff for: Objects/typeobject.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -1835,7 +1835,7 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
18351835
assert(base->tp_dictoffset == 0);
18361836
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
18371837
assert(type->tp_dictoffset == -1);
1838-
int err = _PyObject_VisitManagedDict(self, visit, arg);
1838+
int err = PyObject_VisitManagedDict(self, visit, arg);
18391839
if (err) {
18401840
return err;
18411841
}
@@ -1905,7 +1905,7 @@ subtype_clear(PyObject *self)
19051905
__dict__ slots (as in the case 'self.__dict__ is self'). */
19061906
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
19071907
if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
1908-
_PyObject_ClearManagedDict(self);
1908+
PyObject_ClearManagedDict(self);
19091909
}
19101910
}
19111911
else if (type->tp_dictoffset != base->tp_dictoffset) {

Diff for: Objects/typevarobject.c

+9-9
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ typevar_dealloc(PyObject *self)
200200
Py_XDECREF(tv->evaluate_bound);
201201
Py_XDECREF(tv->constraints);
202202
Py_XDECREF(tv->evaluate_constraints);
203-
_PyObject_ClearManagedDict(self);
203+
PyObject_ClearManagedDict(self);
204204
PyObject_ClearWeakRefs(self);
205205

206206
Py_TYPE(self)->tp_free(self);
@@ -216,7 +216,7 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg)
216216
Py_VISIT(tv->evaluate_bound);
217217
Py_VISIT(tv->constraints);
218218
Py_VISIT(tv->evaluate_constraints);
219-
_PyObject_VisitManagedDict(self, visit, arg);
219+
PyObject_VisitManagedDict(self, visit, arg);
220220
return 0;
221221
}
222222

@@ -227,7 +227,7 @@ typevar_clear(typevarobject *self)
227227
Py_CLEAR(self->evaluate_bound);
228228
Py_CLEAR(self->constraints);
229229
Py_CLEAR(self->evaluate_constraints);
230-
_PyObject_ClearManagedDict((PyObject *)self);
230+
PyObject_ClearManagedDict((PyObject *)self);
231231
return 0;
232232
}
233233

@@ -744,7 +744,7 @@ paramspec_dealloc(PyObject *self)
744744

745745
Py_DECREF(ps->name);
746746
Py_XDECREF(ps->bound);
747-
_PyObject_ClearManagedDict(self);
747+
PyObject_ClearManagedDict(self);
748748
PyObject_ClearWeakRefs(self);
749749

750750
Py_TYPE(self)->tp_free(self);
@@ -757,15 +757,15 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg)
757757
Py_VISIT(Py_TYPE(self));
758758
paramspecobject *ps = (paramspecobject *)self;
759759
Py_VISIT(ps->bound);
760-
_PyObject_VisitManagedDict(self, visit, arg);
760+
PyObject_VisitManagedDict(self, visit, arg);
761761
return 0;
762762
}
763763

764764
static int
765765
paramspec_clear(paramspecobject *self)
766766
{
767767
Py_CLEAR(self->bound);
768-
_PyObject_ClearManagedDict((PyObject *)self);
768+
PyObject_ClearManagedDict((PyObject *)self);
769769
return 0;
770770
}
771771

@@ -1026,7 +1026,7 @@ typevartuple_dealloc(PyObject *self)
10261026
typevartupleobject *tvt = (typevartupleobject *)self;
10271027

10281028
Py_DECREF(tvt->name);
1029-
_PyObject_ClearManagedDict(self);
1029+
PyObject_ClearManagedDict(self);
10301030
PyObject_ClearWeakRefs(self);
10311031

10321032
Py_TYPE(self)->tp_free(self);
@@ -1165,14 +1165,14 @@ static int
11651165
typevartuple_traverse(PyObject *self, visitproc visit, void *arg)
11661166
{
11671167
Py_VISIT(Py_TYPE(self));
1168-
_PyObject_VisitManagedDict(self, visit, arg);
1168+
PyObject_VisitManagedDict(self, visit, arg);
11691169
return 0;
11701170
}
11711171

11721172
static int
11731173
typevartuple_clear(PyObject *self)
11741174
{
1175-
_PyObject_ClearManagedDict(self);
1175+
PyObject_ClearManagedDict(self);
11761176
return 0;
11771177
}
11781178

0 commit comments

Comments
 (0)