8000 Add PyDict_Pop() function (#81) · python/pythoncapi-compat@5bf2fb2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5bf2fb2

Browse files
authored
Add PyDict_Pop() function (#81)
Add PyDict_Pop() and PyDict_PopString() functions.
1 parent 1c1ab38 commit 5bf2fb2

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

docs/api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,14 @@ Python 3.13
129129
130130
See `PyList_Clear() documentation <https://docs.python.org/dev/c-api/list.html#c.PyList_Clear>`__.
131131
132+
.. c:function:: int PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result)
133+
134+
See `PyDict_Pop() documentation <https://docs.python.org/dev/c-api/dict.html#c.PyDict_Pop>`__.
135+
136+
.. c:function:: int PyDict_PopString(PyObject *dict, const char *key, PyObject **result)
137+
138+
See `PyDict_PopString() documentation <https://docs.python.org/dev/c-api/dict.html#c.PyDict_PopString>`__.
139+
132140
133141
Python 3.12
134142
-----------

docs/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
=========
33

4+
* 2023-11-14: Add functions:
5+
6+
* ``PyDict_Pop()``
7+
* ``PyDict_PopString()``
8+
49
* 2023-11-13: Add functions:
510

611
* ``PyList_Extend()``

pythoncapi_compat.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,67 @@ PyList_Clear(PyObject *list)
10281028
}
10291029
#endif
10301030

1031+
// gh-111262 added PyDict_Pop() and PyDict_PopString() to Python 3.13.0a2
1032+
#if PY_VERSION_HEX < 0x030D00A2
1033+
static inline int
1034+
PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result)
1035+
{
1036+
PyObject *value;
1037+
1038+
if (!PyDict_Check(dict)) {
1039+
PyErr_BadInternalCall();
1040+
if (result) {
1041+
*result = NULL;
1042+
}
1043+
return -1;
1044+
}
1045+
1046+
// bpo-16991 added _PyDict_Pop() to Python 3.5.0b2.
1047+
// Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*.
1048+
// Python 3.13.0a1 removed _PyDict_Pop().
1049+
#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000
1050+
value = PyObject_CallMethod(dict, "pop", "O", key);
1051+
#elif PY_VERSION_HEX < 0x030600b3
1052+
value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL);
1053+
#else
1054+
value = _PyDict_Pop(dict, key, NULL);
1055+
#endif
1056+
if (value == NULL) {
1057+
if (result) {
1058+
*result = NULL;
1059+
}
1060+
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_KeyError)) {
1061+
return -1;
1062+
}
1063+
PyErr_Clear();
1064+
return 0;
1065+
}
1066+
if (result) {
1067+
*result = value;
1068+
}
1069+
else {
1070+
Py_DECREF(value);
1071+
}
1072+
return 1;
1073+
}
1074+
1075+
static inline int
1076+
PyDict_PopString(PyObject *dict, const char *key, PyObject **result)
1077+
{
1078+
PyObject *key_obj = PyUnicode_FromString(key);
1079+
if (key_obj == NULL) {
1080+
if (result != NULL) {
1081+
*result = NULL;
1082+
}
1083+
return -1;
1084+
}
1085+
1086+
int res = PyDict_Pop(dict, key_obj, result);
1087+
Py_DECREF(key_obj);
1088+
return res;
1089+
}
1090+
#endif
1091+
10311092
#ifdef __cplusplus
10321093
}
10331094
#endif

tests/test_pythoncapi_compat_cext.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,82 @@ test_dict_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
12531253
}
12541254

12551255

1256+
static PyObject *
1257+
test_dict_pop(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1258+
{
1259+
PyObject *dict = PyDict_New();
1260+
if (dict == NULL) {
1261+
return NULL;
1262+
}
1263+
1264+
PyObject *key = PyUnicode_FromString("key");
1265+
assert(key != NULL);
1266+
PyObject *value = PyUnicode_FromString("abc");
1267+
assert(value != NULL);
1268+
1269+
// test PyDict_Pop(), get the removed value, key is present
1270+
assert(PyDict_SetItem(dict, key, value) == 0);
1271+
PyObject *removed = UNINITIALIZED_OBJ;
1272+
assert(PyDict_Pop(dict, key, &removed) == 1);
1273+
assert(removed == value);
1274+
Py_DECREF(removed);
1275+
1276+
// test PyDict_Pop(), ignore the removed value, key is present
1277+
assert(PyDict_SetItem(dict, key, value) == 0);
1278+
assert(PyDict_Pop(dict, key, NULL) == 1);
1279+
1280+
// test PyDict_Pop(), key is missing
1281+
removed = UNINITIALIZED_OBJ;
1282+
assert(PyDict_Pop(dict, key, &removed) == 0);
1283+
assert(removed == NULL);
1284+
assert(PyDict_Pop(dict, key, NULL) == 0);
1285+
1286+
// test PyDict_PopString(), get the removed value, key is present
1287+
assert(PyDict_SetItem(dict, key, value) == 0);
1288+
removed = UNINITIALIZED_OBJ;
1289+
assert(PyDict_PopString(dict, "key", &removed) == 1);
1290+
assert(removed == value);
1291+
Py_DECREF(removed);
1292+
1293+
// test PyDict_PopString(), ignore the removed value, key is present
1294+
assert(PyDict_SetItem(dict, key, value) == 0);
1295+
assert(PyDict_PopString(dict, "key", NULL) == 1);
1296+
1297+
// test PyDict_PopString(), key is missing
1298+
removed = UNINITIALIZED_OBJ;
1299+
assert(PyDict_PopString(dict, "key", &removed) == 0);
1300+
assert(removed == NULL);
1301+
assert(PyDict_PopString(dict, "key", NULL) == 0);
1302+
1303+
// dict error
1304+
removed = UNINITIALIZED_OBJ;
1305+
assert(PyDict_Pop(key, key, &removed) == -1);
1306+
assert(removed == NULL);
1307+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1308+
PyErr_Clear();
1309+
1310+
assert(PyDict_Pop(key, key, NULL) == -1);
1311+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1312+
PyErr_Clear();
1313+
1314+
removed = UNINITIALIZED_OBJ;
1315+
assert(PyDict_PopString(key, "key", &removed) == -1);
1316+
assert(removed == NULL);
1317+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1318+
PyErr_Clear();
1319+
1320+
assert(PyDict_PopString(key, "key", NULL) == -1);
1321+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1322+
PyErr_Clear();
1323+
1324+
// exit
1325+
Py_DECREF(dict);
1326+
Py_DECREF(key);
1327+
Py_DECREF(value);
1328+
Py_RETURN_NONE;
1329+
}
1330+
1331+
12561332
static PyObject *
12571333
test_long_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
12581334
{
@@ -1447,6 +1523,7 @@ static struct PyMethodDef methods[] = {
14471523
{"test_getattr", test_getattr, METH_NOARGS, _Py_NULL},
14481524
{"test_getitem", test_getitem, METH_NOARGS, _Py_NULL},
14491525
{"test_dict_api", test_dict_api, METH_NOARGS, _Py_NULL},
1526+
{"test_dict_pop", test_dict_pop, METH_NOARGS, _Py_NULL},
14501527
{"test_long_api", test_long_api, METH_NOARGS, _Py_NULL},
14511528
#ifdef TEST_MANAGED_DICT
14521529
{"test_managed_dict", test_managed_dict, METH_NOARGS, _Py_NULL},

0 commit comments

Comments
 (0)
0