Skip to content

Commit eaff3c1

Browse files
committed
Add PyDict_GetItemRef()
1 parent c7dac97 commit eaff3c1

File tree

5 files changed

+166
-7
lines changed

5 files changed

+166
-7
lines changed

docs/api.rst

+12-4
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ Latest version of the header file:
2727
Python 3.13
2828
-----------
2929

30-
.. c:function:: PyObject* PyImport_AddModuleRef(const char *name)
30+
.. c:function:: int PyDict_GetItemRef(PyObject *p, PyObject *key, PyObject **result)
3131
32-
See `PyImport_AddModuleRef() documentation <https://docs.python.org/dev/c-api/import.html#c.PyImport_AddModuleRef>`__.
32+
See `PyDict_GetItemRef() documentation <https://docs.python.org/dev/c-api/dict.html#c.PyDict_GetItemRef>`__.
3333
34-
.. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
34+
.. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **result)
3535
36-
See `PyWeakref_GetRef() documentation <https://docs.python.org/dev/c-api/weakref.html#c.PyWeakref_GetRef>`__.
36+
See `PyDict_GetItemStringRef() documentation <https://docs.python.org/dev/c-api/dict.html#c.PyDict_GetItemStringRef>`__.
37+
38+
.. c:function:: PyObject* PyImport_AddModuleRef(const char *name)
39+
40+
See `PyImport_AddModuleRef() documentation <https://docs.python.org/dev/c-api/import.html#c.PyImport_AddModuleRef>`__.
3741
3842
.. c:function:: int PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result)
3943
@@ -55,6 +59,10 @@ Python 3.13
5559
5660
See `PyModule_Add() documentation <https://docs.python.org/dev/c-api/module.html#c.PyModule_Add>`__.
5761
62+
.. c:function:: int PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
63+
64+
See `PyWeakref_GetRef() documentation <https://docs.python.org/dev/c-api/weakref.html#c.PyWeakref_GetRef>`__.
65+
5866
5967
Python 3.12
6068
-----------

docs/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33

4+
* 2023-07-21: Add ``PyDict_GetItemRef()`` function.
45
* 2023-07-18: Add ``PyModule_Add()`` function.
56
* 2023-07-12: Add ``PyObject_GetOptionalAttr()``,
67
``PyObject_GetOptionalAttrString()``,

docs/users.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@ Examples of projects using pythoncapi_compat.h
3838
(`commit <https://github.com/sergey-dryabzhinsky/python-zstd/commit/8aa6d7a4b250e1f0a4e27b4107c39dc516c87f96>`__)
3939
* `hollerith <https://github.com/pyansys/hollerith/>`_
4040
``src/writer.c`` uses ``PyObject_CallOneArg() and other Python 3.9 apis``
41-
(`pythoncapi_compat.h copy
41+
(`pythoncapi_compat.h copy
4242
<https://github.com/pyansys/hollerith/blob/main/src/pythoncapi_compat.h>`__)
4343

4444

45-
4645
Projects not using pythoncapi_compat.h
4746
======================================
4847

pythoncapi_compat.h

+44-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ PyFrame_GetVar(PyFrameObject *frame, PyObject *name)
258258
#if PY_VERSION_HEX >= 0x03000000
259259
value = PyDict_GetItemWithError(locals, name);
260260
#else
261-
value = PyDict_GetItem(locals, name);
261+
value = _PyDict_GetItemWithError(locals, name);
262262
#endif
263263
Py_DECREF(locals);
264264

@@ -791,6 +791,49 @@ PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **resul
791791
#endif
792792

793793

794+
// gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef()
795+
// to Python 3.13.0a1
796+
#if PY_VERSION_HEX < 0x030D00A1
797+
PYCAPI_COMPAT_STATIC_INLINE(int)
798+
PyDict_GetItemRef(PyObject *mp, PyObject *key, PyObject **result)
799+
{
800+
#if PY_VERSION_HEX >= 0x03000000
801+
PyObject *item = PyDict_GetItemWithError(mp, key);
802+
#else
803+
PyObject *item = _PyDict_GetItemWithError(mp, key);
804+
#endif
805+
if (item != NULL) {
806+
*result = Py_NewRef(item);
807+
return 1; // found
808+
}
809+
if (!PyErr_Occurred()) {
810+
*result = NULL;
811+
return 0; // not found
812+
}
813+
*result = NULL;
814+
return -1;
815+
}
816+
817+
PYCAPI_COMPAT_STATIC_INLINE(int)
818+
PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result)
819+
{
820+
int res;
821+
#if PY_VERSION_HEX >= 0x03000000
822+
PyObject *key_obj = PyUnicode_FromString(key);
823+
#else
824+
PyObject *key_obj = PyString_FromString(key);
825+
#endif
826+
if (key_obj == NULL) {
827+
*result = NULL;
828+
return -1;
829+
}
830+
res = PyDict_GetItemRef(mp, key_obj, result);
831+
Py_DECREF(key_obj);
832+
return res;
833+
}
834+
#endif
835+
836+
794837
// gh-106307 added PyModule_Add() to Python 3.13.0a1
795838
#if PY_VERSION_HEX < 0x030D00A1
796839
PYCAPI_COMPAT_STATIC_INLINE(int)

