From 00e9886bd90490e1afc729a1abac384ba70fa6a1 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 7 Mar 2013 22:16:29 -0500 Subject: [PATCH] Add PyDict_SetDefault. (closes #17327) Patch by Stefan Behnel and I. --- Doc/c-api/dict.rst | 9 +++++++++ Doc/data/refcounts.dat | 5 +++++ Include/dictobject.h | 2 ++ Misc/NEWS | 2 ++ Objects/dictobject.c | 34 +++++++++++++++++++++++----------- 5 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Doc/c-api/dict.rst b/Doc/c-api/dict.rst index 6bacc326824..dc8ef53c264 100644 --- a/Doc/c-api/dict.rst +++ b/Doc/c-api/dict.rst @@ -110,6 +110,15 @@ Dictionary Objects :c:type:`char\*`, rather than a :c:type:`PyObject\*`. +.. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *default) + + This is the same the Python-level :meth:`dict.setdefault`. If present, it + returns the value corresponding to *key* from the dictionary *p*. If the key + is not in the dict, it is inserted with value *defaultobj* and *defaultobj* + is inserted. This function evaluates the hash function of *key* only once, + instead of evaluating it independently for the lookup and the insertion. + + .. c:function:: PyObject* PyDict_Items(PyObject *p) Return a :c:type:`PyListObject` containing all the items from the dictionary. diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index fac89afbd67..e2990503820 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -220,6 +220,11 @@ PyDict_GetItemString:PyObject*::0: PyDict_GetItemString:PyObject*:p:0: PyDict_GetItemString:char*:key:: +PyDict_SetDefault:PyObject*::0: +PyDict_SetDefault:PyObject*:p:0: +PyDict_SetDefault:PyObject*:key:0:conditionally +1 if inserted into the dict +PyDict_SetDefault:PyObject*:default:0:conditionally +1 if inserted into the dict + PyDict_Items:PyObject*::+1: PyDict_Items:PyObject*:p:0: diff --git a/Include/dictobject.h b/Include/dictobject.h index d89aac89321..16bd1d6cb1f 100644 --- a/Include/dictobject.h +++ b/Include/dictobject.h @@ -53,6 +53,8 @@ PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key); PyAPI_FUNC(PyObject *) PyDict_GetItemWithError(PyObject *mp, PyObject *key); PyAPI_FUNC(PyObject *) _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key); +PyAPI_FUNC(PyObject *) PyDict_SetDefault( + PyObject *mp, PyObject *key, PyObject *defaultobj); PyAPI_FUNC(int) PyDict_SetItem(PyObject *mp, PyObject *key, PyObject *item); PyAPI_FUNC(int) PyDict_DelItem(PyObject *mp, PyObject *key); PyAPI_FUNC(void) PyDict_Clear(PyObject *mp); diff --git a/Misc/NEWS b/Misc/NEWS index f15165147eb..17282af6b78 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,8 @@ What's New in Python 3.4.0 Alpha 1? Core and Builtins ----------------- +- Issue #17327: Add PyDict_SetDefault. + - Issue #17032: The "global" in the "NameError: global name 'x' is not defined" error message has been removed. Patch by Ram Rachum. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 9080ddfd288..2e8679fba4e 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2211,19 +2211,19 @@ dict_get(register PyDictObject *mp, PyObject *args) return val; } -static PyObject * -dict_setdefault(register PyDictObject *mp, PyObject *args) +PyObject * +PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { - PyObject *key; - PyObject *failobj = Py_None; + PyDictObject *mp = (PyDictObject *)d; PyObject *val = NULL; Py_hash_t hash; PyDictKeyEntry *ep; PyObject **value_addr; - if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &failobj)) + if (!PyDict_Check(d)) { + PyErr_BadInternalCall(); return NULL; - + } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); @@ -2241,20 +2241,32 @@ dict_setdefault(register PyDictObject *mp, PyObject *args) return NULL; ep = find_empty_slot(mp, key, hash, &value_addr); } - Py_INCREF(failobj); + Py_INCREF(defaultobj); Py_INCREF(key); - MAINTAIN_TRACKING(mp, key, failobj); + MAINTAIN_TRACKING(mp, key, defaultobj); ep->me_key = key; ep->me_hash = hash; - *value_addr = failobj; - val = failobj; + *value_addr = defaultobj; + val = defaultobj; mp->ma_keys->dk_usable--; mp->ma_used++; } - Py_INCREF(val); return val; } +static PyObject * +dict_setdefault(PyDictObject *mp, PyObject *args) +{ + PyObject *key, *val; + PyObject *defaultobj = Py_None; + + if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &defaultobj)) + return NULL; + + val = PyDict_SetDefault(mp, key, defaultobj); + Py_XINCREF(val); + return val; +} static PyObject * dict_clear(register PyDictObject *mp)