https://github.com/python/cpython/commit/4c6dca82925bd4be376a3e4a53c8104ad0b0cb5f
commit: 4c6dca82925bd4be376a3e4a53c8104ad0b0cb5f
branch: main
author: Victor Stinner <[email protected]>
committer: vstinner <[email protected]>
date: 2024-08-28T10:16:13Z
summary:

gh-120389: Add PyLong_FromInt64() and PyLong_AsInt64() (#120390)

Add new functions to convert C <stdint.h> numbers from/to Python int:

* PyLong_FromInt32()
* PyLong_FromUInt32()
* PyLong_FromInt64()
* PyLong_FromUInt64()
* PyLong_AsInt32()
* PyLong_AsUInt32()
* PyLong_AsInt64()
* PyLong_AsUInt64()

files:
A Misc/NEWS.d/next/C API/2024-06-19-17-27-22.gh-issue-120389.GSZeHF.rst
M Doc/c-api/long.rst
M Doc/conf.py
M Doc/data/stable_abi.dat
M Doc/whatsnew/3.14.rst
M Include/longobject.h
M Lib/test/test_capi/test_long.py
M Lib/test/test_stable_abi_ctypes.py
M Misc/stable_abi.toml
M Modules/_testcapimodule.c
M Modules/_testlimitedcapi/long.c
M Objects/longobject.c
M PC/python3dll.c

diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst
index c7c6fe9f942bd8..30133a9c5cfa2f 100644
--- a/Doc/c-api/long.rst
+++ b/Doc/c-api/long.rst
@@ -69,12 +69,32 @@ distinguished from a number.  Use :c:func:`PyErr_Occurred` 
to disambiguate.
    on failure.
 
 
+.. c:function:: PyObject* PyLong_FromInt32(int32_t value)
+                PyObject* PyLong_FromInt64(int64_t value)
+
+   Return a new :c:type:`PyLongObject` object from a signed C
+   :c:expr:`int32_t` or :c:expr:`int64_t`, or ``NULL``
+   with an exception set on failure.
+
+   .. versionadded:: 3.14
+
+
 .. c:function:: PyObject* PyLong_FromUnsignedLongLong(unsigned long long v)
 
    Return a new :c:type:`PyLongObject` object from a C :c:expr:`unsigned long 
long`,
    or ``NULL`` on failure.
 
 
+.. c:function:: PyObject* PyLong_FromUInt32(uint32_t value)
+                PyObject* PyLong_FromUInt64(uint64_t value)
+
+   Return a new :c:type:`PyLongObject` object from an unsigned C
+   :c:expr:`uint32_t` or :c:expr:`uint64_t`, or ``NULL``
+   with an exception set on failure.
+
+   .. versionadded:: 3.14
+
+
 .. c:function:: PyObject* PyLong_FromDouble(double v)
 
    Return a new :c:type:`PyLongObject` object from the integer part of *v*, or
@@ -337,6 +357,43 @@ distinguished from a number.  Use :c:func:`PyErr_Occurred` 
to disambiguate.
       This function will no longer use :meth:`~object.__int__`.
 
 
+.. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *value)
+                int PyLong_AsInt64(PyObject *obj, int64_t *value)
+
+   Set *\*value* to a signed C :c:expr:`int32_t` or :c:expr:`int64_t`
+   representation of *obj*.
+
+   If the *obj* value is out of range, raise an :exc:`OverflowError`.
+
+   Set *\*value* and return ``0`` on success.
+   Set an exception and return ``-1`` on error.
+
+   *value* must not be ``NULL``.
+
+   .. versionadded:: 3.14
+
+
+.. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *value)
+                int PyLong_AsUInt64(PyObject *obj, uint64_t *value)
+
+   Set *\*value* to an unsigned C :c:expr:`uint32_t` or :c:expr:`uint64_t`
+   representation of *obj*.
+
+   If *obj* is not an instance of :c:type:`PyLongObject`, first call its
+   :meth:`~object.__index__` method (if present) to convert it to a
+   :c:type:`PyLongObject`.
+
+   * If *obj* is negative, raise a :exc:`ValueError`.
+   * If the *obj* value is out of range, raise an :exc:`OverflowError`.
+
+   Set *\*value* and return ``0`` on success.
+   Set an exception and return ``-1`` on error.
+
+   *value* must not be ``NULL``.
+
+   .. versionadded:: 3.14
+
+
 .. c:function:: double PyLong_AsDouble(PyObject *pylong)
 
    Return a C :c:expr:`double` representation of *pylong*.  *pylong* must be
