PEP 757: mark as Final (#4167)

Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
Sergey B Kirpichev 2024-12-16 10:23:59 +03:00 committed by GitHub
parent d8194ab32c
commit cbf8efbc53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 42 additions and 21 deletions

View File

@ -3,13 +3,16 @@ Title: C API to import-export Python integers
Author: Sergey B Kirpichev <skirpichev@gmail.com>, Author: Sergey B Kirpichev <skirpichev@gmail.com>,
Victor Stinner <vstinner@python.org> Victor Stinner <vstinner@python.org>
Discussions-To: https://discuss.python.org/t/63895 Discussions-To: https://discuss.python.org/t/63895
Status: Accepted Status: Final
Type: Standards Track Type: Standards Track
Created: 13-Sep-2024 Created: 13-Sep-2024
Python-Version: 3.14 Python-Version: 3.14
Post-History: `14-Sep-2024 <https://discuss.python.org/t/63895>`__ Post-History: `14-Sep-2024 <https://discuss.python.org/t/63895>`__
Resolution: `08-Dec-2024 <https://discuss.python.org/t/63895/79>`__ Resolution: `08-Dec-2024 <https://discuss.python.org/t/63895/79>`__
.. canonical-doc:: the `Export API <https://docs.python.org/dev/c-api/long.html#export-api>`_ and the `PyLongWriter API <https://docs.python.org/dev/c-api/long.html#pylongwriter-api>`_
.. highlight:: c .. highlight:: c
@ -67,11 +70,13 @@ functions.
.. c:member:: uint8_t bits_per_digit .. c:member:: uint8_t bits_per_digit
Bits per digit. Bits per digit. For example, a 15 bit digit means that bits 0-14
contain meaningful information.
.. c:member:: uint8_t digit_size .. c:member:: uint8_t digit_size
Digit size in bytes. Digit size in bytes. For example, a 15 bit digit will require at least
2 bytes.
.. c:member:: int8_t digits_order .. c:member:: int8_t digits_order
@ -85,7 +90,7 @@ functions.
Digit endianness: Digit endianness:
- ``1`` for most significant byte first (big endian) - ``1`` for most significant byte first (big endian)
- ``-1`` for least significant first (little endian) - ``-1`` for least significant byte first (little endian)
.. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void) .. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void)
@ -110,10 +115,8 @@ Export API
There are two cases: There are two cases:
* If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member. * If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member.
Calling :c:func:`PyLong_FreeExport` is optional in this case.
* If :c:member:`digits` is not ``NULL``, use :c:member:`negative`, * If :c:member:`digits` is not ``NULL``, use :c:member:`negative`,
:c:member:`ndigits` and :c:member:`digits` members. :c:member:`ndigits` and :c:member:`digits` members.
Calling :c:func:`PyLong_FreeExport` is mandatory in this case.
.. c:member:: int64_t value .. c:member:: int64_t value
@ -145,11 +148,17 @@ If :c:member:`PyLongExport.digits` is not ``NULL``, a private field of the
Export a Python :class:`int` object. Export a Python :class:`int` object.
On success, set *\*export_long* and return 0. *export_long* must point to a :c:struct:`PyLongExport` structure allocated
by the caller. It must not be ``NULL``.
On success, fill in *\*export_long* and return 0.
On error, set an exception and return -1. On error, set an exception and return -1.
If *export_long->digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be :c:func:`PyLong_FreeExport` must be called when the export is no longer
called when the export is no longer needed. needed.
**CPython implementation detail**: This function always succeeds if *obj* is
a Python :class:`int` object or a subclass.
On CPython 3.14, no memory copy is needed in :c:func:`PyLong_Export`, it's just On CPython 3.14, no memory copy is needed in :c:func:`PyLong_Export`, it's just
@ -160,12 +169,14 @@ a thin wrapper to expose Python :class:`int` internal digits array.
Release the export *export_long* created by :c:func:`PyLong_Export`. Release the export *export_long* created by :c:func:`PyLong_Export`.
**CPython implementation detail**: Calling :c:func:`PyLong_FreeExport` is
optional if *export_long->digits* is ``NULL``.
Import API Import API
---------- ----------
The :c:type:`PyLongWriter` API can be used to import an integer: The :c:type:`PyLongWriter` API can be used to import an integer.
create a Python :class:`int` object from a digits array.
.. c:struct:: PyLongWriter .. c:struct:: PyLongWriter
@ -187,12 +198,20 @@ create a Python :class:`int` object from a digits array.
*ndigits* is the number of digits in the *digits* array. It must be *ndigits* is the number of digits in the *digits* array. It must be
greater than 0. greater than 0.
The caller can either initialize the array of digits *digits* and then *digits* must not be NULL.
either call :c:func:`PyLongWriter_Finish` to get a Python :class:`int` or
:c:func:`PyLongWriter_Discard` to destroy the writer instance. Digits must After a successful call to this function, the caller should fill in the
be in the range [``0``; ``(1 << bits_per_digit) - 1``] (where the array of digits *digits* and then call :c:func:`PyLongWriter_Finish` to get
:c:struct:`~PyLongLayout.bits_per_digit` is the number of bits per digit). a Python :class:`int`.
The unused most-significant digits must be set to ``0``. 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
per digit).
Any unused most significant digits must be set to ``0``.
Alternately, call :c:func:`PyLongWriter_Discard` to destroy the writer
instance without creating an :class:`~int` object.
On CPython 3.14, the :c:func:`PyLongWriter_Create` implementation is a thin On CPython 3.14, the :c:func:`PyLongWriter_Create` implementation is a thin
@ -209,14 +228,16 @@ wrapper to the private :c:func:`!_PyLong_New()` function.
The function takes care of normalizing the digits and converts the The function takes care of normalizing the digits and converts the
object to a compact integer if needed. object to a compact integer if needed.
The writer instance is invalid after the call. The writer instance and the *digits* array are invalid after the call.
.. c:function:: void PyLongWriter_Discard(PyLongWriter *writer) .. c:function:: void PyLongWriter_Discard(PyLongWriter *writer)
Discard a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`. Discard a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`.
The writer instance is invalid after the call. *writer* must not be ``NULL``.
The writer instance and the *digits* array are invalid after the call.
Optimize import for small integers Optimize import for small integers
@ -461,7 +482,7 @@ API example::
This might work for the GMP, as it has :c:func:`!mpz_limbs_read()` and This might work for the GMP, as it has :c:func:`!mpz_limbs_read()` and
:c:func:`!mpz_limbs_write()` functions, that can provide required access to :c:func:`!mpz_limbs_write()` functions, that can provide required access to
internals of :c:struct:`!mpz_t`. Other libraries may require using temporary internals of :c:struct:`!mpz_t`. Other libraries may require using temporary
bufferes and then mpz_import/export-like functions on their side. buffers and then mpz_import/export-like functions on their side.
The major drawback of this approach is that it's much more complex on the The major drawback of this approach is that it's much more complex on the
CPython side (i.e. actual conversion between different layouts). For example, CPython side (i.e. actual conversion between different layouts). For example,
@ -532,7 +553,7 @@ This might look as a simplification from the API designer point of view, but
will be less convenient for end users. They will have to follow Python will be less convenient for end users. They will have to follow Python
development, benchmark different variants for exporting small integers (is that development, benchmark different variants for exporting small integers (is that
obvious why above case was chosen instead of :c:func:`PyLong_AsInt64`?), maybe obvious why above case was chosen instead of :c:func:`PyLong_AsInt64`?), maybe
support different code paths for various CPython versions or accross different support different code paths for various CPython versions or across different
Python implementations. Python implementations.