From 81098119e2e28c52e31eb4f6ba116767b3381b53 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 25 Aug 2023 00:53:00 +0200 Subject: [PATCH] Add PyLong_AsInt() function (#72) --- docs/api.rst | 8 ++++++++ docs/changelog.rst | 1 + pythoncapi_compat.h | 22 +++++++++++++++++++++ tests/test_pythoncapi_compat_cext.c | 30 +++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 6f15f9c..693f0be 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -75,6 +75,14 @@ Python 3.13 See `Py_IsFinalizing() documentation `__. +.. c:function:: int PyDict_ContainsString(PyObject *p, const char *key) + + See `PyDict_ContainsString() documentation `__. + +.. c:function:: int PyLong_AsInt(PyObject *obj) + + See `PyLong_AsInt() documentation `__. + Python 3.12 ----------- diff --git a/docs/changelog.rst b/docs/changelog.rst index 477d038..bab8e65 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,7 @@ Changelog ========= +* 2023-08-25: Add ``PyDict_ContainsString()`` and ``PyLong_AsInt()`` functions. * 2023-08-21: Remove support for Python 2.7, Python 3.4 and older. * 2023-08-16: Add ``Py_IsFinalizing()`` function. * 2023-07-21: Add ``PyDict_GetItemRef()`` function. diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index ae0a1a4..4219db7 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -834,6 +834,28 @@ static inline int PyDict_ContainsString(PyObject *op, const char *key) #endif +// gh-108445 added PyLong_AsInt() to Python 3.13.0a1 +#if PY_VERSION_HEX < 0x030D00A1 +static inline int PyLong_AsInt(PyObject *obj) +{ +#ifdef PYPY_VERSION + long value = PyLong_AsLong(obj); + if (value == -1 && PyErr_Occurred()) { + return -1; + } + if (value < (long)INT_MIN || (long)INT_MAX < value) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C int"); + return -1; + } + return (int)value; +#else + return _PyLong_AsInt(obj); +#endif +} +#endif + + #ifdef __cplusplus } #endif diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index 0f6dae6..da5c785 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -1209,6 +1209,35 @@ test_dict_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) } +static PyObject * +test_long_api(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) +{ + // test PyLong_AsInt() + assert(!PyErr_Occurred()); + PyObject *obj = PyLong_FromLong(123); + if (obj == NULL) { + return NULL; + } + int value = PyLong_AsInt(obj); + assert(value == 123); + assert(!PyErr_Occurred()); + Py_DECREF(obj); + + // test PyLong_AsInt() with overflow + PyObject *obj2 = PyLong_FromLongLong((long long)INT_MAX + 1); + if (obj2 == NULL) { + return NULL; + } + value = PyLong_AsInt(obj2); + assert(value == -1); + assert(PyErr_ExceptionMatches(PyExc_OverflowError)); + PyErr_Clear(); + Py_DECREF(obj2); + + Py_RETURN_NONE; +} + + static struct PyMethodDef methods[] = { {"test_object", test_object, METH_NOARGS, _Py_NULL}, {"test_py_is", test_py_is, METH_NOARGS, _Py_NULL}, @@ -1234,6 +1263,7 @@ static struct PyMethodDef methods[] = { {"test_getattr", test_getattr, METH_NOARGS, _Py_NULL}, {"test_getitem", test_getitem, METH_NOARGS, _Py_NULL}, {"test_dict_api", test_dict_api, METH_NOARGS, _Py_NULL}, + {"test_long_api", test_long_api, METH_NOARGS, _Py_NULL}, {_Py_NULL, _Py_NULL, 0, _Py_NULL} };