diff --git a/Doc/conf.py b/Doc/conf.py
index dc6ea6ad2b1bb6..6281723015435d 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -141,6 +141,7 @@
     ('c:type', 'size_t'),
     ('c:type', 'ssize_t'),
     ('c:type', 'time_t'),
+    ('c:type', 'uint32_t'),
     ('c:type', 'uint64_t'),
     ('c:type', 'uintmax_t'),
     ('c:type', 'uintptr_t'),
diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat
index 592e3465824893..7eeee270bb7f32 100644
--- a/Doc/data/stable_abi.dat
+++ b/Doc/data/stable_abi.dat
@@ -356,23 +356,31 @@ type,PyLongObject,3.2,,opaque
 data,PyLongRangeIter_Type,3.2,,
 func,PyLong_AsDouble,3.2,,
 func,PyLong_AsInt,3.13,,
+func,PyLong_AsInt32,3.14,,
+func,PyLong_AsInt64,3.14,,
 func,PyLong_AsLong,3.2,,
 func,PyLong_AsLongAndOverflow,3.2,,
 func,PyLong_AsLongLong,3.2,,
 func,PyLong_AsLongLongAndOverflow,3.2,,
 func,PyLong_AsSize_t,3.2,,
 func,PyLong_AsSsize_t,3.2,,
+func,PyLong_AsUInt32,3.14,,
+func,PyLong_AsUInt64,3.14,,
 func,PyLong_AsUnsignedLong,3.2,,
 func,PyLong_AsUnsignedLongLong,3.2,,
 func,PyLong_AsUnsignedLongLongMask,3.2,,
 func,PyLong_AsUnsignedLongMask,3.2,,
 func,PyLong_AsVoidPtr,3.2,,
 func,PyLong_FromDouble,3.2,,
+func,PyLong_FromInt32,3.14,,
+func,PyLong_FromInt64,3.14,,
 func,PyLong_FromLong,3.2,,
 func,PyLong_FromLongLong,3.2,,
 func,PyLong_FromSize_t,3.2,,
 func,PyLong_FromSsize_t,3.2,,
 func,PyLong_FromString,3.2,,
+func,PyLong_FromUInt32,3.14,,
+func,PyLong_FromUInt64,3.14,,
 func,PyLong_FromUnsignedLong,3.2,,
 func,PyLong_FromUnsignedLongLong,3.2,,
 func,PyLong_FromVoidPtr,3.2,,
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index ee7d333e2b208e..ba18991dee89d7 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -471,6 +471,20 @@ New Features
   an interned string and deallocate it during module shutdown.
   (Contribued by Eddie Elizondo in :gh:`113601`.)
 
+* Add new functions to convert C ``<stdint.h>`` numbers from/to Python
+  :class:`int`:
+
+  * :c:func:`PyLong_FromInt32`
+  * :c:func:`PyLong_FromInt64`
+  * :c:func:`PyLong_FromUInt32`
+  * :c:func:`PyLong_FromUInt64`
+  * :c:func:`PyLong_AsInt32`
+  * :c:func:`PyLong_AsInt64`
+  * :c:func:`PyLong_AsUInt32`
+  * :c:func:`PyLong_AsUInt64`
+
+  (Contributed by Victor Stinner in :gh:`120389`.)
+
 Porting to Python 3.14
 ----------------------
 
diff --git a/Include/longobject.h b/Include/longobject.h
index 19104cd9d1bef9..45c0d218c13f2f 100644
--- a/Include/longobject.h
+++ b/Include/longobject.h
@@ -30,6 +30,18 @@ PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject 
*);
 PyAPI_FUNC(int) PyLong_AsInt(PyObject *);
 #endif
 
+#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030e0000
+PyAPI_FUNC(PyObject*) PyLong_FromInt32(int32_t value);
+PyAPI_FUNC(PyObject*) PyLong_FromUInt32(uint32_t value);
+PyAPI_FUNC(PyObject*) PyLong_FromInt64(int64_t value);
+PyAPI_FUNC(PyObject*) PyLong_FromUInt64(uint64_t value);
+
+PyAPI_FUNC(int) PyLong_AsInt32(PyObject *obj, int32_t *value);
+PyAPI_FUNC(int) PyLong_AsUInt32(PyObject *obj, uint32_t *value);
+PyAPI_FUNC(int) PyLong_AsInt64(PyObject *obj, int64_t *value);
+PyAPI_FUNC(int) PyLong_AsUInt64(PyObject *obj, uint64_t *value);
+#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 bdbdd7bcfe0f2a..48e795f97b3544 100644
--- a/Lib/test/test_capi/test_long.py
+++ b/Lib/test/test_capi/test_long.py
@@ -631,6 +631,31 @@ def test_long_getsign(self):
 
         # CRASHES getsign(NULL)
 
