_Py_ANNOTATE_FORMAT_STRING = 4,
};
+int _PyObject_SetDict(PyObject *obj, PyObject *value);
+
#ifdef __cplusplus
}
#endif
ManualHeapType = _testcapi.ManualHeapType
for i in range(100):
self.assertIsInstance(ManualHeapType(), ManualHeapType)
+
+ def test_extension_managed_dict_type(self):
+ ManagedDictType = _testcapi.ManagedDictType
+ obj = ManagedDictType()
+ obj.foo = 42
+ self.assertEqual(obj.foo, 42)
+ self.assertEqual(obj.__dict__, {'foo': 42})
+ obj.__dict__ = {'bar': 3}
+ self.assertEqual(obj.__dict__, {'bar': 3})
+ self.assertEqual(obj.bar, 3)
--- /dev/null
+Fix the C API function ``PyObject_GenericSetDict`` to handle extension
+classes with inline values.
return (PyObject *)type;
}
+typedef struct {
+ PyObject_VAR_HEAD
+} ManagedDictObject;
+
+int ManagedDict_traverse(PyObject *self, visitproc visit, void *arg) {
+ PyObject_VisitManagedDict(self, visit, arg);
+ Py_VISIT(Py_TYPE(self));
+ return 0;
+}
+
+int ManagedDict_clear(PyObject *self) {
+ PyObject_ClearManagedDict(self);
+ return 0;
+}
+
+static PyGetSetDef ManagedDict_getset[] = {
+ {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict, NULL, NULL},
+ {NULL, NULL, NULL, NULL, NULL},
+};
+
+static PyType_Slot ManagedDict_slots[] = {
+ {Py_tp_new, (void *)PyType_GenericNew},
+ {Py_tp_getset, (void *)ManagedDict_getset},
+ {Py_tp_traverse, (void *)ManagedDict_traverse},
+ {Py_tp_clear, (void *)ManagedDict_clear},
+ {0}
+};
+
+static PyType_Spec ManagedDict_spec = {
+ "_testcapi.ManagedDictType",
+ sizeof(ManagedDictObject),
+ 0, // itemsize
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_MANAGED_DICT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_GC,
+ ManagedDict_slots
+};
+
+static PyObject *
+create_managed_dict_type(void)
+{
+ return PyType_FromSpec(&ManagedDict_spec);
+}
+
static struct PyModuleDef _testcapimodule = {
PyModuleDef_HEAD_INIT,
.m_name = "_testcapi",
return NULL;
}
+ PyObject *managed_dict_type = create_managed_dict_type();
+ if (managed_dict_type == NULL) {
+ return NULL;
+ }
+ if (PyModule_Add(m, "ManagedDictType", managed_dict_type) < 0) {
+ return NULL;
+ }
/* Include tests from the _testcapi/ directory */
if (_PyTestCapi_Init_Vectorcall(m) < 0) {
int
PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
{
- PyObject **dictptr = _PyObject_GetDictPtr(obj);
- if (dictptr == NULL) {
- if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) &&
- _PyObject_GetManagedDict(obj) == NULL
- ) {
- /* Was unable to convert to dict */
- PyErr_NoMemory();
- }
- else {
- PyErr_SetString(PyExc_AttributeError,
- "This object has no __dict__");
- }
- return -1;
- }
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "cannot delete __dict__");
return -1;
}
- if (!PyDict_Check(value)) {
- PyErr_Format(PyExc_TypeError,
- "__dict__ must be set to a dictionary, "
- "not a '%.200s'", Py_TYPE(value)->tp_name);
- return -1;
- }
- Py_BEGIN_CRITICAL_SECTION(obj);
- PyObject *olddict = *dictptr;
- FT_ATOMIC_STORE_PTR_RELEASE(*dictptr, Py_NewRef(value));
-#ifdef Py_GIL_DISABLED
- _PyObject_XDecRefDelayed(olddict);
-#else
- Py_XDECREF(olddict);
-#endif
- Py_END_CRITICAL_SECTION();
- return 0;
+ return _PyObject_SetDict(obj, value);
}
return PyObject_GenericGetDict(obj, context);
}
+int
+_PyObject_SetDict(PyObject *obj, PyObject *value)
+{
+ if (value != NULL && !PyDict_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "__dict__ must be set to a dictionary, "
+ "not a '%.200s'", Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
+ return _PyObject_SetManagedDict(obj, value);
+ }
+ PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
+ if (dictptr == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "This object has no __dict__");
+ return -1;
+ }
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ PyObject *olddict = *dictptr;
+ FT_ATOMIC_STORE_PTR_RELEASE(*dictptr, Py_NewRef(value));
+#ifdef Py_GIL_DISABLED
+ _PyObject_XDecRefDelayed(olddict);
+#else
+ Py_XDECREF(olddict);
+#endif
+ Py_END_CRITICAL_SECTION();
+ return 0;
+}
+
static int
subtype_setdict(PyObject *obj, PyObject *value, void *context)
{
- PyObject **dictptr;
PyTypeObject *base;
base = get_builtin_base_with_dict(Py_TYPE(obj));
}
return func(descr, obj, value);
}
- /* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */
- if (value != NULL && !PyDict_Check(value)) {
- PyErr_Format(PyExc_TypeError,
- "__dict__ must be set to a dictionary, "
- "not a '%.200s'", Py_TYPE(value)->tp_name);
- return -1;
- }
-
- if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
- return _PyObject_SetManagedDict(obj, value);
- }
- else {
- dictptr = _PyObject_ComputedDictPointer(obj);
- if (dictptr == NULL) {
- PyErr_SetString(PyExc_AttributeError,
- "This object has no __dict__");
- return -1;
- }
- Py_CLEAR(*dictptr);
- *dictptr = Py_XNewRef(value);
- }
- return 0;
+ return _PyObject_SetDict(obj, value);
}
static PyObject *