Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 33.9k
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
Uh oh!
There was an error while loading. Please reload this page.
Conversation
vstinner commented Nov 13, 2023 • edited by github-actions bot
Loading Uh oh!
There was an error while loading. Please reload this page.
edited by github-actions bot
Uh oh!
There was an error while loading. Please reload this page.
vstinner commented Nov 13, 2023
vstinner commented Nov 13, 2023
Simplified example where staticintsys_set_object(PyInterpreterState*interp, PyObject*key, PyObject*v){PyObject*sd=interp->sysdict; if (v==NULL){if (PyDict_Pop(sd, key, NULL) <0){return-1} // no need to care about KeyError or Py_DECREF()return0} else{returnPyDict_SetItem(sd, key, v)} }Example which uses the removed value: PyObject*ob; if (PyDict_Pop(kwargs, key, &ob) <0){goto error} if (ob!=NULL){result->ob_item[i] =ob} else{// no need to care about KeyError or Py_DECREF()result->ob_item[i] =Py_NewRef(self->ob_item[i])} |
vstinner commented Nov 13, 2023
The function is not added to the limited C API: @encukouasks to wait until Python will have a C API Working Group. |
serhiy-storchaka left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR includes yet one change. It allows to pass NULL as a result address. It complicates the implementation, but makes the common use case simpler.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| PyObject* | ||
| _PyDict_Pop(PyObject*dict, PyObject*key, PyObject*default_value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to remove it in a separated PR: see #112026
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| returnNULL; | ||
| } | ||
| if (result==NULL){ | ||
| result=Py_NewRef(Py_None); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| result=Py_NewRef(Py_None); | |
| returnPyLong_FromLong(res); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to return (0, None) to make the tests written in Python closer to what the C API returns. In test_capi.test_dict, you can see that as (0, NULL).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be confused with actual None. For example dict.get() return an actual none, but several corresponding C API functions return NULL.
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 _testcapi.MISSING in future.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
vstinner commented Nov 13, 2023
As requested, I added I added tests on PyDict_PopString() and I also addresssed @serhiy-storchaka's review. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
vstinner commented Nov 13, 2023
@serhiy-storchaka: I added more tests. |
serhiy-storchaka left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
scoder left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM overall, just wording fixes.
| * Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's usually clearer to write "not raising an exception" (at all) than naming an explicit exception and leaving it open whether other exceptions might be raised in the described case.
| * Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: 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. | |
| * Add :c:func:`PyDict_Pop` and :c:func:`PyDict_PopString` functions: remove a | |
| key from a dictionary and optionally return the removed value. This is | |
| similar to :meth:`dict.pop`, but without the default value and not raising | |
| an exception if the key missing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I applied your suggestion to the 3 places where I wrote that doc, but I kept KeyError. IMO it's useful to be explicit about KeyError to understand what we are talking about.
Misc/NEWS.d/next/C API/2023-11-10-10-21-38.gh-issue-111262.2utB5m.rst Outdated Show resolvedHide resolved
Uh oh!
There was an error while loading. Please reload this page.
vstinner commented Nov 14, 2023
At the beginning, I had no idea. It was very blurry. What helped me to make my own opinion was to actually use the different proposed API and look at the updated code. I really like this API without the default value: I'm convinced by these examples. In short, I prefer this PR. |
_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>
_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>
_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>
_PyDict_Pop_KnownHash(): remove the default value and the return type becomes an int.
📚 Documentation preview 📚: https://cpython-previews--112028.org.readthedocs.build/