From 8eb1269c346fa860acce9459c0bed065ffccd3ce Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 19 Feb 2012 19:59:10 -0500 Subject: [PATCH] add generic implementation of a __dict__ descriptor for C types --- Doc/c-api/object.rst | 12 ++++++++++++ Doc/c-api/type.rst | 1 - Include/object.h | 2 ++ Misc/NEWS | 4 ++++ Objects/object.c | 42 ++++++++++++++++++++++++++++++++++++++++++ Objects/typeobject.c | 22 ++++------------------ 6 files changed, 64 insertions(+), 19 deletions(-) diff --git a/Doc/c-api/object.rst b/Doc/c-api/object.rst index 88ba5ac0c4e..43768f3959e 100644 --- a/Doc/c-api/object.rst +++ b/Doc/c-api/object.rst @@ -101,6 +101,18 @@ Object Protocol This is the equivalent of the Python statement ``del o.attr_name``. +.. c:function:: PyObject* PyType_GenericGetDict(PyObject *o, void *context) + + A generic implementation for the getter of a ``__dict__`` descriptor. It + creates the dictionary if necessary. + + +.. c:function:: int PyType_GenericSetDict(PyObject *o, void *context) + + A generic implementation for the setter of a ``__dict__`` descriptor. This + implementation does not allow the dictionary to be deleted. + + .. c:function:: PyObject* PyObject_RichCompare(PyObject *o1, PyObject *o2, int opid) Compare the values of *o1* and *o2* using the operation specified by *opid*, diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index b3386eaa8b5..f8e01c06871 100644 --- a/Doc/c-api/type.rst +++ b/Doc/c-api/type.rst @@ -77,7 +77,6 @@ Type Objects XXX: Document. - .. c:function:: int PyType_Ready(PyTypeObject *type) Finalize a type object. This should be called on all type objects to finish diff --git a/Include/object.h b/Include/object.h index 6907a4464c2..844ff9f14a8 100644 --- a/Include/object.h +++ b/Include/object.h @@ -516,6 +516,8 @@ PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *); PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); +PyAPI_FUNC(PyObject *) PyObject_GenericGetDict(PyObject *, void *); +PyAPI_FUNC(int) PyObject_GenericSetDict(PyObject *, PyObject *, void *); PyAPI_FUNC(Py_hash_t) PyObject_Hash(PyObject *); PyAPI_FUNC(Py_hash_t) PyObject_HashNotImplemented(PyObject *); PyAPI_FUNC(int) PyObject_IsTrue(PyObject *); diff --git a/Misc/NEWS b/Misc/NEWS index 5c24b99c8c1..fbeb1770ffe 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -2253,6 +2253,10 @@ Tests C-API ----- +- Add PyObject_GenericGetDict and PyObject_GeneriSetDict. They are generic + implementations for the getter and setter of a ``__dict__`` descriptor of C + types. + - Issue #13727: Add 3 macros to access PyDateTime_Delta members: PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS. diff --git a/Objects/object.c b/Objects/object.c index 86f5e1b7a4b..81348258a4a 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -1210,6 +1210,48 @@ PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) return _PyObject_GenericSetAttrWithDict(obj, name, value, NULL); } +PyObject * +PyObject_GenericGetDict(PyObject *obj, void *context) +{ + PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return NULL; + } + dict = *dictptr; + if (dict == NULL) + *dictptr = dict = PyDict_New(); + Py_XINCREF(dict); + return dict; +} + +int +PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context) +{ + PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); + if (dictptr == NULL) { + 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; + } + dict = *dictptr; + Py_XINCREF(value); + *dictptr = value; + Py_XDECREF(dict); + return 0; +} + /* Test a value used as condition, e.g., in a for or if statement. Return -1 if an error occurred */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 495cc6dee20..a5abcce4a08 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1759,8 +1759,6 @@ raise_dict_descr_error(PyObject *obj) static PyObject * subtype_dict(PyObject *obj, void *context) { - PyObject **dictptr; - PyObject *dict; PyTypeObject *base; base = get_builtin_base_with_dict(Py_TYPE(obj)); @@ -1778,25 +1776,13 @@ subtype_dict(PyObject *obj, void *context) } return func(descr, obj, (PyObject *)(Py_TYPE(obj))); } - - dictptr = _PyObject_GetDictPtr(obj); - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return NULL; - } - dict = *dictptr; - if (dict == NULL) - *dictptr = dict = PyDict_New(); - Py_XINCREF(dict); - return dict; + return PyObject_GenericGetDict(obj, context); } static int subtype_setdict(PyObject *obj, PyObject *value, void *context) { - PyObject **dictptr; - PyObject *dict; + PyObject *dict, **dictptr; PyTypeObject *base; base = get_builtin_base_with_dict(Py_TYPE(obj)); @@ -1814,14 +1800,14 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context) } return func(descr, obj, value); } - + /* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */ dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return -1; } - if (value != NULL && !PyDict_Check(value)) { + if (!PyDict_Check(value)) { PyErr_Format(PyExc_TypeError, "__dict__ must be set to a dictionary, " "not a '%.200s'", Py_TYPE(value)->tp_name);