summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2023-08-24 21:55:30 (GMT)
committerGitHub <noreply@github.com>2023-08-24 21:55:30 (GMT)
commitbe436e08b8bd9fcd2202d6ce4d924bba7551e96f (patch)
treedf142099ae50383af0e76ed09f5b4d8d1cc15d3e
parentfeb9a49c9c09d08cb8c24cb74d90a218de6af244 (diff)
downloadcpython-be436e08b8bd9fcd2202d6ce4d924bba7551e96f.zip
cpython-be436e08b8bd9fcd2202d6ce4d924bba7551e96f.tar.gz
cpython-be436e08b8bd9fcd2202d6ce4d924bba7551e96f.tar.bz2
gh-108444: Add PyLong_AsInt() public function (#108445)
* Rename _PyLong_AsInt() to PyLong_AsInt(). * Add documentation. * Add test. * For now, keep _PyLong_AsInt() as an alias to PyLong_AsInt().
-rw-r--r--Doc/c-api/long.rst8
-rw-r--r--Doc/data/stable_abi.dat1
-rw-r--r--Doc/whatsnew/3.13.rst6
-rw-r--r--Include/cpython/longobject.h3
-rw-r--r--Include/longobject.h6
-rw-r--r--Lib/test/test_capi/test_long.py30
-rw-r--r--Lib/test/test_stable_abi_ctypes.py1
-rw-r--r--Misc/NEWS.d/next/C API/2023-08-24-20-08-02.gh-issue-108014.20DOSS.rst4
-rw-r--r--Misc/stable_abi.toml2
-rw-r--r--Modules/_testcapi/clinic/long.c.h10
-rw-r--r--Modules/_testcapi/long.c25
-rw-r--r--Objects/longobject.c2
-rwxr-xr-xPC/python3dll.c1
13 files changed, 96 insertions, 3 deletions
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index f1354a3..0456048 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -136,6 +136,14 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
This function will no longer use :meth:`~object.__int__`.
+.. c:function:: int PyLong_AsInt(PyObject *obj)
+
+ Similar to :c:func:`PyLong_AsLong`, but store the result in a C
+ :c:expr:`int` instead of a C :c:expr:`long`.
+
+ .. versionadded:: 3.13
+
+
.. c:function:: long PyLong_AsLongAndOverflow(PyObject *obj, int *overflow)
Return a C :c:expr:`long` representation of *obj*. If *obj* is not an
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index ed415a4..cc6349a 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -347,6 +347,7 @@ var,PyList_Type,3.2,,
type,PyLongObject,3.2,,opaque
var,PyLongRangeIter_Type,3.2,,
function,PyLong_AsDouble,3.2,,
+function,PyLong_AsInt,3.13,,
function,PyLong_AsLong,3.2,,
function,PyLong_AsLongAndOverflow,3.2,,
function,PyLong_AsLongLong,3.2,,
diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst
index 25eb5e9..4ff12b1 100644
--- a/Doc/whatsnew/3.13.rst
+++ b/Doc/whatsnew/3.13.rst
@@ -871,6 +871,12 @@ New Features
:term:`shutting down <interpreter shutdown>`.
(Contributed by Victor Stinner in :gh:`108014`.)
+* Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
+ store the result in a C :c:expr:`int` instead of a C :c:expr:`long`.
+ Previously, it was known as the private function :c:func:`!_PyLong_AsInt`
+ (with an underscore prefix).
+ (Contributed by Victor Stinner in :gh:`108014`.)
+
Porting to Python 3.13
----------------------
diff --git a/Include/cpython/longobject.h b/Include/cpython/longobject.h
index c581f51..c96f351 100644
--- a/Include/cpython/longobject.h
+++ b/Include/cpython/longobject.h
@@ -2,7 +2,8 @@
# error "this header file must not be included directly"
#endif
-PyAPI_FUNC(int) _PyLong_AsInt(PyObject *);
+// Alias for backport compatibility
+#define _PyLong_AsInt PyLong_AsInt
PyAPI_FUNC(int) _PyLong_UnsignedShort_Converter(PyObject *, void *);
PyAPI_FUNC(int) _PyLong_UnsignedInt_Converter(PyObject *, void *);
diff --git a/Include/longobject.h b/Include/longobject.h
index e559e23..7393254 100644
--- a/Include/longobject.h
+++ b/Include/longobject.h
@@ -18,12 +18,18 @@ PyAPI_FUNC(PyObject *) PyLong_FromUnsignedLong(unsigned long);
PyAPI_FUNC(PyObject *) PyLong_FromSize_t(size_t);
PyAPI_FUNC(PyObject *) PyLong_FromSsize_t(Py_ssize_t);
PyAPI_FUNC(PyObject *) PyLong_FromDouble(double);
+
PyAPI_FUNC(long) PyLong_AsLong(PyObject *);
PyAPI_FUNC(long) PyLong_AsLongAndOverflow(PyObject *, int *);
PyAPI_FUNC(Py_ssize_t) PyLong_AsSsize_t(PyObject *);
PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *);
PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *);
+
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
+PyAPI_FUNC(int) PyLong_AsInt(PyObject *);
+#endif
+
PyAPI_FUNC(PyObject *) PyLong_GetInfo(void);
/* It may be useful in the future. I've added it in the PyInt -> PyLong
diff --git a/Lib/test/test_capi/test_long.py b/Lib/test/test_capi/test_long.py
index 8928fd9..101fe1f 100644
--- a/Lib/test/test_capi/test_long.py
+++ b/Lib/test/test_capi/test_long.py
@@ -34,6 +34,36 @@ class LongTests(unittest.TestCase):
self.assertEqual(_testcapi.call_long_compact_api(sys.maxsize),
(False, -1))
+ def test_long_asint(self):
+ PyLong_AsInt = _testcapi.PyLong_AsInt
+ INT_MIN = _testcapi.INT_MIN
+ INT_MAX = _testcapi.INT_MAX
+
+ # round trip (object -> int -> object)
+ for value in (INT_MIN, INT_MAX, -1, 0, 1, 123):
+ with self.subTest(value=value):
+ self.assertEqual(PyLong_AsInt(value), value)
+
+ # use __index__(), not __int__()
+ class MyIndex:
+ def __index__(self):
+ return 10
+ def __int__(self):
+ return 22
+ self.assertEqual(PyLong_AsInt(MyIndex()), 10)
+
+ # bound checking
+ with self.assertRaises(OverflowError):
+ PyLong_AsInt(INT_MIN - 1)
+ with self.assertRaises(OverflowError):
+ PyLong_AsInt(INT_MAX + 1)
+
+ # invalid type
+ for value in (1.0, b'2', '3'):
+ with self.subTest(value=value):
+ with self.assertRaises(TypeError):
+ PyLong_AsInt(value)
+
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py
index 566d36a..1f3cf61 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -375,6 +375,7 @@ SYMBOL_NAMES = (
"PyList_Type",
"PyLongRangeIter_Type",
"PyLong_AsDouble",
+ "PyLong_AsInt",
"PyLong_AsLong",
"PyLong_AsLongAndOverflow",
"PyLong_AsLongLong",
diff --git a/Misc/NEWS.d/next/C API/2023-08-24-20-08-02.gh-issue-108014.20DOSS.rst b/Misc/NEWS.d/next/C API/2023-08-24-20-08-02.gh-issue-108014.20DOSS.rst
new file mode 100644
index 0000000..5c1b04f
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2023-08-24-20-08-02.gh-issue-108014.20DOSS.rst
@@ -0,0 +1,4 @@
+Add :c:func:`PyLong_AsInt` function: similar to :c:func:`PyLong_AsLong`, but
+store the result in a C :c:expr:`int` instead of a C :c:expr:`long`.
+Previously, it was known as the the private function :c:func:`!_PyLong_AsInt`
+(with an underscore prefix). Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index 16d5c1a..2030a08 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2450,3 +2450,5 @@
added = '3.13'
[function.PyDict_GetItemStringRef]
added = '3.13'
+[function.PyLong_AsInt]
+ added = '3.13'
diff --git a/Modules/_testcapi/clinic/long.c.h b/Modules/_testcapi/clinic/long.c.h
index 95885e0..87bba4c 100644
--- a/Modules/_testcapi/clinic/long.c.h
+++ b/Modules/_testcapi/clinic/long.c.h
@@ -163,4 +163,12 @@ PyDoc_STRVAR(_testcapi_call_long_compact_api__doc__,
#define _TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF \
{"call_long_compact_api", (PyCFunction)_testcapi_call_long_compact_api, METH_O, _testcapi_call_long_compact_api__doc__},
-/*[clinic end generated code: output=d000a1b58fa81eab input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(_testcapi_PyLong_AsInt__doc__,
+"PyLong_AsInt($module, arg, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYLONG_ASINT_METHODDEF \
+ {"PyLong_AsInt", (PyCFunction)_testcapi_PyLong_AsInt, METH_O, _testcapi_PyLong_AsInt__doc__},
+/*[clinic end generated code: output=1631a18f1193486a input=a9049054013a1b77]*/
diff --git a/Modules/_testcapi/long.c b/Modules/_testcapi/long.c
index ede43f6..6b74e0a 100644
--- a/Modules/_testcapi/long.c
+++ b/Modules/_testcapi/long.c
@@ -37,6 +37,9 @@ raise_test_long_error(const char* msg)
return raiseTestError("test_long_api", msg);
}
+// Test PyLong_FromLong()/PyLong_AsLong()
+// and PyLong_FromUnsignedLong()/PyLong_AsUnsignedLong().
+
#define TESTNAME test_long_api_inner
#define TYPENAME long
#define F_S_TO_PY PyLong_FromLong
@@ -64,6 +67,9 @@ _testcapi_test_long_api_impl(PyObject *module)
#undef F_U_TO_PY
#undef F_PY_TO_U
+// Test PyLong_FromLongLong()/PyLong_AsLongLong()
+// and PyLong_FromUnsignedLongLong()/PyLong_AsUnsignedLongLong().
+
static PyObject *
raise_test_longlong_error(const char* msg)
{
@@ -595,6 +601,24 @@ _testcapi_call_long_compact_api(PyObject *module, PyObject *arg)
return Py_BuildValue("in", is_compact, value);
}
+/*[clinic input]
+_testcapi.PyLong_AsInt
+ arg: object
+ /
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_PyLong_AsInt(PyObject *module, PyObject *arg)
+/*[clinic end generated code: output=0df9f19de5fa575b input=9561b97105493a67]*/
+{
+ assert(!PyErr_Occurred());
+ int value = PyLong_AsInt(arg);
+ if (value == -1 && PyErr_Occurred()) {
+ return NULL;
+ }
+ return PyLong_FromLong(value);
+}
+
static PyMethodDef test_methods[] = {
_TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
_TESTCAPI_TEST_LONG_API_METHODDEF
@@ -605,6 +629,7 @@ static PyMethodDef test_methods[] = {
_TESTCAPI_TEST_LONG_NUMBITS_METHODDEF
_TESTCAPI_TEST_LONGLONG_API_METHODDEF
_TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF
+ _TESTCAPI_PYLONG_ASINT_METHODDEF
{NULL},
};
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 354cba9..d20ef41 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -549,7 +549,7 @@ PyLong_AsLong(PyObject *obj)
method. Return -1 and set an error if overflow occurs. */
int
-_PyLong_AsInt(PyObject *obj)
+PyLong_AsInt(PyObject *obj)
{
int overflow;
long result = PyLong_AsLongAndOverflow(obj, &overflow);
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 64dfbba..ee3a7d7 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -331,6 +331,7 @@ EXPORT_FUNC(PyList_SetSlice)
EXPORT_FUNC(PyList_Size)
EXPORT_FUNC(PyList_Sort)
EXPORT_FUNC(PyLong_AsDouble)
+EXPORT_FUNC(PyLong_AsInt)
EXPORT_FUNC(PyLong_AsLong)
EXPORT_FUNC(PyLong_AsLongAndOverflow)
EXPORT_FUNC(PyLong_AsLongLong)