8000 Add PyObject_VisitManagedDict() (#76) · python/pythoncapi-compat@a594354 · GitHub
[go: up one dir, main page]

Skip to content

Commit a594354

Browse files
authored
Add PyObject_VisitManagedDict() (#76)
Add PyObject_VisitManagedDict() and PyObject_ClearManagedDict() functions.
1 parent 671fb69 commit a594354

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

docs/api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ Python 3.13
9999
100100
See `PyLong_AsInt() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsInt>`__.
101101
102+
.. c:function:: int PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
103+
104+
See `PyObject_VisitManagedDict() documentation <https://docs.python.org/dev/c-api/object.html#c.PyObject_VisitManagedDict>`__.
105+
106+
.. c:function:: void PyObject_ClearManagedDict(PyObject *obj)
107+
108+
See `PyObject_ClearManagedDict() documentation <https://docs.python.org/dev/c-api/object.html#c.PyObject_ClearManagedDict>`__.
109+
102110
103111
Python 3.12
104112
-----------

docs/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Changelog
22
=========
33

4+
* 2023-10-03: Add ``PyObject_VisitManagedDict()`` and
5+
``PyObject_ClearManagedDict()`` functions.
46
* 2023-09-29: Add functions:
57

68
* ``PyMapping_HasKeyWithError()``

pythoncapi_compat.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,31 @@ static inline int PyLong_AsInt(PyObject *obj)
905905
#endif
906906

907907

908+
// gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1
909+
#if PY_VERSION_HEX < 0x030D00A1
910+
static inline int
911+
PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
912+
{
913+
PyObject **dict = _PyObject_GetDictPtr(obj);
914+
if (*dict == NULL) {
915+
return -1;
916+
}
917+
Py_VISIT(*dict);
918+
return 0;
919+
}
920+
921+
static inline void
922+
PyObject_ClearManagedDict(PyObject *obj)
923+
{
924+
PyObject **dict = _PyObject_GetDictPtr(obj);
925+
if (*dict == NULL) {
926+
return;
927+
}
928+
Py_CLEAR(*dict);
929+
}
930+
#endif
931+
932+
908933
#ifdef __cplusplus
909934
}
910935
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,85 @@ test_long_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
12771277
}
12781278

12791279

1280+
// --- HeapCTypeWithManagedDict --------------------------------------------
1281+
1282+
// Py_TPFLAGS_MANAGED_DICT was added to Python 3.11.0a3
1283+
#if PY_VERSION_HEX >= 0x030B00A3
1284+
# define TEST_MANAGED_DICT
1285+
1286+
typedef struct {
1287+
PyObject_HEAD
1288+
} HeapCTypeObject;
1289+
1290+
static int
1291+
heapmanaged_traverse(PyObject *self, visitproc visit, void *arg)
1292+
{
1293+
Py_VISIT(Py_TYPE(self));
1294+
return PyObject_VisitManagedDict(self, visit, arg);
1295+
}
1296+
1297+
static int
1298+
heapmanaged_clear(PyObject *self)
1299+
{
1300+
PyObject_ClearManagedDict(self);
1301+
return 0;
1302+
}
1303+
1304+
static void
1305+
heapmanaged_dealloc(HeapCTypeObject *self)
1306+
{
1307+
PyTypeObject *tp = Py_TYPE(self);
1308+
PyObject_ClearManagedDict((PyObject *)self);
1309+
PyObject_GC_UnTrack(self);
1310+
PyObject_GC_Del(self);
1311+
Py_DECREF(tp);
1312+
}
1313+
1314+
static PyType_Slot HeapCTypeWithManagedDict_slots[] = {
1315+
{Py_tp_traverse, _Py_CAST(void*, heapmanaged_traverse)},
1316+
{Py_tp_clear, _Py_CAST(void*, heapmanaged_clear)},
1317+
{Py_tp_dealloc, _Py_CAST(void*, heapmanaged_dealloc)},
1318+
{0, 0},
1319+
};
1320+
1321+
static PyType_Spec HeapCTypeWithManagedDict_spec = {
1322+
"test_pythoncapi_compat.HeapCTypeWithManagedDict",
1323+
sizeof(PyObject),
1324+
0,
1325+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT,
1326+
HeapCTypeWithManagedDict_slots
1327+
};
1328+
1329+
static PyObject *
1330+
test_managed_dict(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1331+
{
1332+
PyObject *type = PyType_FromSpec(&HeapCTypeWithManagedDict_spec);
1333+
if (type == NULL) {
1334+
return NULL;
1335+
}
1336+
1337+
PyObject *obj = PyObject_CallNoArgs(type);
1338+
if (obj == NULL) {
1339+
Py_DECREF(type);
1340+
return NULL;
1341+
}
1342+
1343+
// call heapmanaged_traverse()
1344+
PyGC_Collect();
1345+
1346+
// call heapmanaged_clear()
1347+
Py_DECREF(obj);
1348+
PyGC_Collect();
1349+
1350+
Py_DECREF(type);
1351+
// Just in case!
1352+
PyGC_Collect();
1353+
1354+
Py_RETURN_NONE;
1355+
}
1356+
#endif // PY_VERSION_HEX >= 0x030B00A3
1357+
1358+
12801359
static struct PyMethodDef methods[] = {
12811360
{"test_object", test_object, METH_NOARGS, _Py_NULL},
12821361
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1303,6 +1382,9 @@ static struct PyMethodDef methods[] = {
13031382
{"test_getitem", test_getitem, METH_NOARGS, _Py_NULL},
13041383
{"test_dict_api", test_dict_api, METH_NOARGS, _Py_NULL},
13051384
{"test_long_api", test_long_api, METH_NOARGS, _Py_NULL},
1385+
#ifdef TEST_MANAGED_DICT
1386+
{"test_managed_dict", test_managed_dict, METH_NOARGS, _Py_NULL},
1387+
#endif
13061388
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
13071389
};
13081390

0 commit comments

Comments
 (0)
0