From dcf81a55e1674a7d3fc414008c7a570999c0f2ba Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 18:03:45 +0300 Subject: [PATCH 1/9] PEP 757: mark as Final --- peps/pep-0757.rst | 60 +++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 6a1f5cd52df..5fb2943320e 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -3,7 +3,7 @@ Title: C API to import-export Python integers Author: Sergey B Kirpichev , Victor Stinner Discussions-To: https://discuss.python.org/t/63895 -Status: Accepted +Status: Final Type: Standards Track Created: 13-Sep-2024 Python-Version: 3.14 @@ -67,11 +67,13 @@ functions. .. 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 - 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 @@ -85,7 +87,7 @@ functions. Digit endianness: - ``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) @@ -110,11 +112,9 @@ Export API There are two cases: * 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`, :c:member:`ndigits` and :c:member:`digits` members. - Calling :c:func:`PyLong_FreeExport` is mandatory in this case. - + .. c:member:: int64_t value The native integer value of the exported :class:`int` object. @@ -145,11 +145,18 @@ If :c:member:`PyLongExport.digits` is not ``NULL``, a private field of the 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. - If *export_long->digits* is not ``NULL``, :c:func:`PyLong_FreeExport` must be - called when the export is no longer needed. + :c:func:`PyLong_FreeExport` must be called when the export is no longer + needed. + + .. impl-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 @@ -160,12 +167,15 @@ a thin wrapper to expose Python :class:`int` internal digits array. Release the export *export_long* created by :c:func:`PyLong_Export`. + .. impl-detail:: + Calling :c:func:`PyLong_FreeExport` is optional if *export_long->digits* + is ``NULL``. + Import API ---------- -The :c:type:`PyLongWriter` API can be used to import an integer: -create a Python :class:`int` object from a digits array. +The :c:type:`PyLongWriter` API can be used to import an integer. .. c:struct:: PyLongWriter @@ -187,12 +197,20 @@ create a Python :class:`int` object from a digits array. *ndigits* is the number of digits in the *digits* array. It must be greater than 0. - The caller can either initialize the array of digits *digits* and then - either call :c:func:`PyLongWriter_Finish` to get a Python :class:`int` or - :c:func:`PyLongWriter_Discard` to destroy the writer instance. 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). - The unused most-significant digits must be set to ``0``. + *digits* must not be NULL. + + After a successful call to this function, the caller should fill in the + array of digits *digits* and then call :c:func:`PyLongWriter_Finish` to get + a Python :class:`int`. + 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 @@ -209,14 +227,16 @@ wrapper to the private :c:func:`!_PyLong_New()` function. The function takes care of normalizing the digits and converts the 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) 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 From 6dc0239b3032f6e1018bfede4f281ff6cbe01ca3 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 18:24:31 +0300 Subject: [PATCH 2/9] + impl-detail sphinx directive --- pep_sphinx_extensions/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 6a878eaf8ca..201f2e79a90 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -69,6 +69,29 @@ def set_description( context["description"] = "Python Enhancement Proposals (PEPs)" +class ImplementationDetail(SphinxDirective): + + has_content = True + final_argument_whitespace = True + + # This text is copied to templates/dummy.html + label_text = sphinx_gettext('CPython implementation detail:') + + def run(self): + self.assert_has_content() + pnode = nodes.compound(classes=['impl-detail']) + content = self.content + add_text = nodes.strong(self.label_text, self.label_text) + self.state.nested_parse(content, self.content_offset, pnode) + content = nodes.inline(pnode[0].rawsource, translatable=True) + content.source = pnode[0].source + content.line = pnode[0].line + content += pnode[0].children + pnode[0].replace_self(nodes.paragraph( + '', '', add_text, nodes.Text(' '), content, translatable=False)) + return [pnode] + + def setup(app: Sphinx) -> dict[str, bool]: """Initialize Sphinx extension.""" @@ -101,6 +124,8 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_directive("superseded", pep_banner_directive.SupersededBanner) app.add_directive("withdrawn", pep_banner_directive.WithdrawnBanner) + app.add_directive('impl-detail', ImplementationDetail) + # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook From 5f113e03acf636fb7c00bdfc97ac9272fadca1e9 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 18:31:00 +0300 Subject: [PATCH 3/9] +1 --- pep_sphinx_extensions/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 201f2e79a90..5cf24384999 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Any from docutils.writers.html5_polyglot import HTMLTranslator +from docutils import nodes from sphinx import environment from pep_sphinx_extensions.generate_rss import ( @@ -26,6 +27,9 @@ from pep_sphinx_extensions.pep_processor.transforms import pep_references from pep_sphinx_extensions.pep_zero_generator.pep_index_generator import create_pep_zero +from sphinx.locale import _ as sphinx_gettext +from sphinx.util.docutils import SphinxDirective + if TYPE_CHECKING: from sphinx.application import Sphinx From 363f7a5ccf03d5f907717536caa467a4a4b437a2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 18:55:26 +0300 Subject: [PATCH 4/9] + adjust formatting --- peps/pep-0757.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 5fb2943320e..c8c4d872f9e 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -154,9 +154,9 @@ If :c:member:`PyLongExport.digits` is not ``NULL``, a private field of the :c:func:`PyLong_FreeExport` must be called when the export is no longer needed. - .. impl-detail:: - This function always succeeds if *obj* is a Python :class:`int` object - or a subclass. + .. impl-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 From 2d31e2fec415f7b91f5ea2fec9fbc9e14504e24d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 18:56:31 +0300 Subject: [PATCH 5/9] now impl-detail -> admonition directive --- pep_sphinx_extensions/__init__.py | 29 ----------------------------- peps/pep-0757.rst | 8 ++++++-- 2 files changed, 6 insertions(+), 31 deletions(-) diff --git a/pep_sphinx_extensions/__init__.py b/pep_sphinx_extensions/__init__.py index 5cf24384999..6a878eaf8ca 100644 --- a/pep_sphinx_extensions/__init__.py +++ b/pep_sphinx_extensions/__init__.py @@ -7,7 +7,6 @@ from typing import TYPE_CHECKING, Any from docutils.writers.html5_polyglot import HTMLTranslator -from docutils import nodes from sphinx import environment from pep_sphinx_extensions.generate_rss import ( @@ -27,9 +26,6 @@ from pep_sphinx_extensions.pep_processor.transforms import pep_references from pep_sphinx_extensions.pep_zero_generator.pep_index_generator import create_pep_zero -from sphinx.locale import _ as sphinx_gettext -from sphinx.util.docutils import SphinxDirective - if TYPE_CHECKING: from sphinx.application import Sphinx @@ -73,29 +69,6 @@ def set_description( context["description"] = "Python Enhancement Proposals (PEPs)" -class ImplementationDetail(SphinxDirective): - - has_content = True - final_argument_whitespace = True - - # This text is copied to templates/dummy.html - label_text = sphinx_gettext('CPython implementation detail:') - - def run(self): - self.assert_has_content() - pnode = nodes.compound(classes=['impl-detail']) - content = self.content - add_text = nodes.strong(self.label_text, self.label_text) - self.state.nested_parse(content, self.content_offset, pnode) - content = nodes.inline(pnode[0].rawsource, translatable=True) - content.source = pnode[0].source - content.line = pnode[0].line - content += pnode[0].children - pnode[0].replace_self(nodes.paragraph( - '', '', add_text, nodes.Text(' '), content, translatable=False)) - return [pnode] - - def setup(app: Sphinx) -> dict[str, bool]: """Initialize Sphinx extension.""" @@ -128,8 +101,6 @@ def setup(app: Sphinx) -> dict[str, bool]: app.add_directive("superseded", pep_banner_directive.SupersededBanner) app.add_directive("withdrawn", pep_banner_directive.WithdrawnBanner) - app.add_directive('impl-detail', ImplementationDetail) - # Register event callbacks app.connect("builder-inited", _update_config_for_builder) # Update configuration values for builder used app.connect("env-before-read-docs", create_pep_zero) # PEP 0 hook diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index c8c4d872f9e..08f5609c1e0 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -154,7 +154,9 @@ If :c:member:`PyLongExport.digits` is not ``NULL``, a private field of the :c:func:`PyLong_FreeExport` must be called when the export is no longer needed. - .. impl-detail:: + .. admonition:: CPython implementation detail + :class: warning + This function always succeeds if *obj* is a Python :class:`int` object or a subclass. @@ -167,7 +169,9 @@ a thin wrapper to expose Python :class:`int` internal digits array. Release the export *export_long* created by :c:func:`PyLong_Export`. - .. impl-detail:: + .. admonition:: CPython implementation detail + :class: warning + Calling :c:func:`PyLong_FreeExport` is optional if *export_long->digits* is ``NULL``. From 7b1986bd7a1eefbf056ab0f91a47fca8e2013d16 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Fri, 13 Dec 2024 19:09:43 +0300 Subject: [PATCH 6/9] eh, maybe this will be better? --- peps/pep-0757.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 08f5609c1e0..d54fcdbab08 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -114,7 +114,7 @@ Export API * If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member. * If :c:member:`digits` is not ``NULL``, use :c:member:`negative`, :c:member:`ndigits` and :c:member:`digits` members. - + .. c:member:: int64_t value The native integer value of the exported :class:`int` object. @@ -154,11 +154,8 @@ If :c:member:`PyLongExport.digits` is not ``NULL``, a private field of the :c:func:`PyLong_FreeExport` must be called when the export is no longer needed. - .. admonition:: CPython implementation detail - :class: warning - - This function always succeeds if *obj* is a Python :class:`int` object - or a subclass. + **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 @@ -169,11 +166,8 @@ a thin wrapper to expose Python :class:`int` internal digits array. Release the export *export_long* created by :c:func:`PyLong_Export`. - .. admonition:: CPython implementation detail - :class: warning - - Calling :c:func:`PyLong_FreeExport` is optional if *export_long->digits* - is ``NULL``. + **CPython implementation detail**: Calling :c:func:`PyLong_FreeExport` is + optional if *export_long->digits* is ``NULL``. Import API From 720fa55f33d7cf098583a86d211db3c180dc34eb Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 14 Dec 2024 03:30:49 +0300 Subject: [PATCH 7/9] + canonical-doc --- peps/pep-0757.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index d54fcdbab08..d5568990e60 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -10,6 +10,9 @@ Python-Version: 3.14 Post-History: `14-Sep-2024 `__ Resolution: `08-Dec-2024 `__ + +.. canonical-doc:: The `Export API `_ and the `PyLongWriter API `_ + .. highlight:: c From 1f458560988172c1846f638cd187cd33bc2e15e2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Sat, 14 Dec 2024 15:54:24 +0300 Subject: [PATCH 8/9] Update peps/pep-0757.rst Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-0757.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index d5568990e60..92faa6422df 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -11,7 +11,7 @@ Post-History: `14-Sep-2024 `__ Resolution: `08-Dec-2024 `__ -.. canonical-doc:: The `Export API `_ and the `PyLongWriter API `_ +.. canonical-doc:: the `Export API `_ and the `PyLongWriter API `_ .. highlight:: c From 07696943f38be9d57b6fdde587b196315416f12d Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Mon, 16 Dec 2024 04:25:50 +0300 Subject: [PATCH 9/9] + pass "make spellcheck" --- peps/pep-0757.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0757.rst b/peps/pep-0757.rst index 92faa6422df..8d6686b74fa 100644 --- a/peps/pep-0757.rst +++ b/peps/pep-0757.rst @@ -482,7 +482,7 @@ API example:: 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 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 CPython side (i.e. actual conversion between different layouts). For example, @@ -553,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 development, benchmark different variants for exporting small integers (is that 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.