Skip to content

Commit 7515dae

Browse files
committed
Add PyMapping_GetOptionalItem() function
1 parent 157d6d4 commit 7515dae

File tree

4 files changed

+101
-5
lines changed

4 files changed

+101
-5
lines changed

docs/api.rst

+8
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ Python 3.13
4343
4444
See `PyObject_GetOptionalAttrString() documentation <https://docs.python.org/dev/c-api/object.html#c.PyObject_GetOptionalAttrString>`__.
4545
46+
.. c:function:: int PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)
47+
48+
See `PyMapping_GetOptionalItem() documentation <https://docs.python.org/dev/c-api/mapping.html#c.PyMapping_GetOptionalItem>`__.
49+
50+
.. c:function:: int PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result)
51+
52+
See `PyMapping_GetOptionalItemString() documentation <https://docs.python.org/dev/c-api/mapping.html#c.PyMapping_GetOptionalItemString>`__.
53+
4654
4755
Python 3.12
4856
-----------

docs/changelog.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
Changelog
22
=========
33

4-
* 2023-07-12: Add ``PyObject_GetOptionalAttr()`` and
5-
``PyObject_GetOptionalAttrString()`` functions.
4+
* 2023-07-12: Add ``PyObject_GetOptionalAttr()``,
5+
``PyObject_GetOptionalAttrString()``,
6+
``PyMapping_GetOptionalItem()``
7+
and ``PyMapping_GetOptionalItemString()`` functions.
68
* 2023-07-05: Add ``PyObject_Vectorcall()`` function.
79
* 2023-06-21: Add ``PyWeakref_GetRef()`` function.
810
* 2023-06-20: Add ``PyImport_AddModuleRef()`` function.

pythoncapi_compat.h

+44-3
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,11 @@ PYCAPI_COMPAT_STATIC_INLINE(PyObject*)
284284
PyFrame_GetVarString(PyFrameObject *frame, const char *name)
285285
{
286286
PyObject *name_obj, *value;
287+
#if PY_VERSION_HEX >= 0x03000000
287288
name_obj = PyUnicode_FromString(name);
289+
#else
290+
name_obj = PyString_FromString(name);
291+
#endif
288292
if (name_obj == NULL) {
289293
return NULL;
290294
}
@@ -723,9 +727,10 @@ PyObject_GetOptionalAttr(PyObject *obj, PyObject *name, PyObject **result)
723727
}
724728

725729
PYCAPI_COMPAT_STATIC_INLINE(int)
726-
PyObject_GetOptionalAttrString(PyObject *obj, const char *name, PyObject **presult)
730+
PyObject_GetOptionalAttrString(PyObject *obj, const char *name, PyObject **result)
727731
{
728732
PyObject *name_obj;
733+
int rc;
729734
#if PY_VERSION_HEX >= 0x03000000
730735
name_obj = PyUnicode_FromString(name);
731736
#else
@@ -734,9 +739,45 @@ PyObject_GetOptionalAttrString(PyObject *obj, const char *name, PyObject **presu
734739
if (name_obj == NULL) {
735740
return -1;
736741
}
737-
int res = PyObject_GetOptionalAttr(obj, name_obj, presult);
742+
rc = PyObject_GetOptionalAttr(obj, name_obj, result);
738743
Py_DECREF(name_obj);
739-
return res;
744+
return rc;
745+
}
746+
#endif
747+
748+
749+
// gh-106307 added PyObject_GetOptionalAttr() to Python 3.13.0a1
750+
#if PY_VERSION_HEX < 0x030D00A1
751+
PYCAPI_COMPAT_STATIC_INLINE(int)
752+
PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)
753+
{
754+
*result = PyObject_GetItem(obj, key);
755+
if (*result) {
756+
return 1;
757+
}
758+
if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
759+
return -1;
760+
}
761+
PyErr_Clear();
762+
return 0;
763+
}
764+
765+
PYCAPI_COMPAT_STATIC_INLINE(int)
766+
PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result)
767+
{
768+
PyObject *key_obj;
769+
int rc;
770+
#if PY_VERSION_HEX >= 0x03000000
771+
key_obj = PyUnicode_FromString(key);
772+
#else
773+
key_obj = PyString_FromString(key);
774+
#endif
775+
if (key_obj == NULL) {
776+
return -1;
777+
}
778+
rc = PyMapping_GetOptionalItem(obj, key_obj, result);
779+
Py_DECREF(key_obj);
780+
return rc;
740781
}
741782
#endif
742783

tests/test_pythoncapi_compat_cext.c

+45
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,50 @@ test_getattr(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
990990
}
991991

992992

993+
static PyObject *
994+
test_getitem(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
995+
{
996+
assert(!PyErr_Occurred());
997+
998+
PyObject *value = Py_BuildValue("s", "value");
999+
assert(value != NULL);
1000+
PyObject *obj = Py_BuildValue("{sO}", "key", value);
1001+
assert(obj != NULL);
1002+
PyObject *key;
1003+
PyObject *item;
1004+
1005+
// test PyMapping_GetOptionalItem(): key is present
1006+
key = create_string("key");
1007+
item = Py_True; // marker value
1008+
assert(PyMapping_GetOptionalItem(obj, key, &item) == 1);
1009+
assert(item == value);
1010+
Py_DECREF(item);
1011+
Py_DECREF(key);
1012+
1013+
// test PyMapping_GetOptionalItemString(): key is present
1014+
item = Py_True; // marker value
1015+
assert(PyMapping_GetOptionalItemString(obj, "key", &item) == 1);
1016+
assert(item == value);
1017+
Py_DECREF(item);
1018+
1019+
// test PyMapping_GetOptionalItem(): missing key
1020+
key = create_string("dontexist");
1021+
item = Py_True; // marker value
1022+
assert(PyMapping_GetOptionalItem(obj, key, &item) == 0);
1023+
assert(item == NULL);
1024+
Py_DECREF(key);
1025+
1026+
// test PyMapping_GetOptionalItemString(): missing key
1027+
item = Py_True; // marker value
1028+
assert(PyMapping_GetOptionalItemString(obj, "dontexist", &item) == 0);
1029+
assert(item == NULL);
1030+
1031+
Py_DECREF(obj);
1032+
Py_DECREF(value);
1033+
Py_RETURN_NONE;
1034+
}
1035+
1036+
9931037
static struct PyMethodDef methods[] = {
9941038
{"test_object", test_object, METH_NOARGS, _Py_NULL},
9951039
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
@@ -1013,6 +1057,7 @@ static struct PyMethodDef methods[] = {
10131057
{"func_varargs", (PyCFunction)(void*)func_varargs, METH_VARARGS | METH_KEYWORDS, _Py_NULL},
10141058
{"test_vectorcall", test_vectorcall, METH_NOARGS, _Py_NULL},
10151059
{"test_getattr", test_getattr, METH_NOARGS, _Py_NULL},
1060+
{"test_getitem", test_getitem, METH_NOARGS, _Py_NULL},
10161061
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
10171062
};
10181063

0 commit comments

Comments
 (0)