Skip to content

Commit 8eb1269

Browse files
committed
add generic implementation of a __dict__ descriptor for C types
1 parent 4a57846 commit 8eb1269

6 files changed

Lines changed: 64 additions & 19 deletions

File tree

Doc/c-api/object.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,18 @@ Object Protocol
101101
This is the equivalent of the Python statement ``del o.attr_name``.
102102
103103
104+
.. c:function:: PyObject* PyType_GenericGetDict(PyObject *o, void *context)
105+
106+
A generic implementation for the getter of a ``__dict__`` descriptor. It
107+
creates the dictionary if necessary.
108+
109+
110+
.. c:function:: int PyType_GenericSetDict(PyObject *o, void *context)
111+
112+
A generic implementation for the setter of a ``__dict__`` descriptor. This
113+
implementation does not allow the dictionary to be deleted.
114+
115+
104116
.. c:function:: PyObject* PyObject_RichCompare(PyObject *o1, PyObject *o2, int opid)
105117
106118
Compare the values of *o1* and *o2* using the operation specified by *opid*,

Doc/c-api/type.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ Type Objects
7777
7878
XXX: Document.
7979
80-
8180
.. c:function:: int PyType_Ready(PyTypeObject *type)
8281
8382
Finalize a type object. This should be called on all type objects to finish

Include/object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,8 @@ PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *);
516516
PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *);
517517
PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *,
518518
PyObject *, PyObject *);
519+
PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *);
520+
PyAPI_FUNC(int) PyObject_GenericSetDict(PyObject *, PyObject *, void *);
519521
PyAPI_FUNC(Py_hash_t) PyObject_Hash(PyObject *);
520522
PyAPI_FUNC(Py_hash_t) PyObject_HashNotImplemented(PyObject *);
521523
PyAPI_FUNC(int) PyObject_IsTrue(PyObject *);

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,6 +2253,10 @@ Tests
22532253
C-API
22542254
-----
22552255

2256+
- Add PyObject_GenericGetDict and PyObject_GeneriSetDict. They are generic
2257+
implementations for the getter and setter of a ``__dict__`` descriptor of C
2258+
types.
2259+
22562260
- Issue #13727: Add 3 macros to access PyDateTime_Delta members:
22572261
PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS,
22582262
PyDateTime_DELTA_GET_MICROSECONDS.

Objects/object.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,48 @@ PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value)
12101210
return _PyObject_GenericSetAttrWithDict(obj, name, value, NULL);
12111211
}
12121212

1213+
PyObject *
1214+
PyObject_GenericGetDict(PyObject *obj, void *context)
1215+
{
1216+
PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj);
1217+
if (dictptr == NULL) {
1218+
PyErr_SetString(PyExc_AttributeError,
1219+
"This object has no __dict__");
1220+
return NULL;
1221+
}
1222+
dict = *dictptr;
1223+
if (dict == NULL)
1224+
*dictptr = dict = PyDict_New();
1225+
Py_XINCREF(dict);
1226+
return dict;
1227+
}
1228+
1229+
int
1230+
PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
1231+
{
1232+
PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj);
1233+
if (dictptr == NULL) {
1234+
PyErr_SetString(PyExc_AttributeError,
1235+
"This object has no __dict__");
1236+
return -1;
1237+
}
1238+
if (value == NULL) {
1239+
PyErr_SetString(PyExc_TypeError, "cannot delete __dict__");
1240+
return -1;
1241+
}
1242+
if (!PyDict_Check(value)) {
1243+
PyErr_Format(PyExc_TypeError,
1244+
"__dict__ must be set to a dictionary, "
1245+
"not a '%.200s'", Py_TYPE(value)->tp_name);
1246+
return -1;
1247+
}
1248+
dict = *dictptr;
1249+
Py_XINCREF(value);
1250+
*dictptr = value;
1251+
Py_XDECREF(dict);
1252+
return 0;
1253+
}
1254+
12131255

12141256
/* Test a value used as condition, e.g., in a for or if statement.
12151257
Return -1 if an error occurred */

Objects/typeobject.c

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,8 +1759,6 @@ raise_dict_descr_error(PyObject *obj)
17591759
static PyObject *
17601760
subtype_dict(PyObject *obj, void *context)
17611761
{
1762-
PyObject **dictptr;
1763-
PyObject *dict;
17641762
PyTypeObject *base;
17651763

17661764
base = get_builtin_base_with_dict(Py_TYPE(obj));
@@ -1778,25 +1776,13 @@ subtype_dict(PyObject *obj, void *context)
17781776
}
17791777
return func(descr, obj, (PyObject *)(Py_TYPE(obj)));
17801778
}
1781-
1782-
dictptr = _PyObject_GetDictPtr(obj);
1783-
if (dictptr == NULL) {
1784-
PyErr_SetString(PyExc_AttributeError,
1785-
"This object has no __dict__");
1786-
return NULL;
1787-
}
1788-
dict = *dictptr;
1789-
if (dict == NULL)
1790-
*dictptr = dict = PyDict_New();
1791-
Py_XINCREF(dict);
1792-
return dict;
1779+
return PyObject_GenericGetDict(obj, context);
17931780
}
17941781

17951782
static int
17961783
subtype_setdict(PyObject *obj, PyObject *value, void *context)
17971784
{
1798-
PyObject **dictptr;
1799-
PyObject *dict;
1785+
PyObject *dict, **dictptr;
18001786
PyTypeObject *base;
18011787

18021788
base = get_builtin_base_with_dict(Py_TYPE(obj));
@@ -1814,14 +1800,14 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context)
18141800
}
18151801
return func(descr, obj, value);
18161802
}
1817-
1803+
/* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */
18181804
dictptr = _PyObject_GetDictPtr(obj);
18191805
if (dictptr == NULL) {
18201806
PyErr_SetString(PyExc_AttributeError,
18211807
"This object has no __dict__");
18221808
return -1;
18231809
}
1824-
if (value != NULL && !PyDict_Check(value)) {
1810+
if (!PyDict_Check(value)) {
18251811
PyErr_Format(PyExc_TypeError,
18261812
"__dict__ must be set to a dictionary, "
18271813
"not a '%.200s'", Py_TYPE(value)->tp_name);

0 commit comments

Comments
 (0)