Skip to content

Commit 7f73194

Browse files
[3.11] GH-92678: Expose managed dict clear and visit functions (GH-95246). (#95256)
Co-authored-by: Mark Shannon <[email protected]>
1 parent 4c10dba commit 7f73194

File tree

5 files changed

+58
-0
lines changed

5 files changed

+58
-0
lines changed

Include/cpython/dictobject.h

+3
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,6 @@ typedef struct {
7676

7777
PyAPI_FUNC(PyObject *) _PyDictView_New(PyObject *, PyTypeObject *);
7878
PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
79+
80+
PyAPI_FUNC(int) _PyObject_VisitManagedDict(PyObject *self, visitproc visit, void *arg);
81+
PyAPI_FUNC(void) _PyObject_ClearManagedDict(PyObject *self);

Lib/test/test_capi.py

+14
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,20 @@ def test_export_symbols(self):
705705
with self.subTest(name=name):
706706
self.assertTrue(hasattr(ctypes.pythonapi, name))
707707

708+
def test_clear_managed_dict(self):
709+
710+
class C:
711+
def __init__(self):
712+
self.a = 1
713+
714+
c = C()
715+
_testcapi.clear_managed_dict(c)
716+
self.assertEqual(c.__dict__, {})
717+
c = C()
718+
self.assertEqual(c.__dict__, {'a':1})
719+
_testcapi.clear_managed_dict(c)
720+
self.assertEqual(c.__dict__, {})
721+
708722

709723
class TestPendingCalls(unittest.TestCase):
710724

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

+9
Original file line numberDiff line numberDiff line change
@@ -6014,6 +6014,14 @@ settrace_to_record(PyObject *self, PyObject *list)
60146014
Py_RETURN_NONE;
60156015
}
60166016

6017+
static PyObject *
6018+
clear_managed_dict(PyObject *self, PyObject *obj)
6019+
{
6020+
_PyObject_ClearManagedDict(obj);
6021+
Py_RETURN_NONE;
6022+
}
6023+
6024+
60176025
static PyObject *negative_dictoffset(PyObject *, PyObject *);
60186026
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
60196027
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
@@ -6315,6 +6323,7 @@ static PyMethodDef TestMethods[] = {
63156323
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
63166324
{"test_code_api", test_code_api, METH_NOARGS, NULL},
63176325
{"settrace_to_record", settrace_to_record, METH_O, NULL},
6326+
{"clear_managed_dict", clear_managed_dict, METH_O, NULL},
63186327
{NULL, NULL} /* sentinel */
63196328
};
63206329

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)