+    def test_long_asint32(self):
+        # Test PyLong_AsInt32() and PyLong_FromInt32()
+        to_int32 = _testlimitedcapi.pylong_asint32
+        from _testcapi import INT32_MIN, INT32_MAX
+        self.check_long_asint(to_int32, INT32_MIN, INT32_MAX)
+
+    def test_long_asint64(self):
+        # Test PyLong_AsInt64() and PyLong_FromInt64()
+        as_int64 = _testlimitedcapi.pylong_asint64
+        from _testcapi import INT64_MIN, INT64_MAX
+        self.check_long_asint(as_int64, INT64_MIN, INT64_MAX)
+
+    def test_long_asuint32(self):
+        # Test PyLong_AsUInt32() and PyLong_FromUInt32()
+        as_uint32 = _testlimitedcapi.pylong_asuint32
+        from _testcapi import UINT32_MAX
+        self.check_long_asint(as_uint32, 0, UINT32_MAX,
+                              negative_value_error=ValueError)
+
+    def test_long_asuint64(self):
+        # Test PyLong_AsUInt64() and PyLong_FromUInt64()
+        as_uint64 = _testlimitedcapi.pylong_asuint64
+        from _testcapi import UINT64_MAX
+        self.check_long_asint(as_uint64, 0, UINT64_MAX,
+                              negative_value_error=ValueError)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/Lib/test/test_stable_abi_ctypes.py 
b/Lib/test/test_stable_abi_ctypes.py
index fedad17621cb02..4bca33b7451f80 100644
--- a/Lib/test/test_stable_abi_ctypes.py
+++ b/Lib/test/test_stable_abi_ctypes.py
@@ -391,23 +391,31 @@ def test_windows_feature_macros(self):
     "PyLongRangeIter_Type",
     "PyLong_AsDouble",
     "PyLong_AsInt",
+    "PyLong_AsInt32",
+    "PyLong_AsInt64",
     "PyLong_AsLong",
     "PyLong_AsLongAndOverflow",
     "PyLong_AsLongLong",
     "PyLong_AsLongLongAndOverflow",
     "PyLong_AsSize_t",
     "PyLong_AsSsize_t",
+    "PyLong_AsUInt32",
+    "PyLong_AsUInt64",
     "PyLong_AsUnsignedLong",
     "PyLong_AsUnsignedLongLong",
     "PyLong_AsUnsignedLongLongMask",
     "PyLong_AsUnsignedLongMask",
     "PyLong_AsVoidPtr",
     "PyLong_FromDouble",
+    "PyLong_FromInt32",
+    "PyLong_FromInt64",
     "PyLong_FromLong",
     "PyLong_FromLongLong",
     "PyLong_FromSize_t",
     "PyLong_FromSsize_t",
     "PyLong_FromString",
+    "PyLong_FromUInt32",
+    "PyLong_FromUInt64",
     "PyLong_FromUnsignedLong",
     "PyLong_FromUnsignedLongLong",
     "PyLong_FromVoidPtr",
diff --git a/Misc/NEWS.d/next/C 
API/2024-06-19-17-27-22.gh-issue-120389.GSZeHF.rst b/Misc/NEWS.d/next/C 
API/2024-06-19-17-27-22.gh-issue-120389.GSZeHF.rst
new file mode 100644
index 00000000000000..094c8b00ac6859
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2024-06-19-17-27-22.gh-issue-120389.GSZeHF.rst     
@@ -0,0 +1,13 @@
+Add new functions to convert C ``<stdint.h>`` numbers from/to Python
+:class:`int`:
+
+* :c:func:`PyLong_FromInt32`
+* :c:func:`PyLong_FromUInt32`
+* :c:func:`PyLong_FromInt64`
+* :c:func:`PyLong_FromUInt64`
+* :c:func:`PyLong_AsInt32`
+* :c:func:`PyLong_AsUInt32`
+* :c:func:`PyLong_AsInt64`
+* :c:func:`PyLong_AsUInt64`
+
+Patch by Victor Stinner.
diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml
index c38671e389ac5e..d28b13204e1a3c 100644
--- a/Misc/stable_abi.toml
+++ b/Misc/stable_abi.toml
@@ -2510,3 +2510,19 @@
     added = '3.14'
 [function.PyIter_NextItem]
     added = '3.14'
