| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent babb787 commit 0693e77
19 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -173,6 +173,23 @@ Dictionary Objects | |||
| 173 | 173 | ||
| 174 | 174 | .. versionadded:: 3.4 | |
| 175 | 175 | ||
| 176 | + | ||
| 177 | + .. c:function:: int PyDict_Pop(PyObject *p, PyObject *key, PyObject **result) | ||
| 178 | + | ||
| 179 | + Remove *key* from dictionary *p* and optionally return the removed value. | ||
| 180 | + Do not raise :exc:`KeyError` if the key missing. | ||
| 181 | + | ||
| 182 | + - If the key is present, set *\*result* to a new reference to the removed | ||
| 183 | + value if *result* is not ``NULL``, and return ``1``. | ||
| 184 | + - If the key is missing, set *\*result* to ``NULL`` if *result* is not | ||
| 185 | + ``NULL``, and return ``0``. | ||
| 186 | + - On error, raise an exception and return ``-1``. | ||
| 187 | + | ||
| 188 | + This is the similar to :meth:`dict.pop`, but without the default value and | ||
| 189 | + do not raise :exc:`KeyError` if the key missing. | ||
| 190 | + | ||
| 191 | + .. versionadded:: 3.13 | ||
| 192 | + | ||
| 176 | 193 | .. c:function:: PyObject* PyDict_Items(PyObject *p) | |
| 177 | 194 | ||
| 178 | 195 | Return a :c:type:`PyListObject` containing all the items from the dictionary. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1168,6 +1168,12 @@ New Features | |||
| 1168 | 1168 | Python ``list.extend()`` and ``list.clear()`` methods. | |
| 1169 | 1169 | (Contributed by Victor Stinner in :gh:`111138`.) | |
| 1170 | 1170 | ||
| 1171 | + * Add :c:func:`PyDict_Pop` function: remove a key from a dictionary and | ||
| 1172 | + optionally return the removed value. This is the similar to :meth:`dict.pop`, | ||
| 1173 | + but without the default value and do not raise :exc:`KeyError` if the key | ||
| 1174 | + missing. | ||
| 1175 | + (Contributed by Stefan Behnel and Victor Stinner in :gh:`111262`.) | ||
| 1176 | + | ||
| 1171 | 1177 | ||
| 1172 | 1178 | Porting to Python 3.13 | |
| 1173 | 1179 | ---------------------- | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -66,6 +66,8 @@ PyAPI_FUNC(int) PyDict_DelItemString(PyObject *dp, const char *key); | |||
| 66 | 66 | // - On error, raise an exception and return -1. | |
| 67 | 67 | PyAPI_FUNC(int) PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result); | |
| 68 | 68 | PyAPI_FUNC(int) PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result); | |
| 69 | + | ||
| 70 | + PyAPI_FUNC(int) PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result); | ||
| 69 | 71 | #endif | |
| 70 | 72 | ||
| 71 | 73 | #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -116,7 +116,11 @@ extern PyObject *_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *); | |||
| 116 | 116 | extern int _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value); | |
| 117 | 117 | extern int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *name, PyObject *value); | |
| 118 | 118 | ||
| 119 | - extern PyObject *_PyDict_Pop_KnownHash(PyObject *, PyObject *, Py_hash_t, PyObject *); | ||
| 119 | + extern int _PyDict_Pop_KnownHash( | ||
| 120 | + PyDictObject *dict, | ||
| 121 | + PyObject *key, | ||
| 122 | + Py_hash_t hash, | ||
| 123 | + PyObject **result); | ||
| 120 | 124 | ||
| 121 | 125 | #define DKIX_EMPTY (-1) | |
| 122 | 126 | #define DKIX_DUMMY (-2) /* Used internally */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -432,6 +432,35 @@ def test_dict_mergefromseq2(self): | |||
| 432 | 432 | # CRASHES mergefromseq2({}, NULL, 0) | |
| 433 | 433 | # CRASHES mergefromseq2(NULL, {}, 0) | |
| 434 | 434 | ||
| 435 | + def test_dict_pop(self): | ||
| 436 | + # Test PyDict_Pop() | ||
| 437 | + dict_pop = _testcapi.dict_pop | ||
| 438 | + | ||
| 439 | + # key present | ||
| 440 | + mydict = {"key": "value", "key2": "value2"} | ||
| 441 | + self.assertEqual(dict_pop(mydict, "key"), (1, "value")) | ||
| 442 | + self.assertEqual(mydict, {"key2": "value2"}) | ||
| 443 | + self.assertEqual(dict_pop(mydict, "key2"), (1, "value2")) | ||
| 444 | + self.assertEqual(mydict, {}) | ||
| 445 | + | ||
| 446 | + # key missing; empty dict has a fast path | ||
| 447 | + self.assertEqual(dict_pop({}, "key"), (0, None)) | ||
| 448 | + self.assertEqual(dict_pop({"a": 1}, "key"), (0, None)) | ||
| 449 | + | ||
| 450 | + # dict error | ||
| 451 | + not_dict = "string" | ||
| 452 | + self.assertRaises(SystemError, dict_pop, not_dict, "key") | ||
| 453 | + | ||
| 454 | + # key error; don't hash key if dict is empty | ||
| 455 | + not_hashable_key = ["list"] | ||
| 456 | + self.assertEqual(dict_pop({}, not_hashable_key), (0, None)) | ||
| 457 | + with self.assertRaises(TypeError): | ||
| 458 | + dict_pop({'key': 1}, not_hashable_key) | ||
| 459 | + dict_pop({}, NULL) # key is not checked if dict is empty | ||
| 460 | + | ||
| 461 | + # CRASHES dict_pop(NULL, "key") | ||
| 462 | + # CRASHES dict_pop({"a": 1}, NULL, default) | ||
| 463 | + | ||
| 435 | 464 | ||
| 436 | 465 | if __name__ == "__main__": | |
| 437 | 466 | unittest.main() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + Add :c:func:`PyDict_Pop` function: remove a key from a dictionary and | ||
| 2 | + optionally return the removed value. This is the similar to :meth:`dict.pop`, | ||
| 3 | + but without the default value and do not raise :exc:`KeyError` if the key | ||
| 4 | + missing. Patch by Stefan Behnel and Victor Stinner. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2481,3 +2481,5 @@ | |||
| 2481 | 2481 | [function._Py_SetRefcnt] | |
| 2482 | 2482 | added = '3.13' | |
| 2483 | 2483 | abi_only = true | |
| 2484 | + [function.PyDict_Pop] | ||
| 2485 | + added = '3.13' | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1087,19 +1087,9 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds | |||
| 1087 | 1087 | The cache dict holds one reference to the link. | |
| 1088 | 1088 | We created one other reference when the link was created. | |
| 1089 | 1089 | The linked list only has borrowed references. */ | |
| 1090 | - popresult = _PyDict_Pop_KnownHash(self->cache, link->key, | ||
| 1091 | - link->hash, Py_None); | ||
| 1092 | - if (popresult == Py_None) { | ||
| 1093 | - /* Getting here means that the user function call or another | ||
| 1094 | - thread has already removed the old key from the dictionary. | ||
| 1095 | - This link is now an orphan. Since we don't want to leave the | ||
| 1096 | - cache in an inconsistent state, we don't restore the link. */ | ||
| 1097 | - Py_DECREF(popresult); | ||
| 1098 | - Py_DECREF(link); | ||
| 1099 | - Py_DECREF(key); | ||
| 1100 | - return result; | ||
| 1101 | - } | ||
| 1102 | - if (popresult == NULL) { | ||
| 1090 | + int res = _PyDict_Pop_KnownHash((PyDictObject*)self->cache, link->key, | ||
| 1091 | + link->hash, &popresult); | ||
| 1092 | + if (res < 0) { | ||
| 1103 | 1093 | /* An error arose while trying to remove the oldest key (the one | |
| 1104 | 1094 | being evicted) from the cache. We restore the link to its | |
| 1105 | 1095 | 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 | |||
| 1110 | 1100 | Py_DECREF(result); | |
| 1111 | 1101 | return NULL; | |
| 1112 | 1102 | } | |
| 1103 | + if (res == 0) { | ||
| 1104 | + /* Getting here means that the user function call or another | ||
| 1105 | + thread has already removed the old key from the dictionary. | ||
| 1106 | + This link is now an orphan. Since we don't want to leave the | ||
| 1107 | + cache in an inconsistent state, we don't restore the link. */ | ||
| 1108 | + assert(popresult == NULL); | ||
| 1109 | + Py_DECREF(link); | ||
| 1110 | + Py_DECREF(key); | ||
| 1111 | + return result; | ||
| 1112 | + } | ||
| 1113 | + | ||
| 1113 | 1114 | /* Keep a reference to the old key and old result to prevent their | |
| 1114 | 1115 | ref counts from going to zero during the update. That will | |
| 1115 | 1116 | prevent potentially arbitrary object clean-up code (i.e. __del__) | |
| 1116 | 1117 | from running while we're still adjusting the links. */ | |
| 1118 | + assert(popresult != NULL); | ||
| 1117 | 1119 | oldkey = link->key; | |
| 1118 | 1120 | oldresult = link->result; | |
| 1119 | 1121 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments