Repository navigation
gh-111262: Add PyDict_Pop() function [without default value nor KeyError] #112028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
1498269
ae73490
f65c10f
9da06c8
343f75a
2170fc0
c3b8bd2
a286604
229f524
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
_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>
- Loading branch information
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Add :c:func:`PyDict_Pop` function: remove a key from a dictionary and | ||
| optionally return the removed value. This is the similar to :meth:`dict.pop`, | ||
| but without the default value and do not raise :exc:`KeyError` if the key | ||
| missing. Patch by Stefan Behnel and Victor Stinner. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2481,3 +2481,5 @@ | |
| [function._Py_SetRefcnt] | ||
| added = '3.13' | ||
| abi_only = true | ||
| [function.PyDict_Pop] | ||
| added = '3.13' | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -331,6 +331,27 @@ dict_mergefromseq2(PyObject *self, PyObject *args) | |||||
| } | ||||||
|
|
||||||
|
|
||||||
| static PyObject * | ||||||
| dict_pop(PyObject *self, PyObject *args) | ||||||
|
vstinner marked this conversation as resolved.
|
||||||
| { | ||||||
| PyObject *dict, *key; | ||||||
| if (!PyArg_ParseTuple(args, "OO", &dict, &key)) { | ||||||
| return NULL; | ||||||
| } | ||||||
| NULLABLE(dict); | ||||||
| NULLABLE(key); | ||||||
| PyObject *result = UNINITIALIZED_PTR; | ||||||
| int res = PyDict_Pop(dict, key, &result); | ||||||
| if (res < 0) { | ||||||
| return NULL; | ||||||
|
vstinner marked this conversation as resolved.
|
||||||
| } | ||||||
| if (result == NULL) { | ||||||
| result = Py_NewRef(Py_None); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to return
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be confused with actual None. For example In other tests I made them returning AttributeError or KeyError, as it is less chance to confuse with real value, but I think that it would be better to use a special singleton |
||||||
| } | ||||||
| return Py_BuildValue("iN", res, result); | ||||||
| } | ||||||
|
|
||||||
|
|
||||||
| static PyMethodDef test_methods[] = { | ||||||
| {"dict_check", dict_check, METH_O}, | ||||||
| {"dict_checkexact", dict_checkexact, METH_O}, | ||||||
|
|
@@ -358,7 +379,7 @@ static PyMethodDef test_methods[] = { | |||||
| {"dict_merge", dict_merge, METH_VARARGS}, | ||||||
| {"dict_update", dict_update, METH_VARARGS}, | ||||||
| {"dict_mergefromseq2", dict_mergefromseq2, METH_VARARGS}, | ||||||
|
|
||||||
| {"dict_pop", dict_pop, METH_VARARGS}, | ||||||
| {NULL}, | ||||||
| }; | ||||||
|
|
||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2226,64 +2226,102 @@ 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) | ||
| int | ||
| _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash, | ||
| PyObject **result) | ||
| { | ||
| Py_ssize_t ix; | ||
| PyObject *old_value; | ||
| PyDictObject *mp; | ||
| PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
|
|
||
| assert(PyDict_Check(dict)); | ||
| mp = (PyDictObject *)dict; | ||
| 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) | ||
|
|
||
| int | ||
| PyDict_Pop(PyObject *op, PyObject *key, PyObject **result) | ||
| { | ||
| Py_hash_t hash; | ||
| if (!PyDict_Check(op)) { | ||
| if (result) { | ||
| *result = NULL; | ||
| } | ||
| PyErr_BadInternalCall(); | ||
| return -1; | ||
| } | ||
| PyDictObject *dict = (PyDictObject *)op; | ||
|
|
||
| if (((PyDictObject *)dict)->ma_used == 0) { | ||
| if (deflt) { | ||
| return Py_NewRef(deflt); | ||
| if (dict->ma_used == 0) { | ||
| if (result) { | ||
| *result = NULL; | ||
| } | ||
| _PyErr_SetKeyError(key); | ||
| return NULL; | ||
| return 0; | ||
| } | ||
|
|
||
| Py_hash_t hash; | ||
| if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) { | ||
| hash = PyObject_Hash(key); | ||
| if (hash == -1) | ||
| return NULL; | ||
| if (hash == -1) { | ||
| if (result) { | ||
| *result = NULL; | ||
| } | ||
| return -1; | ||
| } | ||
| } | ||
| return _PyDict_Pop_KnownHash(dict, key, hash, deflt); | ||
| return _PyDict_Pop_KnownHash(dict, key, hash, result); | ||
| } | ||
|
|
||
|
|
||
| PyObject * | ||
| _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not remove it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to remove it in a separated PR: see #112026 |
||
| { | ||
| PyObject *result; | ||
| if (PyDict_Pop(dict, key, &result) == 0) { | ||
| if (default_value != NULL) { | ||
| return Py_NewRef(default_value); | ||
| } | ||
| _PyErr_SetKeyError(key); | ||
| return NULL; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
|
|
||
| /* Internal version of dict.from_keys(). It is subclass-friendly. */ | ||
| PyObject * | ||
| _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.