+[function.PyLong_FromInt32]
+    added = '3.14'
+[function.PyLong_FromUInt32]
+    added = '3.14'
+[function.PyLong_AsInt32]
+    added = '3.14'
+[function.PyLong_AsUInt32]
+    added = '3.14'
+[function.PyLong_FromInt64]
+    added = '3.14'
+[function.PyLong_FromUInt64]
+    added = '3.14'
+[function.PyLong_AsInt64]
+    added = '3.14'
+[function.PyLong_AsUInt64]
+    added = '3.14'
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 981efb9629031b..7ffa87dcd10274 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -4046,6 +4046,12 @@ PyInit__testcapi(void)
 
     PyModule_AddIntConstant(m, "the_number_three", 3);
     PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
+    PyModule_AddObject(m, "INT32_MIN", PyLong_FromInt32(INT32_MIN));
+    PyModule_AddObject(m, "INT32_MAX", PyLong_FromInt32(INT32_MAX));
+    PyModule_AddObject(m, "UINT32_MAX", PyLong_FromUInt32(UINT32_MAX));
+    PyModule_AddObject(m, "INT64_MIN", PyLong_FromInt64(INT64_MIN));
+    PyModule_AddObject(m, "INT64_MAX", PyLong_FromInt64(INT64_MAX));
+    PyModule_AddObject(m, "UINT64_MAX", PyLong_FromUInt64(UINT64_MAX));
 
     if (PyModule_AddIntMacro(m, Py_single_input)) {
         return NULL;
diff --git a/Modules/_testlimitedcapi/long.c b/Modules/_testlimitedcapi/long.c
index 5953009b6ef9b7..b9c35803b423c2 100644
--- a/Modules/_testlimitedcapi/long.c
+++ b/Modules/_testlimitedcapi/long.c
@@ -1,7 +1,7 @@
 #include "pyconfig.h"   // Py_GIL_DISABLED
 #ifndef Py_GIL_DISABLED
-   // Need limited C API 3.13 to test PyLong_AsInt()
-#  define Py_LIMITED_API 0x030d0000
+   // Need limited C API 3.14 to test PyLong_AsInt64()
+#  define Py_LIMITED_API 0x030e0000
 #endif
 
 #include "parts.h"
@@ -758,6 +758,52 @@ pylong_aspid(PyObject *module, PyObject *arg)
 }
 
 
