From 72f6c8c85570cef2e92287ce013fae59f601974c Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 16 Jan 2026 15:05:17 +0300 Subject: [PATCH 1/5] gh-143869: Add PEP 757 functions to the limited API --- Doc/data/stable_abi.dat | 9 +++++ Include/cpython/longintrepr.h | 39 ------------------- Include/longobject.h | 38 ++++++++++++++++++ Lib/test/test_stable_abi_ctypes.py | 6 +++ ...-01-16-15-04-26.gh-issue-143869.vf94km.rst | 5 +++ Misc/stable_abi.toml | 21 ++++++++++ PC/python3dll.c | 6 +++ 7 files changed, 85 insertions(+), 39 deletions(-) create mode 100644 Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 9c5fdcefaf81d0..2d96e979ad88bd 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -389,8 +389,14 @@ func,PyList_SetSlice,3.2,, func,PyList_Size,3.2,, func,PyList_Sort,3.2,, data,PyList_Type,3.2,, +type,PyLongExport,3.15,,members +type,PyLongLayout,3.15,,full-abi type,PyLongObject,3.2,,opaque data,PyLongRangeIter_Type,3.2,, +type,PyLongWriter,3.15,,opaque +func,PyLongWriter_Create,3.15,, +func,PyLongWriter_Discard,3.15,, +func,PyLongWriter_Finish,3.15,, func,PyLong_AsDouble,3.2,, func,PyLong_AsInt,3.13,, func,PyLong_AsInt32,3.14,, @@ -409,6 +415,8 @@ func,PyLong_AsUnsignedLongLong,3.2,, func,PyLong_AsUnsignedLongLongMask,3.2,, func,PyLong_AsUnsignedLongMask,3.2,, func,PyLong_AsVoidPtr,3.2,, +func,PyLong_Export,3.15,, +func,PyLong_FreeExport,3.15,, func,PyLong_FromDouble,3.2,, func,PyLong_FromInt32,3.14,, func,PyLong_FromInt64,3.14,, @@ -425,6 +433,7 @@ func,PyLong_FromUnsignedLongLong,3.2,, func,PyLong_FromUnsignedNativeBytes,3.14,, func,PyLong_FromVoidPtr,3.2,, func,PyLong_GetInfo,3.2,, +func,PyLong_GetNativeLayout,3.15,, data,PyLong_Type,3.2,, macro,PyMODEXPORT_FUNC,3.15,, data,PyMap_Type,3.2,, diff --git a/Include/cpython/longintrepr.h b/Include/cpython/longintrepr.h index 4b6f97a5e475d6..804c1e9427e063 100644 --- a/Include/cpython/longintrepr.h +++ b/Include/cpython/longintrepr.h @@ -138,45 +138,6 @@ _PyLong_CompactValue(const PyLongObject *op) #define PyUnstable_Long_CompactValue _PyLong_CompactValue - -/* --- Import/Export API -------------------------------------------------- */ - -typedef struct PyLongLayout { - uint8_t bits_per_digit; - uint8_t digit_size; - int8_t digits_order; - int8_t digit_endianness; -} PyLongLayout; - -PyAPI_FUNC(const PyLongLayout*) PyLong_GetNativeLayout(void); - -typedef struct PyLongExport { - int64_t value; - uint8_t negative; - Py_ssize_t ndigits; - const void *digits; - // Member used internally, must not be used for other purpose. - Py_uintptr_t _reserved; -} PyLongExport; - -PyAPI_FUNC(int) PyLong_Export( - PyObject *obj, - PyLongExport *export_long); -PyAPI_FUNC(void) PyLong_FreeExport( - PyLongExport *export_long); - - -/* --- PyLongWriter API --------------------------------------------------- */ - -typedef struct PyLongWriter PyLongWriter; - -PyAPI_FUNC(PyLongWriter*) PyLongWriter_Create( - int negative, - Py_ssize_t ndigits, - void **digits); -PyAPI_FUNC(PyObject*) PyLongWriter_Finish(PyLongWriter *writer); -PyAPI_FUNC(void) PyLongWriter_Discard(PyLongWriter *writer); - #ifdef __cplusplus } #endif diff --git a/Include/longobject.h b/Include/longobject.h index 19f06977036d05..38673bc18785fa 100644 --- a/Include/longobject.h +++ b/Include/longobject.h @@ -166,6 +166,44 @@ PyAPI_FUNC(PyObject *) PyLong_FromString(const char *, char **, int); PyAPI_FUNC(unsigned long) PyOS_strtoul(const char *, char **, int); PyAPI_FUNC(long) PyOS_strtol(const char *, char **, int); +/* --- Import/Export API -------------------------------------------------- */ + +typedef struct PyLongLayout { + uint8_t bits_per_digit; + uint8_t digit_size; + int8_t digits_order; + int8_t digit_endianness; +} PyLongLayout; + +PyAPI_FUNC(const PyLongLayout*) PyLong_GetNativeLayout(void); + +typedef struct PyLongExport { + int64_t value; + uint8_t negative; + Py_ssize_t ndigits; + const void *digits; + // Member used internally, must not be used for other purpose. + Py_uintptr_t _reserved; +} PyLongExport; + +PyAPI_FUNC(int) PyLong_Export( + PyObject *obj, + PyLongExport *export_long); +PyAPI_FUNC(void) PyLong_FreeExport( + PyLongExport *export_long); + + +/* --- PyLongWriter API --------------------------------------------------- */ + +typedef struct PyLongWriter PyLongWriter; + +PyAPI_FUNC(PyLongWriter*) PyLongWriter_Create( + int negative, + Py_ssize_t ndigits, + void **digits); +PyAPI_FUNC(PyObject*) PyLongWriter_Finish(PyLongWriter *writer); +PyAPI_FUNC(void) PyLongWriter_Discard(PyLongWriter *writer); + #ifndef Py_LIMITED_API # define Py_CPYTHON_LONGOBJECT_H # include "cpython/longobject.h" diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index 2e93ac08f82868..28f5dd11130c70 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -391,6 +391,9 @@ def test_windows_feature_macros(self): "PyList_Sort", "PyList_Type", "PyLongRangeIter_Type", + "PyLongWriter_Create", + "PyLongWriter_Discard", + "PyLongWriter_Finish", "PyLong_AsDouble", "PyLong_AsInt", "PyLong_AsInt32", @@ -409,6 +412,8 @@ def test_windows_feature_macros(self): "PyLong_AsUnsignedLongLongMask", "PyLong_AsUnsignedLongMask", "PyLong_AsVoidPtr", + "PyLong_Export", + "PyLong_FreeExport", "PyLong_FromDouble", "PyLong_FromInt32", "PyLong_FromInt64", @@ -425,6 +430,7 @@ def test_windows_feature_macros(self): "PyLong_FromUnsignedNativeBytes", "PyLong_FromVoidPtr", "PyLong_GetInfo", + "PyLong_GetNativeLayout", "PyLong_Type", "PyMap_Type", "PyMapping_Check", diff --git a/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst b/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst new file mode 100644 index 00000000000000..fe707220a8ad7c --- /dev/null +++ b/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst @@ -0,0 +1,5 @@ +Added :c:func:`PyLong_GetNativeLayout`, :c:struct:`PyLongExport`, +:c:func:`PyLong_Export`, :c:func:`PyLong_FreeExport`, +:c:struct:`PyLongWriter`, :c:func:`PyLongWriter_Create`, +:c:func:`PyLongWriter_Finish` and :c:func:`PyLongWriter_Discard` to the +limited API. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 31d22e64b846ba..460b9f2ffc40fd 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2664,3 +2664,24 @@ [function.Py_SET_SIZE] # Before 3.15, this was a macro that accessed the PyObject member added = '3.15' +[function.PyLong_GetNativeLayout] + added = '3.15' +[function.PyLong_Export] + added = '3.15' +[function.PyLong_FreeExport] + added = '3.15' +[function.PyLongWriter_Create] + added = '3.15' +[function.PyLongWriter_Finish] + added = '3.15' +[function.PyLongWriter_Discard] + added = '3.15' +[struct.PyLongWriter] + added = '3.15' + struct_abi_kind = 'opaque' +[struct.PyLongLayout] + added = '3.15' + struct_abi_kind = 'full-abi' +[struct.PyLongExport] + added = '3.15' + struct_abi_kind = 'members' diff --git a/PC/python3dll.c b/PC/python3dll.c index 0d9e7e9a1bac93..b23bc2b8f4382f 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -366,6 +366,8 @@ EXPORT_FUNC(PyLong_AsUnsignedLongLong) EXPORT_FUNC(PyLong_AsUnsignedLongLongMask) EXPORT_FUNC(PyLong_AsUnsignedLongMask) EXPORT_FUNC(PyLong_AsVoidPtr) +EXPORT_FUNC(PyLong_Export) +EXPORT_FUNC(PyLong_FreeExport) EXPORT_FUNC(PyLong_FromDouble) EXPORT_FUNC(PyLong_FromInt32) EXPORT_FUNC(PyLong_FromInt64) @@ -382,6 +384,10 @@ EXPORT_FUNC(PyLong_FromUnsignedLongLong) EXPORT_FUNC(PyLong_FromUnsignedNativeBytes) EXPORT_FUNC(PyLong_FromVoidPtr) EXPORT_FUNC(PyLong_GetInfo) +EXPORT_FUNC(PyLong_GetNativeLayout) +EXPORT_FUNC(PyLongWriter_Create) +EXPORT_FUNC(PyLongWriter_Discard) +EXPORT_FUNC(PyLongWriter_Finish) EXPORT_FUNC(PyMapping_Check) EXPORT_FUNC(PyMapping_GetItemString) EXPORT_FUNC(PyMapping_GetOptionalItem) From c7262fde62110e88f046e9584c94e40bede73e8f Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 16 Jan 2026 16:46:51 +0300 Subject: [PATCH 2/5] +1 --- .../2026-01-16-15-04-26.gh-issue-143869.vf94km.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst b/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst index fe707220a8ad7c..60b0c1ec13062b 100644 --- a/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst +++ b/Misc/NEWS.d/next/C_API/2026-01-16-15-04-26.gh-issue-143869.vf94km.rst @@ -1,5 +1,5 @@ -Added :c:func:`PyLong_GetNativeLayout`, :c:struct:`PyLongExport`, -:c:func:`PyLong_Export`, :c:func:`PyLong_FreeExport`, -:c:struct:`PyLongWriter`, :c:func:`PyLongWriter_Create`, -:c:func:`PyLongWriter_Finish` and :c:func:`PyLongWriter_Discard` to the -limited API. +Added :c:func:`PyLong_GetNativeLayout`, :c:struct:`PyLongLayout`, +:c:struct:`PyLongExport`, :c:func:`PyLong_Export`, +:c:func:`PyLong_FreeExport`, :c:struct:`PyLongWriter`, +:c:func:`PyLongWriter_Create`, :c:func:`PyLongWriter_Finish` and +:c:func:`PyLongWriter_Discard` to the limited API. From c83244faf1ba5046be763b8b44f6343233cc2b33 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 17 Jan 2026 00:54:43 +0300 Subject: [PATCH 3/5] +1 --- Doc/c-api/long.rst | 4 ++-- Misc/stable_abi.toml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index ed9b37dc1721f2..46c487385b52bd 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -735,7 +735,7 @@ Export API in a process, and so it can be cached. -.. c:struct:: PyLongExport +.. c:type:: PyLongExport Export of a Python :class:`int` object. @@ -769,7 +769,7 @@ Export API Export a Python :class:`int` object. - *export_long* must point to a :c:struct:`PyLongExport` structure allocated + *export_long* must point to a :c:type:`PyLongExport` structure allocated by the caller. It must not be ``NULL``. On success, fill in *\*export_long* and return ``0``. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index 460b9f2ffc40fd..8519db9056da5f 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -2664,6 +2664,9 @@ [function.Py_SET_SIZE] # Before 3.15, this was a macro that accessed the PyObject member added = '3.15' + +# PEP 757 import/export API. + [function.PyLong_GetNativeLayout] added = '3.15' [function.PyLong_Export] @@ -2685,3 +2688,4 @@ [struct.PyLongExport] added = '3.15' struct_abi_kind = 'members' + members = ['value', 'negative', 'ndigits', 'digits'] From f9fda9b43fab9c5698a780cda83f8d2cdcaa3197 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 17 Jan 2026 00:55:16 +0300 Subject: [PATCH 4/5] +1 --- Doc/data/stable_abi.dat | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 2d96e979ad88bd..3fd34f1e47221a 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -390,6 +390,10 @@ func,PyList_Size,3.2,, func,PyList_Sort,3.2,, data,PyList_Type,3.2,, type,PyLongExport,3.15,,members +member,PyLongExport.value,3.15,, +member,PyLongExport.negative,3.15,, +member,PyLongExport.ndigits,3.15,, +member,PyLongExport.digits,3.15,, type,PyLongLayout,3.15,,full-abi type,PyLongObject,3.2,,opaque data,PyLongRangeIter_Type,3.2,, From 99d9f24d7df41992ccc969080575bb0f29b3026e Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 17 Jan 2026 01:02:40 +0300 Subject: [PATCH 5/5] +1 --- Doc/c-api/long.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index 46c487385b52bd..790ec8da109ba8 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -687,7 +687,7 @@ Export API .. versionadded:: 3.14 -.. c:struct:: PyLongLayout +.. c:type:: PyLongLayout Layout of an array of "digits" ("limbs" in the GMP terminology), used to represent absolute value for arbitrary precision integers. @@ -727,7 +727,7 @@ Export API Get the native layout of Python :class:`int` objects. - See the :c:struct:`PyLongLayout` structure. + See the :c:type:`PyLongLayout` structure. The function must not be called before Python initialization nor after Python finalization. The returned layout is valid until Python is @@ -799,7 +799,7 @@ The :c:type:`PyLongWriter` API can be used to import an integer. .. versionadded:: 3.14 -.. c:struct:: PyLongWriter +.. c:type:: PyLongWriter A Python :class:`int` writer instance. @@ -827,7 +827,7 @@ The :c:type:`PyLongWriter` API can be used to import an integer. The layout of *digits* is described by :c:func:`PyLong_GetNativeLayout`. Digits must be in the range [``0``; ``(1 << bits_per_digit) - 1``] - (where the :c:struct:`~PyLongLayout.bits_per_digit` is the number of bits + (where the :c:type:`~PyLongLayout.bits_per_digit` is the number of bits per digit). Any unused most significant digits must be set to ``0``.