tests/test_pythoncapi_compat_cext.c

+108
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,113 @@ test_getitem(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
10861086
}
10871087

10881088

1089+
static PyObject *
1090+
test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
1091+
{
1092+
assert(!PyErr_Occurred());
1093+
1094+
PyObject *dict = NULL, *key = NULL, *missing_key = NULL, *value = NULL;
1095+
PyObject *invalid_key = NULL;
1096+
PyObject *invalid_dict = NULL;
1097+
PyObject *get_value = NULL;
1098+
int res;
1099+
1100+
// test PyDict_New()
1101+
dict = PyDict_New();
1102+
if (dict == NULL) {
1103+
goto error;
1104+
}
1105+
1106+
key = PyUnicode_FromString("key");
1107+
if (key == NULL) {
1108+
goto error;
1109+
}
1110+
1111+
missing_key = PyUnicode_FromString("missing_key");
1112+
if (missing_key == NULL) {
1113+
goto error;
1114+
}
1115+
1116+
value = PyUnicode_FromString("value");
1117+
if (value == NULL) {
1118+
goto error;
1119+
}
1120+
1121+
res = PyDict_SetItemString(dict, "key", value);
1122+
if (res < 0) {
1123+
goto error;
1124+
}
1125+
assert(res == 0);
1126+
1127+
// test PyDict_GetItemRef(), key is present
1128+
get_value = Py_Ellipsis; // marker value
1129+
assert(PyDict_GetItemRef(dict, key, &get_value) == 1);
1130+
assert(get_value == value);
1131+
Py_DECREF(get_value);
1132+
1133+
// test PyDict_GetItemStringRef(), key is present
1134+
get_value = Py_Ellipsis; // marker value
1135+
assert(PyDict_GetItemStringRef(dict, "key", &get_value) == 1);
1136+
assert(get_value == value);
1137+
Py_DECREF(get_value);
1138+
1139+
// test PyDict_GetItemRef(), missing key
1140+
get_value = Py_Ellipsis; // marker value
1141+
assert(PyDict_GetItemRef(dict, missing_key, &get_value) == 0);
1142+
assert(!PyErr_Occurred());
1143+
assert(get_value == NULL);
1144+
1145+
// test PyDict_GetItemStringRef(), missing key
1146+
get_value = Py_Ellipsis; // marker value
1147+
assert(PyDict_GetItemStringRef(dict, "missing_key", &get_value) == 0);
1148+
assert(!PyErr_Occurred());
1149+
assert(get_value == NULL);
1150+
1151+
// test PyDict_GetItemRef(), invalid dict
1152+
invalid_dict = key; // borrowed reference
1153+
get_value = Py_Ellipsis; // marker value
1154+
assert(PyDict_GetItemRef(invalid_dict, key, &get_value) == -1);
1155+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1156+
PyErr_Clear();
1157+
assert(get_value == NULL);
1158+
1159+
// test PyDict_GetItemStringRef(), invalid dict
1160+
get_value = Py_Ellipsis; // marker value
1161+
assert(PyDict_GetItemStringRef(invalid_dict, "key", &get_value) == -1);
1162+
assert(PyErr_ExceptionMatches(PyExc_SystemError));
1163+
PyErr_Clear();
1164+
assert(get_value == NULL);
1165+
1166+
invalid_key = PyList_New(0); // not hashable key
1167+
if (invalid_key == NULL) {
1168+
goto error;
1169+
}
1170+
1171+
// test PyDict_GetItemRef(), invalid key
1172+
get_value = Py_Ellipsis; // marker value
1173+
assert(PyDict_GetItemRef(dict, invalid_key, &get_value) == -1);
1174+
assert(PyErr_ExceptionMatches(PyExc_TypeError));
1175+
PyErr_Clear();
1176+
assert(get_value == NULL);
1177+
1178+
Py_DECREF(dict);
1179+
Py_DECREF(key);
1180+
Py_DECREF(missing_key);
1181+
Py_DECREF(value);
1182+
Py_DECREF(invalid_key);
1183+
1184+
Py_RETURN_NONE;
1185+
1186+
error:
1187+
Py_XDECREF(dict);
1188+
Py_XDECREF(key);
1189+
Py_XDECREF(missing_key);
1190+
Py_XDECREF(value);
1191+
Py_XDECREF(invalid_key);
1192+
return NULL;
1193+
}
1194+
1195+
10891196
static struct PyMethodDef methods[] = {
10901197
{"test_object", test_object, METH_NOARGS, _Py_NULL},
10911198
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1110,6 +1217,7 @@ static struct PyMethodDef methods[] = {
11101217
{"test_vectorcall", test_vectorcall, METH_NOARGS, _Py_NULL},
11111218
{"test_getattr", test_getattr, METH_NOARGS, _Py_NULL},
11121219
{"test_getitem", test_getitem, METH_NOARGS, _Py_NULL},
1220+
{"test_dict_getitemref", test_dict_getitemref, METH_NOARGS, _Py_NULL},
11131221
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
11141222
};
11151223

0 commit comments

Comments
 (0)