+static PyObject *
+pylong_asint32(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    int32_t value;
+    if (PyLong_AsInt32(arg, &value) < 0) {
+        return NULL;
+    }
+    return PyLong_FromInt32(value);
+}
+
+static PyObject *
+pylong_asuint32(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    uint32_t value;
+    if (PyLong_AsUInt32(arg, &value) < 0) {
+        return NULL;
+    }
+    return PyLong_FromUInt32(value);
+}
+
+
+static PyObject *
+pylong_asint64(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    int64_t value;
+    if (PyLong_AsInt64(arg, &value) < 0) {
+        return NULL;
+    }
+    return PyLong_FromInt64(value);
+}
+
+static PyObject *
+pylong_asuint64(PyObject *module, PyObject *arg)
+{
+    NULLABLE(arg);
+    uint64_t value;
+    if (PyLong_AsUInt64(arg, &value) < 0) {
+        return NULL;
+    }
+    return PyLong_FromUInt64(value);
+}
+
+
 static PyMethodDef test_methods[] = {
     _TESTLIMITEDCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
     _TESTLIMITEDCAPI_TEST_LONG_API_METHODDEF
@@ -785,6 +831,10 @@ static PyMethodDef test_methods[] = {
     {"pylong_asdouble",             pylong_asdouble,            METH_O},
     {"pylong_asvoidptr",            pylong_asvoidptr,           METH_O},
     {"pylong_aspid",                pylong_aspid,               METH_O},
+    {"pylong_asint32",              pylong_asint32,             METH_O},
+    {"pylong_asuint32",             pylong_asuint32,            METH_O},
+    {"pylong_asint64",              pylong_asint64,             METH_O},
+    {"pylong_asuint64",             pylong_asuint64,            METH_O},
     {NULL},
 };
 
diff --git a/Objects/longobject.c b/Objects/longobject.c
index 7239d685fdf740..fde98418920673 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -6728,3 +6728,69 @@ Py_ssize_t
 PyUnstable_Long_CompactValue(const PyLongObject* op) {
     return _PyLong_CompactValue((PyLongObject*)op);
 }
+
+PyObject* PyLong_FromInt32(int32_t value)
+{ return PyLong_FromNativeBytes(&value, sizeof(value), -1); }
+
+PyObject* PyLong_FromUInt32(uint32_t value)
+{ return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1); }
+
+PyObject* PyLong_FromInt64(int64_t value)
+{ return PyLong_FromNativeBytes(&value, sizeof(value), -1); }
+
+PyObject* PyLong_FromUInt64(uint64_t value)
+{ return PyLong_FromUnsignedNativeBytes(&value, sizeof(value), -1); }
+
+#define LONG_TO_INT(obj, value, type_name) \
+    do { \
+        int flags = (Py_ASNATIVEBYTES_NATIVE_ENDIAN \
+                     | Py_ASNATIVEBYTES_ALLOW_INDEX); \
+        Py_ssize_t bytes = PyLong_AsNativeBytes(obj, value, sizeof(*value), 
flags); \
+        if (bytes < 0) { \
+            return -1; \
+        } \
+        if ((size_t)bytes > sizeof(*value)) { \
+            PyErr_SetString(PyExc_OverflowError, \
+                            "Python int too large to convert to " type_name); \
+            return -1; \
+        } \
+        return 0; \
+    } while (0)
+
+int PyLong_AsInt32(PyObject *obj, int32_t *value)
+{
+    LONG_TO_INT(obj, value, "C int32_t");
+}
+
+int PyLong_AsInt64(PyObject *obj, int64_t *value)
+{
+    LONG_TO_INT(obj, value, "C int64_t");
+}
+
+#define LONG_TO_UINT(obj, value, type_name) \
+    do { \
+        int flags = (Py_ASNATIVEBYTES_NATIVE_ENDIAN \
+                     | Py_ASNATIVEBYTES_UNSIGNED_BUFFER \
+                     | Py_ASNATIVEBYTES_REJECT_NEGATIVE \
+                     | Py_ASNATIVEBYTES_ALLOW_INDEX); \
+        Py_ssize_t bytes = PyLong_AsNativeBytes(obj, value, sizeof(*value), 
flags); \
+        if (bytes < 0) { \
+            return -1; \
+        } \
+        if ((size_t)bytes > sizeof(*value)) { \
+            PyErr_SetString(PyExc_OverflowError, \
+                            "Python int too large to convert to " type_name); \
+            return -1; \
+        } \
+        return 0; \
+    } while (0)
+
+int PyLong_AsUInt32(PyObject *obj, uint32_t *value)
+{
+    LONG_TO_UINT(obj, value, "C uint32_t");
+}
+
+int PyLong_AsUInt64(PyObject *obj, uint64_t *value)
+{
+    LONG_TO_UINT(obj, value, "C uint64_t");
+}
diff --git a/PC/python3dll.c b/PC/python3dll.c
index 78bcef155f51d5..1845334b244d8c 100755
--- a/PC/python3dll.c
+++ b/PC/python3dll.c
@@ -342,23 +342,31 @@ EXPORT_FUNC(PyList_Size)
 EXPORT_FUNC(PyList_Sort)
 EXPORT_FUNC(PyLong_AsDouble)
 EXPORT_FUNC(PyLong_AsInt)
+EXPORT_FUNC(PyLong_AsInt32)
+EXPORT_FUNC(PyLong_AsInt64)
 EXPORT_FUNC(PyLong_AsLong)
 EXPORT_FUNC(PyLong_AsLongAndOverflow)
 EXPORT_FUNC(PyLong_AsLongLong)
 EXPORT_FUNC(PyLong_AsLongLongAndOverflow)
 EXPORT_FUNC(PyLong_AsSize_t)
 EXPORT_FUNC(PyLong_AsSsize_t)
+EXPORT_FUNC(PyLong_AsUInt32)
+EXPORT_FUNC(PyLong_AsUInt64)
 EXPORT_FUNC(PyLong_AsUnsignedLong)
 EXPORT_FUNC(PyLong_AsUnsignedLongLong)
 EXPORT_FUNC(PyLong_AsUnsignedLongLongMask)
 EXPORT_FUNC(PyLong_AsUnsignedLongMask)
 EXPORT_FUNC(PyLong_AsVoidPtr)
 EXPORT_FUNC(PyLong_FromDouble)
+EXPORT_FUNC(PyLong_FromInt32)
+EXPORT_FUNC(PyLong_FromInt64)
 EXPORT_FUNC(PyLong_FromLong)
 EXPORT_FUNC(PyLong_FromLongLong)
 EXPORT_FUNC(PyLong_FromSize_t)
 EXPORT_FUNC(PyLong_FromSsize_t)
 EXPORT_FUNC(PyLong_FromString)
+EXPORT_FUNC(PyLong_FromUInt32)
+EXPORT_FUNC(PyLong_FromUInt64)
 EXPORT_FUNC(PyLong_FromUnsignedLong)
 EXPORT_FUNC(PyLong_FromUnsignedLongLong)
 EXPORT_FUNC(PyLong_FromVoidPtr)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to