Skip to content

Commit 27055d7

Browse files
authored
pythonGH-92678: Expose managed dict clear and visit functions (python#95246)
1 parent 2d26449 commit 27055d7

File tree

5 files changed

+57
-0
lines changed

5 files changed

+57
-0
lines changed

Include/cpython/dictobject.h

+3
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,6 @@ typedef struct {
8383

8484
PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *);
8585
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
86+
87+
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg);
88+
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *self);

Lib/test/test_capi.py

+14
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,20 @@ def test_export_symbols(self):
722722
with self.subTest(name=name):
723723
self.assertTrue(hasattr(ctypes.pythonapi, name))
724724

725+
def test_clear_managed_dict(self):
726+
727+
class C:
728+
def __init__(self):
729+
self.a = 1
730+
731+
c = C()
732+
_testcapi.clear_managed_dict(c)
733+
self.assertEqual(c.__dict__, {})
734+
c = C()
735+
self.assertEqual(c.__dict__, {'a':1})
736+
_testcapi.clear_managed_dict(c)
737+
self.assertEqual(c.__dict__, {})
738+
725739

726740
class TestPendingCalls(unittest.TestCase):
727741

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Adds unstable C-API functions ``_PyObject_VisitManagedDict`` and
2+
``_PyObject_ClearManagedDict`` to allow C extensions to allow the VM to
3+
manage their object's dictionaries.

Modules/_testcapimodule.c

+8
Original file line numberDiff line numberDiff line change
@@ -6009,6 +6009,13 @@ settrace_to_record(PyObject *self, PyObject *list)
60096009
Py_RETURN_NONE;
60106010
}
60116011

6012+
static PyObject *
6013+
clear_managed_dict(PyObject *self, PyObject *obj)
6014+
{
6015+
_PyObject_ClearManagedDict(obj);
6016+
Py_RETURN_NONE;
6017+
}
6018+
60126019

60136020
static PyObject *
60146021
test_macros(PyObject *self, PyObject *Py_UNUSED(args))
@@ -6347,6 +6354,7 @@ static PyMethodDef TestMethods[] = {
63476354
{"test_code_api", test_code_api, METH_NOARGS, NULL},
63486355
{"settrace_to_record", settrace_to_record, METH_O, NULL},
63496356
{"test_macros", test_macros, METH_NOARGS, NULL},
6357+
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
63506358
{NULL, NULL} /* sentinel */
63516359
};
63526360

Objects/dictobject.c

+29
Original file line numberDiff line numberDiff line change
@@ -5583,6 +5583,35 @@ _PyObject_FreeInstanceAttributes(PyObject *self)
55835583
free_values(*values_ptr);
55845584
}
55855585

5586+
int
5587+
_PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg)
5588+
{
5589+
PyTypeObject *tp = Py_TYPE(self);
5590+
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
5591+
return 0;
5592+
}
5593+
assert(tp->tp_dictoffset);
5594+
int err = _PyObject_VisitInstanceAttributes(self, visit, arg);
5595+
if (err) {
5596+
return err;
5597+
}
5598+
Py_VISIT(*_PyObject_ManagedDictPointer(self));
5599+
return 0;
5600+
}
5601+
5602+
5603+
void
5604+
_PyObject_ClearManagedDict(PyObject *self)
5605+
{
5606+
PyTypeObject *tp = Py_TYPE(self);
5607+
if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
5608+
return;
5609+
}
5610+
_PyObject_FreeInstanceAttributes(self);
5611+
*_PyObject_ValuesPointer(self) = NULL;
5612+
Py_CLEAR(*_PyObject_ManagedDictPointer(self));
5613+
}
5614+
55865615
PyObject *
55875616
PyObject_GenericGetDict(PyObject *obj, void *context)
55885617
{

0 commit comments

Comments
 (0)