Skip to content

Commit c08d0df

Browse files
authored
Merge branch 'main' into 694-update
2 parents 3ccfe41 + b261297 commit c08d0df

11 files changed

Lines changed: 284 additions & 95 deletions

File tree

pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,45 @@ def write_peps_json(peps: list[parser.PEP], path: Path) -> None:
6060
Path(path, "api", "peps.json").write_text(json_peps, encoding="utf-8")
6161

6262

63+
def build_release_peps(peps: list[parser.PEP]) -> dict[str, int]:
64+
"""Map each Python version to its release-schedule PEP number.
65+
66+
Handles release PEPs that cover multiple versions jointly
67+
(e.g. "2.6, 3.0"), so individual versions also resolve.
68+
"""
69+
release_peps: dict[str, int] = {}
70+
71+
for pep in peps:
72+
if pep.python_version and "release" in pep.topic:
73+
for version in map(str.strip, pep.python_version.split(",")):
74+
release_peps[version] = pep.number
75+
76+
return release_peps
77+
78+
6379
def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> None:
6480
peps = _parse_peps(Path(app.srcdir))
6581

66-
numerical_index_text = writer.PEPZeroWriter().write_numerical_index(peps)
82+
release_peps = build_release_peps(peps)
83+
84+
numerical_index_text = writer.PEPZeroWriter(
85+
release_peps
86+
).write_numerical_index(peps)
6787
subindices.update_sphinx("numerical", numerical_index_text, docnames, env)
6888

69-
pep0_text = writer.PEPZeroWriter().write_pep0(peps, builder=env.settings["builder"])
89+
pep0_text = writer.PEPZeroWriter(
90+
release_peps
91+
).write_pep0(peps, builder=env.settings["builder"])
7092
pep0_path = subindices.update_sphinx("pep-0000", pep0_text, docnames, env)
7193
peps.append(parser.PEP(pep0_path))
7294

73-
subindices.generate_subindices(SUBINDICES_BY_TOPIC, peps, docnames, env)
95+
subindices.generate_subindices(
96+
SUBINDICES_BY_TOPIC,
97+
peps,
98+
release_peps,
99+
docnames,
100+
env,
101+
)
74102

75103
write_peps_json(peps, Path(app.outdir))
76104

