mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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:
parent
f44d6ff6e0
commit
4f04172c92
15 changed files with 338 additions and 76 deletions
|
@ -173,6 +173,33 @@ Dictionary Objects
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyDict_Pop(PyObject *p, PyObject *key, PyObject **result)
|
||||||
|
|
||||||
|
Remove *key* from dictionary *p* and optionally return the removed value.
|
||||||
|
Do not raise :exc:`KeyError` if the key missing.
|
||||||
|
|
||||||
|
- If the key is present, set *\*result* to a new reference to the removed
|
||||||
|
value if *result* is not ``NULL``, and return ``1``.
|
||||||
|
- If the key is missing, set *\*result* to ``NULL`` if *result* is not
|
||||||
|
``NULL``, and return ``0``.
|
||||||
|
- On error, raise an exception and return ``-1``.
|
||||||
|
|
||||||
|
This is similar to :meth:`dict.pop`, but without the default value and
|
||||||
|
not raising :exc:`KeyError` if the key missing.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: int PyDict_PopString(PyObject *p, const char *key, PyObject **result)
|
||||||
|
|
||||||
|
Similar to :c:func:`PyDict_Pop`, but *key* is specified as a
|
||||||
|
:c:expr:`const char*` UTF-8 encoded bytes string, rather than a
|
||||||
|
:c:expr:`PyObject*`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: PyObject* PyDict_Items(PyObject *p)
|
.. c:function:: PyObject* PyDict_Items(PyObject *p)
|
||||||
|
|
||||||
Return a :c:type:`PyListObject` containing all the items from the dictionary.
|
Return a :c:type:`PyListObject` containing all the items from the dictionary.
|
||||||
|
|
|
@ -1175,6 +1175,12 @@ New Features
|
||||||
Python ``list.extend()`` and ``list.clear()`` methods.
|
Python ``list.extend()`` and ``list.clear()`` methods.
|
||||||
(Contributed by Victor Stinner in :gh:`111138`.)
|
(Contributed by Victor Stinner in :gh:`111138`.)
|
||||||
|
|
||||||
|
* 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
|
||||||
|
:exc:`KeyError` if the key missing.
|
||||||
|
(Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
@ -46,6 +46,8 @@ static inline Py_ssize_t PyDict_GET_SIZE(PyObject *op) {
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyDict_ContainsString(PyObject *mp, const char *key);
|
PyAPI_FUNC(int) PyDict_ContainsString(PyObject *mp, const char *key);
|
||||||
|
|
||||||
|
PyAPI_FUNC(int) PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result);
|
||||||
|
PyAPI_FUNC(int) PyDict_PopString(PyObject *dict, const char *key, PyObject **result);
|
||||||
PyAPI_FUNC(PyObject *) _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value);
|
PyAPI_FUNC(PyObject *) _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value);
|
||||||
|
|
||||||
/* Dictionary watchers */
|
/* Dictionary watchers */
|
||||||
|
|
|
@ -116,7 +116,11 @@ extern PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
|
||||||
extern int _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
|
extern int _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
|
||||||
extern int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value);
|
extern int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value);
|
||||||
|
|
||||||
extern PyObject *_PyDict_Pop_KnownHash(PyObject *, PyObject *, Py_hash_t, PyObject *);
|
extern int _PyDict_Pop_KnownHash(
|
||||||
|
PyDictObject *dict,
|
||||||
|
PyObject *key,
|
||||||
|
Py_hash_t hash,
|
||||||
|
PyObject **result);
|
||||||
|
|
||||||
#define DKIX_EMPTY (-1)
|
#define DKIX_EMPTY (-1)
|
||||||
#define DKIX_DUMMY (-2) /* Used internally */
|
#define DKIX_DUMMY (-2) /* Used internally */
|
||||||
|
|
|
@ -432,6 +432,93 @@ class CAPITest(unittest.TestCase):
|
||||||
# CRASHES mergefromseq2({}, NULL, 0)
|
# CRASHES mergefromseq2({}, NULL, 0)
|
||||||
# CRASHES mergefromseq2(NULL, {}, 0)
|
# CRASHES mergefromseq2(NULL, {}, 0)
|
||||||
|
|
||||||
|
def test_dict_pop(self):
|
||||||
|
# Test PyDict_Pop()
|
||||||
|
dict_pop = _testcapi.dict_pop
|
||||||
|
dict_pop_null = _testcapi.dict_pop_null
|
||||||
|
|
||||||
|
# key present, get removed value
|
||||||
|
mydict = {"key": "value", "key2": "value2"}
|
||||||
|
self.assertEqual(dict_pop(mydict, "key"), (1, "value"))
|
||||||
|
self.assertEqual(mydict, {"key2": "value2"})
|
||||||
|
self.assertEqual(dict_pop(mydict, "key2"), (1, "value2"))
|
||||||
|
self.assertEqual(mydict, {})
|
||||||
|
|
||||||
|
# key present, ignore removed value
|
||||||
|
mydict = {"key": "value", "key2": "value2"}
|
||||||
|
self.assertEqual(dict_pop_null(mydict, "key"), 1)
|
||||||
|
self.assertEqual(mydict, {"key2": "value2"})
|
||||||
|
self.assertEqual(dict_pop_null(mydict, "key2"), 1)
|
||||||
|
self.assertEqual(mydict, {})
|
||||||
|
|
||||||
|
# key missing, expect removed value; empty dict has a fast path
|
||||||
|
self.assertEqual(dict_pop({}, "key"), (0, NULL))
|
||||||
|
self.assertEqual(dict_pop({"a": 1}, "key"), (0, NULL))
|
||||||
|
|
||||||
|
# key missing, ignored removed value; empty dict has a fast path
|
||||||
|
self.assertEqual(dict_pop_null({}, "key"), 0)
|
||||||
|
self.assertEqual(dict_pop_null({"a": 1}, "key"), 0)
|
||||||
|
|
||||||
|
# dict error
|
||||||
|
not_dict = UserDict({1: 2})
|
||||||
|
self.assertRaises(SystemError, dict_pop, not_dict, "key")
|
||||||
|
self.assertRaises(SystemError, dict_pop_null, not_dict, "key")
|
||||||
|
|
||||||
|
# key error; don't hash key if dict is empty
|
||||||
|
not_hashable_key = ["list"]
|
||||||
|
self.assertEqual(dict_pop({}, not_hashable_key), (0, NULL))
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
dict_pop({'key': 1}, not_hashable_key)
|
||||||
|
dict_pop({}, NULL) # key is not checked if dict is empty
|
||||||
|
|
||||||
|
# CRASHES dict_pop(NULL, "key")
|
||||||
|
# CRASHES dict_pop({"a": 1}, NULL)
|
||||||
|
|
||||||
|
def test_dict_popstring(self):
|
||||||
|
# Test PyDict_PopString()
|
||||||
|
dict_popstring = _testcapi.dict_popstring
|
||||||
|
dict_popstring_null = _testcapi.dict_popstring_null
|
||||||
|
|
||||||
|
# key present, get removed value
|
||||||
|
mydict = {"key": "value", "key2": "value2"}
|
||||||
|
self.assertEqual(dict_popstring(mydict, "key"), (1, "value"))
|
||||||
|
self.assertEqual(mydict, {"key2": "value2"})
|
||||||
|
self.assertEqual(dict_popstring(mydict, "key2"), (1, "value2"))
|
||||||
|
self.assertEqual(mydict, {})
|
||||||
|
|
||||||
|
# key present, ignore removed value
|
||||||
|
mydict = {"key": "value", "key2": "value2"}
|
||||||
|
self.assertEqual(dict_popstring_null(mydict, "key"), 1)
|
||||||
|
self.assertEqual(mydict, {"key2": "value2"})
|
||||||
|
self.assertEqual(dict_popstring_null(mydict, "key2"), 1)
|
||||||
|
self.assertEqual(mydict, {})
|
||||||
|
|
||||||
|
# key missing; empty dict has a fast path
|
||||||
|
self.assertEqual(dict_popstring({}, "key"), (0, NULL))
|
||||||
|
self.assertEqual(dict_popstring_null({}, "key"), 0)
|
||||||
|
self.assertEqual(dict_popstring({"a": 1}, "key"), (0, NULL))
|
||||||
|
self.assertEqual(dict_popstring_null({"a": 1}, "key"), 0)
|
||||||
|
|
||||||
|
# non-ASCII key
|
||||||
|
non_ascii = '\U0001f40d'
|
||||||
|
dct = {'\U0001f40d': 123}
|
||||||
|
self.assertEqual(dict_popstring(dct, '\U0001f40d'.encode()), (1, 123))
|
||||||
|
dct = {'\U0001f40d': 123}
|
||||||
|
self.assertEqual(dict_popstring_null(dct, '\U0001f40d'.encode()), 1)
|
||||||
|
|
||||||
|
# dict error
|
||||||
|
not_dict = UserDict({1: 2})
|
||||||
|
self.assertRaises(SystemError, dict_popstring, not_dict, "key")
|
||||||
|
self.assertRaises(SystemError, dict_popstring_null, not_dict, "key")
|
||||||
|
|
||||||
|
# key error
|
||||||
|
self.assertRaises(UnicodeDecodeError, dict_popstring, {1: 2}, INVALID_UTF8)
|
||||||
|
self.assertRaises(UnicodeDecodeError, dict_popstring_null, {1: 2}, INVALID_UTF8)
|
||||||
|
|
||||||
|
# CRASHES dict_popstring(NULL, "key")
|
||||||
|
# CRASHES dict_popstring({}, NULL)
|
||||||
|
# CRASHES dict_popstring({"a": 1}, NULL)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
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 :exc:`KeyError`
|
||||||
|
if the key missing. Patch by Stefan Behnel and Victor Stinner.
|
|
@ -1087,19 +1087,9 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
|
||||||
The cache dict holds one reference to the link.
|
The cache dict holds one reference to the link.
|
||||||
We created one other reference when the link was created.
|
We created one other reference when the link was created.
|
||||||
The linked list only has borrowed references. */
|
The linked list only has borrowed references. */
|
||||||
popresult = _PyDict_Pop_KnownHash(self->cache, link->key,
|
int res = _PyDict_Pop_KnownHash((PyDictObject*)self->cache, link->key,
|
||||||
link->hash, Py_None);
|
link->hash, &popresult);
|
||||||
if (popresult == Py_None) {
|
if (res < 0) {
|
||||||
/* Getting here means that the user function call or another
|
|
||||||
thread has already removed the old key from the dictionary.
|
|
||||||
This link is now an orphan. Since we don't want to leave the
|
|
||||||
cache in an inconsistent state, we don't restore the link. */
|
|
||||||
Py_DECREF(popresult);
|
|
||||||
Py_DECREF(link);
|
|
||||||
Py_DECREF(key);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (popresult == NULL) {
|
|
||||||
/* An error arose while trying to remove the oldest key (the one
|
/* An error arose while trying to remove the oldest key (the one
|
||||||
being evicted) from the cache. We restore the link to its
|
being evicted) from the cache. We restore the link to its
|
||||||
original position as the oldest link. Then we allow the
|
original position as the oldest link. Then we allow the
|
||||||
|
@ -1110,10 +1100,22 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (res == 0) {
|
||||||
|
/* Getting here means that the user function call or another
|
||||||
|
thread has already removed the old key from the dictionary.
|
||||||
|
This link is now an orphan. Since we don't want to leave the
|
||||||
|
cache in an inconsistent state, we don't restore the link. */
|
||||||
|
assert(popresult == NULL);
|
||||||
|
Py_DECREF(link);
|
||||||
|
Py_DECREF(key);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/* Keep a reference to the old key and old result to prevent their
|
/* Keep a reference to the old key and old result to prevent their
|
||||||
ref counts from going to zero during the update. That will
|
ref counts from going to zero during the update. That will
|
||||||
prevent potentially arbitrary object clean-up code (i.e. __del__)
|
prevent potentially arbitrary object clean-up code (i.e. __del__)
|
||||||
from running while we're still adjusting the links. */
|
from running while we're still adjusting the links. */
|
||||||
|
assert(popresult != NULL);
|
||||||
oldkey = link->key;
|
oldkey = link->key;
|
||||||
oldresult = link->result;
|
oldresult = link->result;
|
||||||
|
|
||||||
|
|
|
@ -331,6 +331,88 @@ dict_mergefromseq2(PyObject *self, PyObject *args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
dict_pop(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
// Test PyDict_Pop(dict, key, &value)
|
||||||
|
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) {
|
||||||
|
assert(result == NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (res == 0) {
|
||||||
|
assert(result == NULL);
|
||||||
|
result = Py_NewRef(Py_None);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(result != NULL);
|
||||||
|
}
|
||||||
|
return Py_BuildValue("iN", res, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
dict_pop_null(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
// Test PyDict_Pop(dict, key, NULL)
|
||||||
|
PyObject *dict, *key;
|
||||||
|
if (!PyArg_ParseTuple(args, "OO", &dict, &key)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NULLABLE(dict);
|
||||||
|
NULLABLE(key);
|
||||||
|
RETURN_INT(PyDict_Pop(dict, key, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
dict_popstring(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *dict;
|
||||||
|
const char *key;
|
||||||
|
Py_ssize_t key_size;
|
||||||
|
if (!PyArg_ParseTuple(args, "Oz#", &dict, &key, &key_size)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NULLABLE(dict);
|
||||||
|
PyObject *result = UNINITIALIZED_PTR;
|
||||||
|
int res = PyDict_PopString(dict, key, &result);
|
||||||
|
if (res < 0) {
|
||||||
|
assert(result == NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (res == 0) {
|
||||||
|
assert(result == NULL);
|
||||||
|
result = Py_NewRef(Py_None);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(result != NULL);
|
||||||
|
}
|
||||||
|
return Py_BuildValue("iN", res, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
dict_popstring_null(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *dict;
|
||||||
|
const char *key;
|
||||||
|
Py_ssize_t key_size;
|
||||||
|
if (!PyArg_ParseTuple(args, "Oz#", &dict, &key, &key_size)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
NULLABLE(dict);
|
||||||
|
RETURN_INT(PyDict_PopString(dict, key, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
{"dict_check", dict_check, METH_O},
|
{"dict_check", dict_check, METH_O},
|
||||||
{"dict_checkexact", dict_checkexact, METH_O},
|
{"dict_checkexact", dict_checkexact, METH_O},
|
||||||
|
@ -358,7 +440,10 @@ static PyMethodDef test_methods[] = {
|
||||||
{"dict_merge", dict_merge, METH_VARARGS},
|
{"dict_merge", dict_merge, METH_VARARGS},
|
||||||
{"dict_update", dict_update, METH_VARARGS},
|
{"dict_update", dict_update, METH_VARARGS},
|
||||||
{"dict_mergefromseq2", dict_mergefromseq2, METH_VARARGS},
|
{"dict_mergefromseq2", dict_mergefromseq2, METH_VARARGS},
|
||||||
|
{"dict_pop", dict_pop, METH_VARARGS},
|
||||||
|
{"dict_pop_null", dict_pop_null, METH_VARARGS},
|
||||||
|
{"dict_popstring", dict_popstring, METH_VARARGS},
|
||||||
|
{"dict_popstring_null", dict_popstring_null, METH_VARARGS},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -967,11 +967,8 @@ local_clear(localobject *self)
|
||||||
HEAD_UNLOCK(runtime);
|
HEAD_UNLOCK(runtime);
|
||||||
while (tstate) {
|
while (tstate) {
|
||||||
if (tstate->dict) {
|
if (tstate->dict) {
|
||||||
PyObject *v = _PyDict_Pop(tstate->dict, self->key, Py_None);
|
if (PyDict_Pop(tstate->dict, self->key, NULL) < 0) {
|
||||||
if (v != NULL) {
|
// Silently ignore error
|
||||||
Py_DECREF(v);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,16 +392,10 @@ remove_unusable_flags(PyObject *m)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
PyObject *flag_name = PyUnicode_FromString(win_runtime_flags[i].flag_name);
|
if (PyDict_PopString(dict, win_runtime_flags[i].flag_name,
|
||||||
if (flag_name == NULL) {
|
NULL) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
PyObject *v = _PyDict_Pop(dict, flag_name, Py_None);
|
|
||||||
Py_DECREF(flag_name);
|
|
||||||
if (v == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
Py_DECREF(v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -2226,64 +2226,119 @@ PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
|
||||||
return _PyDict_Next(op, ppos, pkey, pvalue, NULL);
|
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));
|
/* Internal version of dict.pop(). */
|
||||||
mp = (PyDictObject *)dict;
|
int
|
||||||
|
_PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
|
||||||
|
PyObject **result)
|
||||||
|
{
|
||||||
|
assert(PyDict_Check(mp));
|
||||||
|
|
||||||
if (mp->ma_used == 0) {
|
if (mp->ma_used == 0) {
|
||||||
if (deflt) {
|
if (result) {
|
||||||
return Py_NewRef(deflt);
|
*result = NULL;
|
||||||
}
|
}
|
||||||
_PyErr_SetKeyError(key);
|
return 0;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
ix = _Py_dict_lookup(mp, key, hash, &old_value);
|
|
||||||
if (ix == DKIX_ERROR)
|
PyObject *old_value;
|
||||||
return NULL;
|
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 (ix == DKIX_EMPTY || old_value == NULL) {
|
||||||
if (deflt) {
|
if (result) {
|
||||||
return Py_NewRef(deflt);
|
*result = NULL;
|
||||||
}
|
}
|
||||||
_PyErr_SetKeyError(key);
|
return 0;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(old_value != NULL);
|
assert(old_value != NULL);
|
||||||
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
uint64_t new_version = _PyDict_NotifyEvent(
|
uint64_t new_version = _PyDict_NotifyEvent(
|
||||||
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
interp, PyDict_EVENT_DELETED, mp, key, NULL);
|
||||||
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
|
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
|
||||||
|
|
||||||
ASSERT_CONSISTENT(mp);
|
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) {
|
int
|
||||||
if (deflt) {
|
PyDict_Pop(PyObject *op, PyObject *key, PyObject **result)
|
||||||
return Py_NewRef(deflt);
|
{
|
||||||
|
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);
|
_PyErr_SetKeyError(key);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
|
return result;
|
||||||
hash = PyObject_Hash(key);
|
|
||||||
if (hash == -1)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return _PyDict_Pop_KnownHash(dict, key, hash, deflt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Internal version of dict.from_keys(). It is subclass-friendly. */
|
/* Internal version of dict.from_keys(). It is subclass-friendly. */
|
||||||
PyObject *
|
PyObject *
|
||||||
_PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
|
_PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
|
||||||
|
|
|
@ -1049,7 +1049,10 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* Now delete the value from the dict. */
|
/* 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()) {
|
else if (value == NULL && !PyErr_Occurred()) {
|
||||||
/* Apply the fallback value, if necessary. */
|
/* Apply the fallback value, if necessary. */
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "pycore_dict.h" // _PyDict_Pop()
|
|
||||||
#include "pycore_initconfig.h" // _PyStatus_OK()
|
#include "pycore_initconfig.h" // _PyStatus_OK()
|
||||||
#include "pycore_modsupport.h" // _PyArg_NoPositional()
|
#include "pycore_modsupport.h" // _PyArg_NoPositional()
|
||||||
#include "pycore_object.h" // _PyObject_GC_TRACK()
|
#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
|
// 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).
|
// i >= n_visible_fields case without slicing with (i - n_unnamed_fields).
|
||||||
for (i = 0; i < n_fields; ++i) {
|
for (i = 0; i < n_fields; ++i) {
|
||||||
PyObject *key = PyUnicode_FromString(Py_TYPE(self)->tp_members[i].name);
|
PyObject *ob;
|
||||||
if (!key) {
|
if (PyDict_PopString(kwargs, Py_TYPE(self)->tp_members[i].name,
|
||||||
|
&ob) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
PyObject *ob = _PyDict_Pop(kwargs, key, self->ob_item[i]);
|
if (ob == NULL) {
|
||||||
Py_DECREF(key);
|
ob = Py_NewRef(self->ob_item[i]);
|
||||||
if (!ob) {
|
|
||||||
goto error;
|
|
||||||
}
|
}
|
||||||
result->ob_item[i] = ob;
|
result->ob_item[i] = ob;
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,8 +395,8 @@ remove_module(PyThreadState *tstate, PyObject *name)
|
||||||
|
|
||||||
PyObject *modules = MODULES(tstate->interp);
|
PyObject *modules = MODULES(tstate->interp);
|
||||||
if (PyDict_CheckExact(modules)) {
|
if (PyDict_CheckExact(modules)) {
|
||||||
PyObject *mod = _PyDict_Pop(modules, name, Py_None);
|
// Error is reported to the caller
|
||||||
Py_XDECREF(mod);
|
(void)PyDict_Pop(modules, name, NULL);
|
||||||
}
|
}
|
||||||
else if (PyMapping_DelItem(modules, name) < 0) {
|
else if (PyMapping_DelItem(modules, name) < 0) {
|
||||||
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
|
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
|
||||||
|
|
|
@ -125,11 +125,9 @@ sys_set_object(PyInterpreterState *interp, PyObject *key, PyObject *v)
|
||||||
}
|
}
|
||||||
PyObject *sd = interp->sysdict;
|
PyObject *sd = interp->sysdict;
|
||||||
if (v == NULL) {
|
if (v == NULL) {
|
||||||
v = _PyDict_Pop(sd, key, Py_None);
|
if (PyDict_Pop(sd, key, NULL) < 0) {
|
||||||
if (v == NULL) {
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
Py_DECREF(v);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue