diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index 7586485..ae0a1a4 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -819,6 +819,21 @@ static inline int Py_IsFinalizing(void) #endif +// gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyDict_ContainsString(PyObject *op, const char *key) +{ + PyObject *key_obj = PyUnicode_FromString(key); + if (key_obj == NULL) { + return -1; + } + int res = PyDict_Contains(op, key_obj); + Py_DECREF(key_obj); + return res; +} +#endif + + #ifdef __cplusplus } #endif diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index 0213556..0f6dae6 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -1092,7 +1092,7 @@ test_getitem(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) static PyObject * -test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +test_dict_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { assert(!PyErr_Occurred()); @@ -1112,12 +1112,18 @@ test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) if (key == NULL) { goto error; } + invalid_dict = key; // borrowed reference missing_key = PyUnicode_FromString("missing_key"); if (missing_key == NULL) { goto error; } + invalid_key = PyList_New(0); // not hashable key + if (invalid_key == NULL) { + goto error; + } + value = PyUnicode_FromString("value"); if (value == NULL) { goto error; @@ -1129,6 +1135,17 @@ test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) } assert(res == 0); + // test PyDict_Contains() + assert(PyDict_Contains(dict, key) == 1); + assert(PyDict_Contains(dict, missing_key) == 0); + + // test PyDict_ContainsString() + assert(PyDict_ContainsString(dict, "key") == 1); + assert(PyDict_ContainsString(dict, "missing_key") == 0); + assert(PyDict_ContainsString(dict, "\xff") == -1); + assert(PyErr_ExceptionMatches(PyExc_UnicodeDecodeError)); + PyErr_Clear(); + // test PyDict_GetItemRef(), key is present get_value = Py_Ellipsis; // marker value assert(PyDict_GetItemRef(dict, key, &get_value) == 1); @@ -1154,7 +1171,6 @@ test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) assert(get_value == NULL); // test PyDict_GetItemRef(), invalid dict - invalid_dict = key; // borrowed reference get_value = Py_Ellipsis; // marker value assert(PyDict_GetItemRef(invalid_dict, key, &get_value) == -1); assert(PyErr_ExceptionMatches(PyExc_SystemError)); @@ -1168,11 +1184,6 @@ test_dict_getitemref(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) PyErr_Clear(); assert(get_value == NULL); - invalid_key = PyList_New(0); // not hashable key - if (invalid_key == NULL) { - goto error; - } - // test PyDict_GetItemRef(), invalid key get_value = Py_Ellipsis; // marker value assert(PyDict_GetItemRef(dict, invalid_key, &get_value) == -1); @@ -1222,7 +1233,7 @@ static struct PyMethodDef methods[] = { {"test_vectorcall", test_vectorcall, METH_NOARGS, _Py_NULL}, {"test_getattr", test_getattr, METH_NOARGS, _Py_NULL}, {"test_getitem", test_getitem, METH_NOARGS, _Py_NULL}, - {"test_dict_getitemref", test_dict_getitemref, METH_NOARGS, _Py_NULL}, + {"test_dict_api", test_dict_api, METH_NOARGS, _Py_NULL}, {_Py_NULL, _Py_NULL, 0, _Py_NULL} };