pep_sphinx_extensions/pep_zero_generator/subindices.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def update_sphinx(filename: str, text: str, docnames: list[str], env: BuildEnvir
3535
def generate_subindices(
3636
subindices: dict[str, str],
3737
peps: list[PEP],
38+
release_peps: dict[str, int],
3839
docnames: list[str],
3940
env: BuildEnvironment,
4041
) -> None:
@@ -58,8 +59,11 @@ def generate_subindices(
5859
5960
{additional_description}
6061
"""
61-
subindex_text = writer.PEPZeroWriter().write_pep0(
62-
filtered_peps, header, subindex_intro, is_pep0=False,
62+
subindex_text = writer.PEPZeroWriter(release_peps).write_pep0(
63+
filtered_peps,
64+
header,
65+
subindex_intro,
66+
is_pep0=False,
6367
)
6468
update_sphinx(f"topic/{subindex}", subindex_text, docnames, env)
6569

pep_sphinx_extensions/pep_zero_generator/writer.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ class PEPZeroWriter:
5959
801: "Warsaw",
6060
}
6161

62-
def __init__(self):
62+
def __init__(self, release_peps: dict[str, int] | None = None):
6363
self.output: list[str] = []
64+
self.release_peps = release_peps or {}
6465

6566
def emit_text(self, content: str) -> None:
6667
# Appends content argument to the output list
@@ -87,7 +88,17 @@ def emit_pep_row(
8788
self.emit_text(f" - :pep:`{title.replace('`', '')} <{number}>`")
8889
self.emit_text(f" - {authors}")
8990
if python_version is not None:
90-
self.emit_text(f" - {python_version}")
91+
linked_versions = []
92+
93+
for version in map(str.strip, python_version.split(",")):
94+
release_pep = self.release_peps.get(version)
95+
96+
if release_pep is not None:
97+
linked_versions.append(f":pep:`{version} <{release_pep}>`")
98+
else:
99+
linked_versions.append(version)
100+
101+
self.emit_text(f" - {', '.join(linked_versions)}")
91102

92103
def emit_column_headers(self, *, include_version=True) -> None:
93104
"""Output the column headers for the PEP indices."""

pep_sphinx_extensions/tests/pep_zero_generator/test_pep_index_generator.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,13 @@ def test_create_pep_json():
99
out = pep_index_generator.create_pep_json(peps)
1010

1111
assert '"url": "https://peps.python.org/pep-0008/"' in out
12+
13+
14+
def test_build_release_peps_links_individual_versions_from_joint_release_pep():
15+
peps = [
16+
parser.PEP(PEP_ROOT / "pep-0361.rst"), # "2.6, 3.0" joint release PEP
17+
]
18+
19+
release_peps = pep_index_generator.build_release_peps(peps)
20+
21+
assert release_peps == {"2.6": 361, "3.0": 361}

pep_sphinx_extensions/tests/pep_zero_generator/test_writer.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,42 @@ def test_sort_authors():
8686

8787
# Assert
8888
assert out == ["Aardvark, Alfred", "lowercase, laurence", "Zebra, Zoë"]
89+
90+
91+
@pytest.mark.parametrize(
92+
("python_version", "expected"),
93+
[
94+
("3.14", " - :pep:`3.14 <745>`"),
95+
(
96+
"2.4, 2.5, 2.6",
97+
" - :pep:`2.4 <320>`, :pep:`2.5 <356>`, :pep:`2.6 <361>`",
98+
),
99+
("2.4, 2.9", " - :pep:`2.4 <320>`, 2.9"),
100+
("1.5.2", " - 1.5.2"),
101+
("", " - "),
102+
],
103+
)
104+
def test_emit_pep_row_links_python_version_to_release_pep(
105+
python_version,
106+
expected,
107+
):
108+
# Arrange
109+
release_peps = {
110+
"2.4": 320,
111+
"2.5": 356,
112+
"2.6": 361,
113+
"3.14": 745,
114+
}
115+
pep0_writer = writer.PEPZeroWriter(release_peps=release_peps)
116+
117+
# Act
118+
pep0_writer.emit_pep_row(
119+
shorthand="Active",
120+
number=999,
121+
title="Test PEP",
122+
authors="Test Author",
123+
python_version=python_version,
124+
)
125+
126+
# Assert
127+
assert expected in pep0_writer.output

peps/pep-0011.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ Tier 2
107107
Target Triple Notes Contacts
108108
============================= ========================== ========
109109
aarch64-unknown-linux-gnu glibc, clang Victor Stinner, Gregory P. Smith
110+
aarch64-pc-windows-msvc Steve Dower, Diego Russo, Chris Eibl
110111
wasm32-unknown-wasip1 WASI SDK, Wasmtime Brett Cannon, Michael Droettboom, Savannah Ostrowski
111112
x86_64-apple-darwin macOS, clang Sam Gross, Barry Warsaw, Ronald Oussoren
112113
x86_64-unknown-linux-gnu glibc, clang Victor Stinner, Gregory P. Smith
@@ -126,7 +127,6 @@ Tier 3
126127
Target Triple Notes Contacts
127128
================================ =========================== ========
128129
aarch64-linux-android Russell Keith-Magee, Petr Viktorin
129-
aarch64-pc-windows-msvc Steve Dower
130130
arm64-apple-ios iOS on device Russell Keith-Magee, Ned Deily
131131
arm64-apple-ios-simulator iOS on M1 macOS simulator Russell Keith-Magee, Ned Deily
132132
armv7l-unknown-linux-gnueabihf 32-bit Raspberry Pi OS, gcc Gregory P. Smith

peps/pep-0793.rst

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ PEP: 793
22
Title: PyModExport: A new entry point for C extension modules
33
Author: Petr Viktorin <encukou@gmail.com>
44
Discussions-To: https://discuss.python.org/t/93444
5-
Status: Accepted
5+
Status: Final
66
Type: Standards Track
77
Created: 23-May-2025
88
Python-Version: 3.15
99
Post-History: `14-Mar-2025 <https://discuss.python.org/t/84498/>`__,
1010
`27-May-2025 <https://discuss.python.org/t/93444/>`__,
1111
Resolution: `23-Oct-2025 <https://discuss.python.org/t/93444/46>`__
1212

13+
.. canonical-doc:: :ref:`py3.15:extension-modules`
14+
1315

1416
Abstract
1517
========
@@ -21,7 +23,7 @@ This allows extension authors to avoid using a statically allocated
2123
``PyObject``, lifting the most common obstacle to making one compiled library
2224
file usable with both regular and free-threaded builds of CPython.
2325

24-
To make this viable, we also specify new module slot types to replace
26+
To make this viable, we also specify new module slot IDs to replace
2527
``PyModuleDef``'s fields, and to allow adding a *token* similar to the
2628
``Py_tp_token`` used for type objects.
2729

@@ -131,8 +133,8 @@ This proposal does away with fixed fields and proposes using a slots array
131133
directly, without a wrapper struct.
132134

133135
The ``PyModuleDef_Slot`` struct does have some downsides compared to fixed fields.
134-
We believe these are fixable, but leave that out of scope of this PEP
135-
(see “Improving slots in general” in the Possible Future Directions section).
136+
We believe these are fixable, but leave that out of scope of this PEP.
137+
(Note: this was done in :pep:`820`, still in Python 3.15.)
136138

137139

138140
Tokens
@@ -187,6 +189,8 @@ like this:
187189
188190
PyModuleDef_Slot *PyModExport_<NAME>(void);
189191
192+
.. note:: :pep:`820` changed the return type to ``PySlot *``.
193+
190194
where ``<NAME>`` is the name of the module.
191195
For non-ASCII names, it will instead look for ``PyModExportU_<NAME>``,
192196
with ``<NAME>`` encoded as for existing ``PyInitU_*`` hooks
@@ -223,14 +227,13 @@ A new function will be added to create a module from an array of slots:
223227
224228
PyObject *PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *slots, PyObject *spec)
225229
230+
.. note:: :pep:`820` changed the first argument type to ``PySlot *``.
231+
226232
The *slots* argument must point to an array of ``PyModuleDef_Slot`` structures,
227233
terminated by a slot with ``slot=0`` (typically written as ``{0}`` in C).
228-
There are no required slots, though *slots* must not be ``NULL``.
229-
It follows that minimal input contains only the terminator slot.
230-
231-
.. note::
232-
233-
If :pep:`803` is accepted, the ``Py_mod_abi`` slot will be mandatory.
234+
The ``Py_mod_abi`` slot is required (see :pep:`803`); all other slots
235+
are optional.
236+
It follows that *slots* must not be ``NULL``.
234237

235238
The *spec* argument is a duck-typed ModuleSpec-like object, meaning that any
236239
attributes defined for ``importlib.machinery.ModuleSpec`` have matching
@@ -373,7 +376,7 @@ Bits & Pieces
373376
-------------
374377

375378
A ``PyMODEXPORT_FUNC`` macro will be added, similar to the ``PyMODINIT_FUNC``
376-
macro but with ``PyModuleDef_Slot *`` as the return type.
379+
macro but with ``PySlot *`` as the return type.
377380

378381
A ``PyModule_GetStateSize`` function will be added to retrieve the size set
379382
by ``Py_mod_state_size`` or ``PyModuleDef.m_size``.
@@ -397,6 +400,9 @@ The ``PyInit_*`` export hook will be
397400

398401
New API summary
399402
---------------
403+
404+
.. note:: This summary was updated with a change from :pep:`820`.
405+
400406
Python will load a new module export hook, with two variants:
401407

402408
.. code-block:: c
@@ -408,7 +414,7 @@ The following functions will be added:
408414

409415
.. code-block:: c
410416
411-
PyObject *PyModule_FromSlotsAndSpec(const PyModuleDef_Slot *, PyObject *spec)
417+
PyObject *PyModule_FromSlotsAndSpec(const PySlot *, PyObject *spec)
412418
int PyModule_Exec(PyObject *)
413419
int PyModule_GetToken(PyObject *, void**)
414420
PyObject *PyType_GetModuleByToken(PyTypeObject *type, const void *token)
@@ -477,6 +483,15 @@ Here is a guide to convert an existing module to the new API, including
477483
some tricky edge cases.
478484
It should be moved to a HOWTO in the documentation.
479485

486+
.. note::
487+
488+
The guide is available at :ref:`py3.15:abi3t-howto-modexport`.
489+
(It is part of the ``abi3t`` migration HOWTO since switching to
490+
``PyModExport`` doesn't bring benefits in 3.15 if you don't also
491+
adopt ``abi3t``.)
492+
493+
This section contains the original, outdated guide.
494+
480495
This guide is meant for hand-written modules. For code generators and language
481496
wrappers, the :ref:`pep793-shim` below may be more useful.
482497

@@ -585,24 +600,29 @@ The following implementation can be copied and pasted to a project; only the
585600
names ``PyInit_examplemodule`` (twice) and ``PyModExport_examplemodule`` should
586601
need adjusting.
587602

588-
When added to the :ref:`pep793-example` below and compiled with a
589-
non-free-threaded build of this PEP's reference implementation, the resulting
590-
extension is compatible with non-free-threading 3.9+ builds, in addition to a
603+
.. note::
604+
605+
This section was updated for :pep:`820`.
606+
607+
When compiled together with the :ref:`pep793-example` below on a
608+
non-free-threaded build of Python 3.15, the resulting
609+
extension is compatible with non-free-threading 3.11+ builds, in addition to a
591610
free-threading build of the reference implementation.
592611
(The module must be named without a version tag, e.g. ``examplemodule.so``,
593612
and be placed on ``sys.path``.)
594613

595614
Full support for creating such modules will require backports of some new
596615
API, and support in build/install tools. This is out of scope of this PEP.
597616
(In particular, the demo “cheats” by using a subset of Limited API 3.15 that
598-
*happens to work* on 3.9; a proper implementation would use Limited API 3.9
599-
with backport shims for new API like ``Py_mod_name``.)
617+
*happens to work* on 3.11, and includes a few hacks.
618+
A proper implementation would use Limited API 3.11 with cleaner backport shims
619+
for new API like ``Py_mod_name``.)
600620

601621
This implementation places a few additional requirements on the slots array:
602622

603-
- Slots that correspond to ``PyModuleDef`` members must come first.
623+
- ``Py_mod_slots`` and ``Py_slot_subslots`` are not supported.
604624
- A ``Py_mod_name`` slot is required.
605-
- Any ``Py_mod_token`` must be set to ``&module_def_and_token``, defined here.
625+
- Any ``Py_mod_token`` must be set to the ``MOD_TOKEN`` defined here.
606626

607627
.. literalinclude:: pep-0793/shim.c
608628
:language: c
@@ -626,6 +646,10 @@ be added as a new HOWTO.
626646
Example
627647
=======
628648

649+
.. note::
650+
651+
The example was updated for :pep:`820`.
652+
629653
.. literalinclude:: pep-0793/examplemodule.c
630654
:language: c
631655

@@ -693,6 +717,10 @@ These ideas are out of scope for *this* proposal.
693717
Improving slots in general
694718
--------------------------
695719

720+
.. note::
721+
722+
This idea was implemented in :pep:`820`.
723+
696724
Slots -- and specifically the existing ``PyModuleDef_Slot`` -- do have a few
697725
shortcomings. The most important are:
698726

0 commit comments

Comments
 (0)