gh-111262: Add PyDict_Pop() function (#112028)

_PyDict_Pop_KnownHash(): remove the default value and the return type
becomes an int.

Co-authored-by: Stefan Behnel <stefan_ml@behnel.de>
Co-authored-by: Antoine Pitrou <pitrou@free.fr>
This commit is contained in:
Victor Stinner 2023-11-14 13:51:00 +01:00 committed by GitHub
parent f44d6ff6e0
commit 4f04172c92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 338 additions and 76 deletions

View file

@ -2226,64 +2226,119 @@ PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
return _PyDict_Next(op, ppos, pkey, pvalue, NULL);
}
/* Internal version of dict.pop(). */
PyObject *
_PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt)
{
Py_ssize_t ix;
PyObject *old_value;
PyDictObject *mp;
PyInterpreterState *interp = _PyInterpreterState_GET();
assert(PyDict_Check(dict));
mp = (PyDictObject *)dict;
/* Internal version of dict.pop(). */
int
_PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
PyObject **result)
{
assert(PyDict_Check(mp));
if (mp->ma_used == 0) {
if (deflt) {
return Py_NewRef(deflt);
if (result) {
*result = NULL;
}
_PyErr_SetKeyError(key);
return NULL;
return 0;
}
ix = _Py_dict_lookup(mp, key, hash, &old_value);
if (ix == DKIX_ERROR)
return NULL;
PyObject *old_value;
Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
if (ix == DKIX_ERROR) {
if (result) {
*result = NULL;
}
return -1;
}
if (ix == DKIX_EMPTY || old_value == NULL) {
if (deflt) {
return Py_NewRef(deflt);
if (result) {
*result = NULL;
}
_PyErr_SetKeyError(key);
return NULL;
return 0;
}
assert(old_value != NULL);
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
ASSERT_CONSISTENT(mp);
return old_value;
if (result) {
*result = old_value;
}
else {
Py_DECREF(old_value);
}
return 1;
}
PyObject *
_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt)
{
Py_hash_t hash;
if (((PyDictObject *)dict)->ma_used == 0) {
if (deflt) {
return Py_NewRef(deflt);
int
PyDict_Pop(PyObject *op, PyObject *key, PyObject **result)
{
if (!PyDict_Check(op)) {
if (result) {
*result = NULL;
}
PyErr_BadInternalCall();
return -1;
}
PyDictObject *dict = (PyDictObject *)op;
if (dict->ma_used == 0) {
if (result) {
*result = NULL;
}
return 0;
}
Py_hash_t hash;
if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
hash = PyObject_Hash(key);
if (hash == -1) {
if (result) {
*result = NULL;
}
return -1;
}
}
return _PyDict_Pop_KnownHash(dict, key, hash, result);
}
int
PyDict_PopString(PyObject *op, const char *key, PyObject **result)
{
PyObject *key_obj = PyUnicode_FromString(key);
if (key_obj == NULL) {
if (result != NULL) {
*result = NULL;
}
return -1;
}
int res = PyDict_Pop(op, key_obj, result);
Py_DECREF(key_obj);
return res;
}
PyObject *
_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value)
{
PyObject *result;
if (PyDict_Pop(dict, key, &result) == 0) {
if (default_value != NULL) {
return Py_NewRef(default_value);
}
_PyErr_SetKeyError(key);
return NULL;
}
if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
hash = PyObject_Hash(key);
if (hash == -1)
return NULL;
}
return _PyDict_Pop_KnownHash(dict, key, hash, deflt);
return result;
}
/* Internal version of dict.from_keys(). It is subclass-friendly. */
PyObject *
_PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)

View file

@ -1049,7 +1049,10 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
return NULL;
}
/* Now delete the value from the dict. */
value = _PyDict_Pop_KnownHash(od, key, hash, failobj);
if (_PyDict_Pop_KnownHash((PyDictObject *)od, key, hash,
&value) == 0) {
value = Py_NewRef(failobj);
}
}
else if (value == NULL && !PyErr_Occurred()) {
/* Apply the fallback value, if necessary. */

View file

@ -8,7 +8,6 @@
*/
#include "Python.h"
#include "pycore_dict.h" // _PyDict_Pop()
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_modsupport.h" // _PyArg_NoPositional()
#include "pycore_object.h" // _PyObject_GC_TRACK()
@ -417,14 +416,13 @@ structseq_replace(PyStructSequence *self, PyObject *args, PyObject *kwargs)
// We do not support types with unnamed fields, so we can iterate over
// i >= n_visible_fields case without slicing with (i - n_unnamed_fields).
for (i = 0; i < n_fields; ++i) {
PyObject *key = PyUnicode_FromString(Py_TYPE(self)->tp_members[i].name);
if (!key) {
PyObject *ob;
if (PyDict_PopString(kwargs, Py_TYPE(self)->tp_members[i].name,
&ob) < 0) {
goto error;
}
PyObject *ob = _PyDict_Pop(kwargs, key, self->ob_item[i]);
Py_DECREF(key);
if (!ob) {
goto error;
if (ob == NULL) {
ob = Py_NewRef(self->ob_item[i]);
}
result->ob_item[i] = ob;
}