]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-108444: Add PyLong_AsInt() public function (#108445)
authorVictor Stinner <vstinner@python.org>
Thu, 24 Aug 2023 21:55:30 +0000 (23:55 +0200)
committerGitHub <noreply@github.com>
Thu, 24 Aug 2023 21:55:30 +0000 (23:55 +0200)
* Rename _PyLong_AsInt() to PyLong_AsInt().
* Add documentation.
* Add test.
* For now, keep _PyLong_AsInt() as an alias to PyLong_AsInt().

13 files changed:
Doc/c-api/long.rst
Doc/data/stable_abi.dat
Doc/whatsnew/3.13.rst
Include/cpython/longobject.h
Include/longobject.h
Lib/test/test_capi/test_long.py
Lib/test/test_stable_abi_ctypes.py
Misc/NEWS.d/next/C API/2023-08-24-20-08-02.gh-issue-108014.20DOSS.rst [new file with mode: 0644]
Misc/stable_abi.toml
Modules/_testcapi/clinic/long.c.h
Modules/_testcapi/long.c
Objects/longobject.c
PC/python3dll.c

index f1354a34f2b2d525374bbfb5decac43c21587a3c..045604870d3c8405da3c0c83deaa8319f10b8221 100644 (file)
@@ -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
index ed415a4dc644a43d106c2349db3ba180881ce329..cc6349a0330e8cf42a19869f651830c193c620bc 100644 (file)
@@ -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,,
index 25eb5e981c55e132c86bb62bfb09e692eb225cb8..4ff12b16d00266b7d1f2fa04726878d9897cf3e5 100644 (file)
@@ -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
 ----------------------
 
index c581f51cbcdf3898861fdced337792710cc460ae..c96f35188c47fee0ae05cda25cd8cd6b131de480 100644 (file)
@@ -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 *);
index e559e238ae5a3555aa103a53a3c1262c616e3639..7393254cd24a9bf5feee46b66c005ebb6e57f21d 100644 (file)
@@ -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
index 8928fd94a1d6a3f287bd4ebded43efb523deb1e2..101fe1f0de77f105d82a1595e1b9a03479bf021a 100644 (file)
@@ -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()
index 566d36a3f5ba11fb7d9b694be087ac0a7f386fde..1f3cf612c18f6d152b176988a83030e2ac9bb5e3 100644 (file)
@@ -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 (file)
index 0000000..5c1b04f
--- /dev/null
@@ -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.
index 16d5c1a07ae3e22a4bfe729317e61cf1ff19bb06..2030a085abf27cd6ac651ce938fb54ca2a3a6328 100644 (file)
     added = '3.13'
 [function.PyDict_GetItemStringRef]
     added = '3.13'
+[function.PyLong_AsInt]
+    added = '3.13'
index 95885e0ae17a6cc098ec880e166740fc84421762..87bba4cfacfe55752b3658c1a093cf03a135913c 100644 (file)
@@ -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]*/
index ede43f60d06f9593a844c9ae80665cad1860a117..6b74e0ab8e0d1cc2651a6f42c9b390bbd7393e00 100644 (file)
@@ -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},
 };
 
index 354cba9d6d800fa3a67d1bb2885a1db743533f08..d20ef412367bb75b73b0fc2e250a4db389584c05 100644 (file)
@@ -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);
index 64dfbba3e424a174e1bf5bcd19eca1a0901365a8..ee3a7d7b4e5be1107e946a659c40bf90bc791e8d 100755 (executable)
@@ -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)