From ca4f78f54994f766957b5732a5c810015b709738 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Fri, 31 Oct 2025 16:15:08 +0100 Subject: [PATCH 01/70] Fix zlib example --- peps/pep-0804.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 239567c86d3..f137d4bcb2e 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -682,10 +682,10 @@ for brevity, could look like: "mappings": [ { "id": "dep:generic/zlib", - "description": "zlib data compression library for the next generation systems. From zlib-ng/zlib-ng.", - "specs": "zlib-ng", // Simplest form + "description": "Massively spiffy yet delicately unobtrusive compression library.", + "specs": "zlib", // Simplest form "urls": { - "feedstock": "https://github.com/conda-forge/zlib-ng-feedstock" + "feedstock": "https://github.com/conda-forge/zlib-feedstock" } }, { From b6adf805b2f9b3838ef35e86f416d14b7f6f9f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 31 Oct 2025 20:37:41 +0100 Subject: [PATCH 02/70] PEP 596: Add 3.9.25, mark as EOL --- peps/pep-0596.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/peps/pep-0596.rst b/peps/pep-0596.rst index eee2eeaa277..11289f3a8d7 100644 --- a/peps/pep-0596.rst +++ b/peps/pep-0596.rst @@ -2,7 +2,7 @@ PEP: 596 Title: Python 3.9 Release Schedule Author: Łukasz Langa Discussions-To: https://discuss.python.org/t/pep-596-python-3-9-release-schedule-doubling-the-release-cadence/1828 -Status: Active +Status: Final Type: Informational Topic: Release Created: 04-Jun-2019 @@ -99,6 +99,7 @@ Provided irregularly on an "as-needed" basis until October 2025. - 3.9.22: Tuesday, 2025-04-08 - 3.9.23: Tuesday, 2025-06-03 - 3.9.24: Thursday, 2025-10-09 +- 3.9.25: Thursday, 2025-10-31 3.9 Lifespan @@ -107,10 +108,14 @@ Provided irregularly on an "as-needed" basis until October 2025. 3.9 received bugfix updates approximately every 2 months for approximately 18 months. Some time after the release of 3.10.0 final, the ninth and final 3.9 bugfix update was released. After that, -it is expected that security updates (source only) will be released -until 5 years after the release of 3.9 final, so until approximately -October 2025. - +security updates (source only) were released +until October 31st 2025, that is 5 years after the release of 3.9 final. + +As of 2025-10-31, 3.9 has reached the +`end-of-life phase `_ +of its release cycle. 3.9.25 was the final security release. +The codebase for 3.9 is now frozen and no further updates will be +provided nor issues of any kind will be accepted on the bug tracker. Features for 3.9 ================ From a64b84c3b34965921d5e578858c9037f8f6e5a86 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Sat, 1 Nov 2025 10:07:18 +0100 Subject: [PATCH 03/70] 804: Rename `host` to `build_host` (#6) --- peps/pep-0804.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index f137d4bcb2e..274ad508973 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -230,7 +230,7 @@ of dictionaries, in which each entry consists of: - a ``specs`` field whose value MUST be one of: - - a dictionary with three keys (``build``, ``host``, ``run``). The values + - a dictionary with three keys (``build``, ``build_host``, ``run``). The values MUST be a string or list of strings representing the ecosystem-specific package identifiers as needed as build-, host- and runtime dependencies (see PEP 725 for details on these definitions). @@ -514,9 +514,9 @@ Each entry in this list is defined as: - Hyperlinks to web locations that provide more information about the definition. - False * - ``specs`` - - ``string | list[string] | dict[Literal['build', 'host', 'run'], string | list[string]]`` + - ``string | list[string] | dict[Literal['build', 'build_host', 'run'], string | list[string]]`` - Ecosystem-specific identifiers for this package. The full form is a dictionary - that maps the categories ``build``, ``host`` and ``run`` to their corresponding + that maps the categories ``build``, ``build_host`` and ``run`` to their corresponding package identifiers. As a shorthand, a single string or a list of strings can be provided, in which case will be used to populate the three categories identically. - Either ``specs`` or ``specs_from`` MUST be present. @@ -693,7 +693,7 @@ for brevity, could look like: "description": "WebP image library. libwebp-base ships libraries; libwebp ships the binaries.", "specs": { // expanded form with single spec per category "build": "libwebp", - "host": "libwebp-base", + "build_host": "libwebp-base", "run": "libwebp" }, "urls": { @@ -708,7 +708,7 @@ for brevity, could look like: "clang", "clangxx" ], - "host": [ + "build_host": [ "clangdev" ], "run": [ @@ -832,7 +832,7 @@ plus ``meson-python`` as the build backend: build-requires = [ "dep:virtual/compiler/cxx", ] - host-requires = [ + build-host-requires = [ "dep:generic/zlib", ] @@ -847,18 +847,18 @@ following: build-requires = [ "dep:virtual/compiler/cxx", ] - host-requires = [ + build-host-requires = [ "dep:generic/zlib", ] # show all external dependencies, but mapped to the autodetected ecosystem $ python -m pyproject_external show --output=mapped . [external] - build_requires = [ + build-requires = [ "g++", "python3", ] - host_requires = [ + build-host-requires = [ "zlib1g", "zlib1g-dev", ] From f2214371e1896e9b5dcbb21d415a907af893d291 Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Sun, 2 Nov 2025 16:45:03 -0500 Subject: [PATCH 04/70] PEP 788: Change my email address (#4683) --- peps/pep-0788.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0788.rst b/peps/pep-0788.rst index d933316f97f..835234cbc23 100644 --- a/peps/pep-0788.rst +++ b/peps/pep-0788.rst @@ -1,6 +1,6 @@ PEP: 788 Title: Protecting the C API from Interpreter Finalization -Author: Peter Bierma +Author: Peter Bierma Sponsor: Victor Stinner Discussions-To: https://discuss.python.org/t/104150 Status: Draft From 6a0138ccaa20065692cc9f4d033ea95b3a607afd Mon Sep 17 00:00:00 2001 From: jaimergp Date: Mon, 3 Nov 2025 14:45:41 +0100 Subject: [PATCH 05/70] 804: Reject existing databases (#2) --- peps/pep-0804.rst | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 274ad508973..cdec517d6f0 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -1199,7 +1199,6 @@ The reasons include: discrepancies across target ecosystems, where different versions may be available or required. - Mapping PyPI projects to repackaged counterparts in target ecosystems --------------------------------------------------------------------- @@ -1263,6 +1262,36 @@ We suggest simply checking that the provided identifiers are well-formed. Future work may choose to also enforce that the identifiers are recognized as canonical, once the central registry has matured with significant adoption. +Reusing existing databases as a central registry +------------------------------------------------ + +A cursory online search for cross-ecosystem databases of packages would reveal +different sets of results close to the needs of this proposal, but not quite there. +For example: + +- Some solutions only focus on Linux distributions or Unix systems, + like Repology_ or `pkgs.org `__. +- Other services like `Libraries.io `__ require a login. +- Other providers like `ecosyste.ms `__ are only available via APIs. +- The service `purldb `__ only focuses + on collecting concrete PURLs (which identify specific package artifacts), + instead of abstract PURLs concerned with identifying input requirements. + +The proposed mappings try to be as lightweight as possible, without +requiring the maintenance of a live server and an API. Simply a collection +of static JSON files that can be easily updated and distributed online and offline. + +If in the future a service exists providing the following features, then it would +be a strong contender for superseding this PEP: + +- Provides mappings between source DepURLs, PURLs and their repackaged counterparts. + This implies that PURLs have gained the notion of virtual packages and ergonomic + version range expressions. +- Can generate package manager instructions for a given input PURL. +- Can be distributed as local artifacts for offline consumption. +- Does not require a live server or an API. +- FOSS-licensed. + Open Issues =========== From b0e0364e58322cf8a8524e19174e0f12b194a6fc Mon Sep 17 00:00:00 2001 From: jaimergp Date: Mon, 3 Nov 2025 14:53:47 +0100 Subject: [PATCH 06/70] 804: Reject "Tracking package name changes" (#7) --- peps/pep-0804.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index cdec517d6f0..dfaf23fc4a4 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -1262,6 +1262,27 @@ We suggest simply checking that the provided identifiers are well-formed. Future work may choose to also enforce that the identifiers are recognized as canonical, once the central registry has matured with significant adoption. +Tracking package name changes +----------------------------- + +Packaging ecosystems tend to correct, extend and evolve the naming schemes used. +It is common to split what started as a monolithic build into smaller components +(e.g. avoid shipping development files to runtime-only environments, saving bandwidth). +Some practitioners also use the package name to track ABI compatibility across SONAME +changes. The reasons may be multiple and diverse, but the problem is the same: a +given upstream project name may be distributed as different names over time. + +A proposal to track these changes in the mapping suggested the inclusion of additional +date fields (such as ``valid_from`` and ``valid_to``), but the authors decided to reject +this idea. It adds complexity to the implementation, it is difficult to maintain up-to-date, +and doesn't add value to the end-user, simply serving as a historical record. + +Instead, we expect that versioned distributions maintain a separate mapping per release +(see the proposed mapping naming schemes). Rolling ecosystems should strive to keep +alias packages around, with deprecation warnings if needed and feasible. In general, we +also recommend keeping the mapping files under public version control so end-users +can refer to older versions if necessary. + Reusing existing databases as a central registry ------------------------------------------------ From d5ba5ec8383ea89195e6adaefee6699a445bc9a9 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 3 Nov 2025 14:17:55 -0500 Subject: [PATCH 07/70] PEP 810: Mark as accepted and address SC's feedback (#4684) Signed-off-by: Pablo Galindo --- peps/pep-0810.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/peps/pep-0810.rst b/peps/pep-0810.rst index c2b7e2a2e39..ed12385a0dd 100644 --- a/peps/pep-0810.rst +++ b/peps/pep-0810.rst @@ -8,12 +8,13 @@ Author: Pablo Galindo , Noah Kim , Tim Stumbaugh Discussions-To: https://discuss.python.org/t/104131 -Status: Draft +Status: Accepted Type: Standards Track Created: 02-Oct-2025 Python-Version: 3.15 Post-History: `03-Oct-2025 `__, +Resolution: `03-Nov-2025 `__ Abstract @@ -361,6 +362,15 @@ being imported, and (if applicable) the fromlist. An import remains lazy only if the filter function returns ``True``. If no lazy import filter is set, all *potentially lazy* imports are lazy. +Note on .pth files +~~~~~~~~~~~~~~~~~~ + +The lazy import mechanism does not apply to .pth files processed by the +``site`` module. While .pth files have special handling for lines that begin +with ``import`` followed by a space or tab, this special handling will not be +adapted to support lazy imports. Imports specified in .pth files remain eager +as they always have been. + Lazy objects ------------ @@ -617,6 +627,9 @@ lazy imports filter: ``"normal"`` (respect ``lazy`` keyword only), ``"all"`` (force all imports to be potentially lazy), or ``"none"`` (force all imports to be eager). +* ``sys.get_lazy_imports()`` - Returns the current lazy imports mode as a + string: ``"normal"``, ``"all"``, or ``"none"``. + The filter function is called for every potentially lazy import, and must return ``True`` if the import should be lazy. This allows for fine-grained control over which imports should be lazy, useful for excluding modules with @@ -692,6 +705,11 @@ The global lazy imports flag can be controlled through: * The ``PYTHON_LAZY_IMPORTS=`` environment variable * The ``sys.set_lazy_imports(mode)`` function (primarily for testing) +The precedence order for setting the lazy imports mode follows the standard +Python pattern: ``sys.set_lazy_imports()`` takes highest precedence, followed +by ``-X lazy_imports=``, then ``PYTHON_LAZY_IMPORTS=``. If none +are specified, the mode defaults to ``"normal"``. + Where ```` can be: * ``"normal"`` (or unset): Only explicitly marked lazy imports are lazy From 433d90427af2998dd3ef508443d6c0d8863e1db7 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Mon, 3 Nov 2025 15:18:50 -0500 Subject: [PATCH 08/70] PEP 807: use RFC 9457 for error responses (#4635) --- peps/pep-0807.rst | 48 +++++++++++------------------------------------ 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/peps/pep-0807.rst b/peps/pep-0807.rst index 1e54e30241c..adff6c70f15 100644 --- a/peps/pep-0807.rst +++ b/peps/pep-0807.rst @@ -125,6 +125,11 @@ apply to all parts of this PEP's specification: Receiving servers **SHOULD** respond with a ``406 Not Acceptable`` status code if any other ``Accept`` header is present. +* Unless otherwise specified, all error (4xx and 5xx) responses from the server + **MUST** use the :rfc:`9457` (Problem Details for HTTP APIs) format. + In particular, the server **MUST** use the "Problem Details JSON Object" + defined in :rfc:`Section 3 <9457#section-3>` and **SHOULD** use + the ``application/problem+json`` media type in its responses. Trusted Publishing Discovery ---------------------------- @@ -189,16 +194,10 @@ The discovery mechanism is as follows: If the server does not support Trusted Publishing for the given upload URL, it **MUST** respond with a ``404 Not Found`` status code. -When responding with a ``404 Not Found``, the server **SHOULD NOT** -include a response body. If a response body is included, it **MUST** -be ignored by the client. Servers **MAY** additionally respond with any other standard HTTP -error code in the 400 or 500 range to indicate an error condition. - -Non-``200 OK``, non-``404 Not Found`` responses **MAY** include a body which, -if present, **MUST** be a JSON object containing an -`Error Response `__. +error code in the 400 or 500 range to indicate an appropriate error +condition. Trusted Publishing Token Exchange --------------------------------- @@ -233,11 +232,8 @@ containing a JSON object with the following field: - ``audience``: a string containing the expected OIDC audience. -On failure, the server **MUST** respond with any standard HTTP -error code in the 400 or 500 range to indicate an error condition. -Failure responses **MAY** include a body which, if present, -**MUST** be a JSON object containing an -`Error Response `__. +On failure, the server **MUST** respond with a standard HTTP +error code in the 400 or 500 range to indicate the appropriate error condition. Token Minting ~~~~~~~~~~~~~ @@ -277,29 +273,7 @@ containing a JSON object with the following fields: above) to determine when to refresh the upload credential, if needed. On failure, the server **MUST** respond with any standard HTTP -error code in the 400 or 500 range to indicate an error condition. -Failure responses **MUST** include a body which, if present, -**MUST** be a JSON object containing an `Error Response `__. - -Error Responses ---------------- - -When an error response body is included, it **MUST** be a JSON object -containing the following fields: - -- ``message``: a string containing a short, high-level - human-readable summary of the error. - -- ``errors``: an array of one or more objects, each containing - the following fields: - - - ``code``: a string containing a machine-readable error code. - - ``description``: a string containing a human-readable - description of the error. - -This PEP does not specify any particular error codes. Clients **SHOULD NOT** -assume that error codes are consistent across different indices, and instead -**MUST** treat error codes as opaque strings. +error code in the 400 or 500 range to indicate the appropriate error condition. Security Implications ===================== @@ -417,7 +391,7 @@ Footnotes .. [#fn-hash] - The discovery key may be computed thus: + The discovery key may be computed thusly: .. code-block:: pycon From 107eeb54b6183db522cf8ce2e0c273e34c2a2610 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 4 Nov 2025 18:18:36 +0300 Subject: [PATCH 09/70] PEP 791: Mark as Final (#4671) --- peps/pep-0791.rst | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/peps/pep-0791.rst b/peps/pep-0791.rst index 573619bfb56..ff1400ff49c 100644 --- a/peps/pep-0791.rst +++ b/peps/pep-0791.rst @@ -1,15 +1,22 @@ PEP: 791 Title: math.integer --- submodule for integer-specific mathematics functions -Author: Sergey B Kirpichev +Author: Neil Girdhar , + Sergey B Kirpichev , + Tim Peters , + Serhiy Storchaka Sponsor: Victor Stinner Discussions-To: https://discuss.python.org/t/92548 -Status: Draft +Status: Final Type: Standards Track Created: 12-May-2025 Python-Version: 3.15 Post-History: `12-Jul-2018 `__, `09-May-2025 `__, `19-May-2025 `__, +Resolution: `23-Oct-2025 `__ + + +.. canonical-doc:: `math.integer — integer-specific mathematics functions `_ Abstract @@ -146,8 +153,8 @@ Module functions will accept integers and objects that implement the object to an integer number. Suitable functions must be computed exactly, given sufficient time and memory. -The :pypi:`intmath` package will provide new submodule content for older Python -versions. +The :pypi:`intmath` package, available on PyPI, will provide new submodule +content for older Python versions. Possible Extensions @@ -169,8 +176,8 @@ compatible interface for the stdlib. Backwards Compatibility ======================= -As aliases in :external+py3.14:mod:`math` will be kept for an indefinite time -(their use would be discouraged), there are no anticipated code breaks. +As aliases in :external+py3.14:mod:`math` will be kept indefinitely (their use +would be discouraged), there are no anticipated code breaks. How to Teach This @@ -201,10 +208,11 @@ isqrt() renaming --------------------------------------------- There was a brief discussion about exposing :external+py3.14:func:`math.isqrt` -as ``imath.sqrt`` in the same way that :external+py3.14:func:`cmath.sqrt` is -the complex version of :external+py3.14:func:`math.sqrt`. However, ``isqrt`` -is ultimately a different function: it is the floor of the square root. It -would be confusing to give it the same name (under a different module). +as ``sqrt`` in the new namespace in the same way that +:external+py3.14:func:`cmath.sqrt` is the complex version of +:external+py3.14:func:`math.sqrt`. However, ``isqrt`` is ultimately a +different function: it is the floor of the square root. It would be confusing +to give it the same name (under a different submodule). Module name @@ -218,15 +226,15 @@ Other proposed names include ``ntheory`` (like SymPy's submodule), But the SC prefers a submodule rather than a new top-level module. Most popular variants of the :external+py3.14:mod:`math`'s submodule are: -``integer``, ``discrete`` or ``ntheory`` (author preference). +``integer``, ``discrete`` or ``ntheory``. Acknowledgements ================ -Thanks to Tim Peters for reviving the idea of splitting the -:external+py3.14:mod:`math` module. Thanks to Neil Girdhar for substantial -improvements of the initial draft. +Thanks to Victor Stinner for sponsoring this PEP. +Thanks to everyone who participated in the discussions on discuss.python.org, +providing feedback, especially to Oscar Benjamin, Steve Dower and Paul Moore. Copyright From 0ce188404b204aedb88271f502a284eebee67646 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 4 Nov 2025 12:24:22 -0800 Subject: [PATCH 10/70] PEP 772: Add link to board resolution. (#4687) --- peps/pep-0772.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/peps/pep-0772.rst b/peps/pep-0772.rst index 3935ae78f85..6857e70bc51 100644 --- a/peps/pep-0772.rst +++ b/peps/pep-0772.rst @@ -445,8 +445,8 @@ this relationship would be figured out by the inaugural Council. Appendix A: PEP approval process ================================ -This PEP likely requires an atypical approval process, given the parties that must agree. To that -end, the authors will submit this PEP +This PEP requires an atypical approval process, given the parties that must agree. To that end, the +authors will submit this PEP #. for a vote with the PSF Board, which must approve the linking of Packaging Council Electors to the PSF Membership, and the deactivation of the Packaging Workgroup. @@ -457,6 +457,8 @@ end, the authors will submit this PEP enforce the PSF Code of Conduct, in addition to enforcement mechanisms otherwise approved by the Foundation. - Requested language added in `PR 4550 `_. + - Resolution from `PSF Board 2025-08-13 minutes + `__. #. for a vote on the pypa-committers mailing list, in accordance with the process outlined in :pep:`609` From 9a3ab9c1f33291353b5e29713aa063dee32a3290 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 6 Nov 2025 20:32:09 -0800 Subject: [PATCH 11/70] Normalize Pablo's name across all PEPs (#4689) --- peps/pep-0570.rst | 2 +- peps/pep-0617.rst | 2 +- peps/pep-0626.rst | 2 +- peps/pep-0641.rst | 2 +- peps/pep-0648.rst | 2 +- peps/pep-0657.rst | 2 +- peps/pep-0701.rst | 2 +- peps/pep-0705.rst | 2 +- peps/pep-0758.rst | 2 +- peps/pep-0760.rst | 2 +- peps/pep-0799.rst | 2 +- peps/pep-0810.rst | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/peps/pep-0570.rst b/peps/pep-0570.rst index 4bedf5cf581..fbd49620a3d 100644 --- a/peps/pep-0570.rst +++ b/peps/pep-0570.rst @@ -1,7 +1,7 @@ PEP: 570 Title: Python Positional-Only Parameters Author: Larry Hastings , - Pablo Galindo , + Pablo Galindo Salgado , Mario Corchero , Eric N. Vander Weele BDFL-Delegate: Guido van Rossum diff --git a/peps/pep-0617.rst b/peps/pep-0617.rst index ecc376d3261..4d146c6b122 100644 --- a/peps/pep-0617.rst +++ b/peps/pep-0617.rst @@ -1,7 +1,7 @@ PEP: 617 Title: New PEG parser for CPython Author: Guido van Rossum , - Pablo Galindo , + Pablo Galindo Salgado , Lysandros Nikolaou Discussions-To: python-dev@python.org Status: Final diff --git a/peps/pep-0626.rst b/peps/pep-0626.rst index cd6a1d70ed3..1d8b54ae6c8 100644 --- a/peps/pep-0626.rst +++ b/peps/pep-0626.rst @@ -1,7 +1,7 @@ PEP: 626 Title: Precise line numbers for debugging and other tools. Author: Mark Shannon -BDFL-Delegate: Pablo Galindo +BDFL-Delegate: Pablo Galindo Salgado Status: Final Type: Standards Track Created: 15-Jul-2020 diff --git a/peps/pep-0641.rst b/peps/pep-0641.rst index 393133b88e6..2e3771eb257 100644 --- a/peps/pep-0641.rst +++ b/peps/pep-0641.rst @@ -3,7 +3,7 @@ Title: Using an underscore in the version portion of Python 3.10 compatibility t Author: Brett Cannon , Steve Dower , Barry Warsaw -PEP-Delegate: Pablo Galindo +PEP-Delegate: Pablo Galindo Salgado Discussions-To: https://discuss.python.org/t/pep-641-using-an-underscore-in-the-version-portion-of-python-3-10-compatibility-tags/5513 Status: Rejected Type: Standards Track diff --git a/peps/pep-0648.rst b/peps/pep-0648.rst index 19b4e287b66..b02c477d394 100644 --- a/peps/pep-0648.rst +++ b/peps/pep-0648.rst @@ -1,7 +1,7 @@ PEP: 648 Title: Extensible customizations of the interpreter at startup Author: Mario Corchero -Sponsor: Pablo Galindo +Sponsor: Pablo Galindo Salgado Discussions-To: https://discuss.python.org/t/pep-648-extensible-customizations-of-the-interpreter-at-startup/6403 Status: Rejected Type: Standards Track diff --git a/peps/pep-0657.rst b/peps/pep-0657.rst index 55005c2f8bb..ec83803d244 100644 --- a/peps/pep-0657.rst +++ b/peps/pep-0657.rst @@ -1,6 +1,6 @@ PEP: 657 Title: Include Fine Grained Error Locations in Tracebacks -Author: Pablo Galindo , +Author: Pablo Galindo Salgado , Batuhan Taskaya , Ammar Askar Discussions-To: https://discuss.python.org/t/pep-657-include-fine-grained-error-locations-in-tracebacks/8629 diff --git a/peps/pep-0701.rst b/peps/pep-0701.rst index d14c1929c3d..3a6f81b3855 100644 --- a/peps/pep-0701.rst +++ b/peps/pep-0701.rst @@ -1,6 +1,6 @@ PEP: 701 Title: Syntactic formalization of f-strings -Author: Pablo Galindo , +Author: Pablo Galindo Salgado , Batuhan Taskaya , Lysandros Nikolaou , Marta Gómez Macías diff --git a/peps/pep-0705.rst b/peps/pep-0705.rst index cae156d4127..44908d69213 100644 --- a/peps/pep-0705.rst +++ b/peps/pep-0705.rst @@ -1,7 +1,7 @@ PEP: 705 Title: TypedDict: Read-only items Author: Alice Purcell -Sponsor: Pablo Galindo +Sponsor: Pablo Galindo Salgado Discussions-To: https://discuss.python.org/t/pep-705-read-only-typeddict-items/37867 Status: Final Type: Standards Track diff --git a/peps/pep-0758.rst b/peps/pep-0758.rst index 9660dbddd88..8f75e145380 100644 --- a/peps/pep-0758.rst +++ b/peps/pep-0758.rst @@ -1,6 +1,6 @@ PEP: 758 Title: Allow ``except`` and ``except*`` expressions without parentheses -Author: Pablo Galindo , Brett Cannon +Author: Pablo Galindo Salgado , Brett Cannon Status: Final Type: Standards Track Created: 30-Sep-2024 diff --git a/peps/pep-0760.rst b/peps/pep-0760.rst index 721944c3d03..31b5c4a4143 100644 --- a/peps/pep-0760.rst +++ b/peps/pep-0760.rst @@ -1,6 +1,6 @@ PEP: 760 Title: No More Bare Excepts -Author: Pablo Galindo , Brett Cannon +Author: Pablo Galindo Salgado , Brett Cannon Status: Withdrawn Type: Standards Track Created: 02-Oct-2024 diff --git a/peps/pep-0799.rst b/peps/pep-0799.rst index bc7c5098f3c..b941420f147 100644 --- a/peps/pep-0799.rst +++ b/peps/pep-0799.rst @@ -1,6 +1,6 @@ PEP: 799 Title: A dedicated ``profiling`` package for organizing Python profiling tools -Author: Pablo Galindo , +Author: Pablo Galindo Salgado , László Kiss Kollár Discussions-To: https://discuss.python.org/t/pep-799-a-dedicated-profilers-package-for-organizing-python-profiling-tool/100898 Status: Accepted diff --git a/peps/pep-0810.rst b/peps/pep-0810.rst index ed12385a0dd..db84fa16f80 100644 --- a/peps/pep-0810.rst +++ b/peps/pep-0810.rst @@ -1,6 +1,6 @@ PEP: 810 Title: Explicit lazy imports -Author: Pablo Galindo , +Author: Pablo Galindo Salgado , Germán Méndez Bravo , Thomas Wouters , Dino Viehland , From 06c423e5d575291e202d624694e3378d77a31450 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:03:13 +0200 Subject: [PATCH 12/70] PEP 596: 2025-10-31 was a Friday (#4692) --- peps/pep-0596.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0596.rst b/peps/pep-0596.rst index 11289f3a8d7..9d3731e532d 100644 --- a/peps/pep-0596.rst +++ b/peps/pep-0596.rst @@ -99,7 +99,7 @@ Provided irregularly on an "as-needed" basis until October 2025. - 3.9.22: Tuesday, 2025-04-08 - 3.9.23: Tuesday, 2025-06-03 - 3.9.24: Thursday, 2025-10-09 -- 3.9.25: Thursday, 2025-10-31 +- 3.9.25: Friday, 2025-10-31 3.9 Lifespan From 14ba26f71a04ec19de4499b48bcb39f1bebfa2b6 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:08:26 +0200 Subject: [PATCH 13/70] PEP 745: Add planned bugfix releases for Python 3.14 (#4691) --- peps/pep-0745.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/peps/pep-0745.rst b/peps/pep-0745.rst index 0e8530c5286..134936793e3 100644 --- a/peps/pep-0745.rst +++ b/peps/pep-0745.rst @@ -53,9 +53,28 @@ Actual: - 3.14.0 candidate 3: Thursday, 2025-09-18 - 3.14.0 final: Tuesday, 2025-10-07 +Subsequent bugfix releases every two months. + Expected: -Subsequent bugfix releases every two months. +- 3.14.1: Tuesday, 2025-12-02 +- 3.14.2: Tuesday, 2026-02-03 +- 3.14.3: Tuesday, 2026-04-07 +- 3.14.4: Tuesday, 2026-06-09 +- 3.14.5: Tuesday, 2026-08-04 +- 3.14.6: Tuesday, 2026-10-06 +- 3.14.7: Tuesday, 2026-12-01 +- 3.14.8: Tuesday, 2027-02-02 +- 3.14.9: Tuesday, 2027-04-06 +- 3.14.10: Tuesday, 2027-06-01 +- 3.14.11: Tuesday, 2027-08-03 +- 3.14.12: Tuesday, 2027-10-05 + + +Source-only security fix releases +--------------------------------- + +Provided irregularly on an as-needed basis until October 2030. 3.14 lifespan From 84d1499e1bb61db60c593da09848c26f5bdddc1e Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:10:06 +0200 Subject: [PATCH 14/70] Infra: only create peps.json at /api/peps.json (#4231) Only create peps.json at /api/peps.json --- pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py index 2dc3e7ff52d..a87c6b76a0f 100644 --- a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py +++ b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py @@ -55,7 +55,6 @@ def create_pep_json(peps: list[parser.PEP]) -> str: def write_peps_json(peps: list[parser.PEP], path: Path) -> None: # Create peps.json json_peps = create_pep_json(peps) - Path(path, "peps.json").write_text(json_peps, encoding="utf-8") os.makedirs(os.path.join(path, "api"), exist_ok=True) Path(path, "api", "peps.json").write_text(json_peps, encoding="utf-8") From 1b13e95ea8713f0d90b020e1a7ba4e65de735e06 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:03:32 +0000 Subject: [PATCH 15/70] Add python-releases.toml (#4331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Langa Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .pre-commit-config.yaml | 14 + Makefile | 5 + .../pep_zero_generator/pep_index_generator.py | 7 + peps/pep-0569.rst | 78 +- peps/pep-0596.rst | 63 +- peps/pep-0619.rst | 16 +- peps/pep-0664.rst | 18 +- peps/pep-0693.rst | 13 +- peps/pep-0719.rst | 10 + peps/pep-0745.rst | 4 + peps/pep-0790.rst | 4 + release_management/.ruff.toml | 5 + release_management/LICENCE.rst | 2 + release_management/__init__.py | 81 + release_management/__main__.py | 33 + release_management/python-releases.toml | 3536 +++++++++++++++++ release_management/serialize.py | 50 + .../update_release_schedules.py | 179 + requirements.txt | 3 + 19 files changed, 4060 insertions(+), 61 deletions(-) create mode 100644 release_management/.ruff.toml create mode 100644 release_management/LICENCE.rst create mode 100644 release_management/__init__.py create mode 100644 release_management/__main__.py create mode 100644 release_management/python-releases.toml create mode 100644 release_management/serialize.py create mode 100644 release_management/update_release_schedules.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f7f3380ee0..28dc3a896aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -65,6 +65,11 @@ repos: args: - '--exit-non-zero-on-fix' files: '^pep_sphinx_extensions/tests/' + - id: ruff-format + name: "Format with Ruff" + args: + - '--check' + files: '^release_management/' - repo: https://github.com/tox-dev/tox-ini-fmt rev: 1.4.1 @@ -111,3 +116,12 @@ repos: language: "python" files: '^peps/pep-\d{4}\.rst$' require_serial: true + + # Hook to regenerate release schedules + - id: "regen-schedules" + name: "Regenerate release schedules from python-releases.toml" + entry: "python -m release_management update-peps" + language: "python" + files: "^release_management/" + pass_filenames: false + require_serial: true diff --git a/Makefile b/Makefile index 4ec6084b1d1..d15b304dd3c 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,11 @@ test: venv spellcheck: _ensure-pre-commit $(VENVDIR)/bin/python3 -m pre_commit run --all-files --hook-stage manual codespell +## regen-all to regenerate generated source files +.PHONY: regen-all +regen-all: + $(PYTHON) -m release_management update-peps + .PHONY: help help : Makefile @echo "Please use \`make ' where is one of" diff --git a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py index a87c6b76a0f..84787aabb85 100644 --- a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py +++ b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py @@ -26,6 +26,7 @@ from pep_sphinx_extensions.pep_zero_generator import subindices from pep_sphinx_extensions.pep_zero_generator import writer from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC +from release_management.serialize import create_release_cycle, create_release_json if TYPE_CHECKING: from sphinx.application import Sphinx @@ -72,3 +73,9 @@ def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> subindices.generate_subindices(SUBINDICES_BY_TOPIC, peps, docnames, env) write_peps_json(peps, Path(app.outdir)) + + release_cycle = create_release_cycle() + app.outdir.joinpath('api/release-cycle.json').write_text(release_cycle, encoding="utf-8") + + release_json = create_release_json() + app.outdir.joinpath('api/python-releases.json').write_text(release_json, encoding="utf-8") diff --git a/peps/pep-0569.rst b/peps/pep-0569.rst index fc8d32d73ed..bfecefa4afd 100644 --- a/peps/pep-0569.rst +++ b/peps/pep-0569.rst @@ -51,6 +51,10 @@ Release Schedule 3.8.0 schedule -------------- +.. release schedule: feature + +Actual: + - 3.8 development begins: Monday, 2018-01-29 - 3.8.0 alpha 1: Sunday, 2019-02-03 - 3.8.0 alpha 2: Monday, 2019-02-25 @@ -58,51 +62,65 @@ Release Schedule - 3.8.0 alpha 4: Monday, 2019-05-06 - 3.8.0 beta 1: Tuesday, 2019-06-04 (No new features beyond this point.) - - 3.8.0 beta 2: Thursday, 2019-07-04 - 3.8.0 beta 3: Monday, 2019-07-29 - 3.8.0 beta 4: Friday, 2019-08-30 - 3.8.0 candidate 1: Tuesday, 2019-10-01 - 3.8.0 final: Monday, 2019-10-14 +.. release schedule: ends + Bugfix releases --------------- -- 3.8.1rc1: Tuesday, 2019-12-10 -- 3.8.1: Wednesday, 2019-12-18 -- 3.8.2rc1: Monday, 2020-02-10 -- 3.8.2rc2: Monday, 2020-02-17 -- 3.8.2: Monday, 2020-02-24 -- 3.8.3rc1: Wednesday, 2020-04-29 -- 3.8.3: Wednesday, 2020-05-13 -- 3.8.4rc1: Tuesday, 2020-06-30 -- 3.8.4: Monday, 2020-07-13 -- 3.8.5: Monday, 2020-07-20 (security hotfix) -- 3.8.6rc1: Tuesday, 2020-09-08 -- 3.8.6: Thursday, 2020-09-24 -- 3.8.7rc1: Monday, 2020-12-07 -- 3.8.7: Monday, 2020-12-21 -- 3.8.8rc1: Tuesday, 2021-02-16 -- 3.8.8: Friday, 2021-02-19 -- 3.8.9: Friday, 2021-04-02 (security hotfix) -- 3.8.10: Monday, 2021-05-03 (final regular bugfix release with binary - installers) +.. release schedule: bugfix + +Actual: + +- 3.8.1 candidate 1: Tuesday, 2019-12-10 +- 3.8.1 final: Wednesday, 2019-12-18 +- 3.8.2 candidate 1: Monday, 2020-02-10 +- 3.8.2 candidate 2: Monday, 2020-02-17 +- 3.8.2 final: Monday, 2020-02-24 +- 3.8.3 candidate 1: Wednesday, 2020-04-29 +- 3.8.3 final: Wednesday, 2020-05-13 +- 3.8.4 candidate 1: Tuesday, 2020-06-30 +- 3.8.4 final: Monday, 2020-07-13 +- 3.8.5 final: Monday, 2020-07-20 + (security hotfix) +- 3.8.6 candidate 1: Tuesday, 2020-09-08 +- 3.8.6 final: Thursday, 2020-09-24 +- 3.8.7 candidate 1: Monday, 2020-12-07 +- 3.8.7 final: Monday, 2020-12-21 +- 3.8.8 candidate 1: Tuesday, 2021-02-16 +- 3.8.8 final: Friday, 2021-02-19 +- 3.8.9 final: Friday, 2021-04-02 + (security hotfix) +- 3.8.10 final: Monday, 2021-05-03 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases --------------------------------- Provided irregularly on an "as-needed" basis until October 7th 2024. -- 3.8.11: Monday, 2021-06-28 -- 3.8.12: Monday, 2021-08-30 -- 3.8.13: Wednesday, 2022-03-16 -- 3.8.14: Tuesday, 2022-09-06 -- 3.8.15: Tuesday, 2022-10-11 -- 3.8.16: Tuesday, 2022-12-06 -- 3.8.17: Tuesday, 2023-06-06 -- 3.8.18: Thursday, 2023-08-24 -- 3.8.19: Tuesday, 2024-03-19 -- 3.8.20: Friday, 2024-09-06 (final security release) +.. release schedule: security + +- 3.8.11 final: Monday, 2021-06-28 +- 3.8.12 final: Monday, 2021-08-30 +- 3.8.13 final: Wednesday, 2022-03-16 +- 3.8.14 final: Tuesday, 2022-09-06 +- 3.8.15 final: Tuesday, 2022-10-11 +- 3.8.16 final: Tuesday, 2022-12-06 +- 3.8.17 final: Tuesday, 2023-06-06 +- 3.8.18 final: Thursday, 2023-08-24 +- 3.8.19 final: Tuesday, 2024-03-19 +- 3.8.20 final: Friday, 2024-09-06 + (final security release) + +.. release schedule: ends Features for 3.8 diff --git a/peps/pep-0596.rst b/peps/pep-0596.rst index 9d3731e532d..5339a1128c4 100644 --- a/peps/pep-0596.rst +++ b/peps/pep-0596.rst @@ -40,6 +40,8 @@ Note: the dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.9 development begins: Tuesday, 2019-06-04 @@ -59,28 +61,37 @@ Actual: - 3.9.0 candidate 2: Thursday, 2020-09-17 - 3.9.0 final: Monday, 2020-10-05 +.. release schedule: ends + Bugfix releases --------------- +.. release schedule: bugfix + Actual: - 3.9.1 candidate 1: Tuesday, 2020-11-24 - 3.9.1 final: Monday, 2020-12-07 - 3.9.2 candidate 1: Tuesday, 2021-02-16 - 3.9.2 final: Friday, 2021-02-19 -- 3.9.3: Friday, 2021-04-02 (security hotfix; recalled due to bpo-43710) -- 3.9.4: Sunday, 2021-04-04 (ABI compatibility hotfix) -- 3.9.5: Monday, 2021-05-03 -- 3.9.6: Monday, 2021-06-28 -- 3.9.7: Monday, 2021-08-30 -- 3.9.8: Friday, 2021-11-05 (recalled due to bpo-45235) -- 3.9.9: Monday, 2021-11-15 -- 3.9.10: Friday, 2022-01-14 -- 3.9.11: Wednesday, 2022-03-16 -- 3.9.12: Wednesday, 2022-03-23 -- 3.9.13: Tuesday, 2022-05-17 (final regular bugfix release with binary - installers) +- 3.9.3 final: Friday, 2021-04-02 + (security hotfix; recalled due to bpo-43710) +- 3.9.4 final: Sunday, 2021-04-04 + (ABI compatibility hotfix) +- 3.9.5 final: Monday, 2021-05-03 +- 3.9.6 final: Monday, 2021-06-28 +- 3.9.7 final: Monday, 2021-08-30 +- 3.9.8 final: Friday, 2021-11-05 + (recalled due to bpo-45235) +- 3.9.9 final: Monday, 2021-11-15 +- 3.9.10 final: Friday, 2022-01-14 +- 3.9.11 final: Wednesday, 2022-03-16 +- 3.9.12 final: Wednesday, 2022-03-23 +- 3.9.13 final: Tuesday, 2022-05-17 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases @@ -88,18 +99,22 @@ Source-only security fix releases Provided irregularly on an "as-needed" basis until October 2025. -- 3.9.14: Tuesday, 2022-09-06 -- 3.9.15: Tuesday, 2022-10-11 -- 3.9.16: Tuesday, 2022-12-06 -- 3.9.17: Tuesday, 2023-06-06 -- 3.9.18: Thursday, 2023-08-24 -- 3.9.19: Tuesday, 2024-03-19 -- 3.9.20: Friday, 2024-09-06 -- 3.9.21: Tuesday, 2024-12-03 -- 3.9.22: Tuesday, 2025-04-08 -- 3.9.23: Tuesday, 2025-06-03 -- 3.9.24: Thursday, 2025-10-09 -- 3.9.25: Friday, 2025-10-31 +.. release schedule: security + +- 3.9.14 final: Tuesday, 2022-09-06 +- 3.9.15 final: Tuesday, 2022-10-11 +- 3.9.16 final: Tuesday, 2022-12-06 +- 3.9.17 final: Tuesday, 2023-06-06 +- 3.9.18 final: Thursday, 2023-08-24 +- 3.9.19 final: Tuesday, 2024-03-19 +- 3.9.20 final: Friday, 2024-09-06 +- 3.9.21 final: Tuesday, 2024-12-03 +- 3.9.22 final: Tuesday, 2025-04-08 +- 3.9.23 final: Tuesday, 2025-06-03 +- 3.9.24 final: Thursday, 2025-10-09 +- 3.9.25 final: Friday, 2025-10-31 + +.. release schedule: ends 3.9 Lifespan diff --git a/peps/pep-0619.rst b/peps/pep-0619.rst index 1835a9db950..5405e2aee57 100644 --- a/peps/pep-0619.rst +++ b/peps/pep-0619.rst @@ -37,6 +37,8 @@ Note: the dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.10 development begins: Monday, 2020-05-18 @@ -56,9 +58,13 @@ Actual: - 3.10.0 candidate 2: Tuesday, 2021-09-07 - 3.10.0 final: Monday, 2021-10-04 +.. release schedule: ends + Bugfix releases --------------- +.. release schedule: bugfix + Actual: - 3.10.1: Monday, 2021-12-06 @@ -71,14 +77,18 @@ Actual: - 3.10.8: Tuesday, 2022-10-11 - 3.10.9: Tuesday, 2022-12-06 - 3.10.10: Wednesday, 2023-02-08 -- 3.10.11: Wednesday, 2023-04-05 (final regular bugfix release with binary - installers) +- 3.10.11: Wednesday, 2023-04-05 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases --------------------------------- Provided irregularly on an "as-needed" basis until October 2026. +.. release schedule: security + - 3.10.12: Tuesday, 2023-06-06 - 3.10.13: Thursday, 2023-08-24 - 3.10.14: Tuesday, 2024-03-19 @@ -88,6 +98,8 @@ Provided irregularly on an "as-needed" basis until October 2026. - 3.10.18: Tuesday, 2025-06-03 - 3.10.19: Thursday, 2025-10-09 +.. release schedule: ends + 3.10 Lifespan ------------- diff --git a/peps/pep-0664.rst b/peps/pep-0664.rst index 198f2069943..18aa1d11d6b 100644 --- a/peps/pep-0664.rst +++ b/peps/pep-0664.rst @@ -38,6 +38,8 @@ Note: the dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.11 development begins: Monday, 2021-05-03 @@ -56,11 +58,15 @@ Actual: - 3.11.0 beta 5: Tuesday, 2022-07-26 - 3.11.0 candidate 1: Monday, 2022-08-08 - 3.11.0 candidate 2: Monday, 2022-09-12 -- 3.11.0 final: Monday, 2022-10-24 +- 3.11.0 final: Monday, 2022-10-24 + +.. release schedule: ends Bugfix releases --------------- +.. release schedule: bugfix + Actual: - 3.11.1: Tuesday, 2022-12-06 @@ -71,20 +77,26 @@ Actual: - 3.11.6: Monday, 2023-10-02 - 3.11.7: Monday, 2023-12-04 - 3.11.8: Tuesday, 2024-02-06 -- 3.11.9: Tuesday, 2024-04-02 (final regular bugfix release with binary - installers) +- 3.11.9: Tuesday, 2024-04-02 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases --------------------------------- Provided irregularly on an "as-needed" basis until October 2027. +.. release schedule: security + - 3.11.10: Saturday, 2024-09-07 - 3.11.11: Tuesday, 2024-12-03 - 3.11.12: Tuesday, 2025-04-08 - 3.11.13: Tuesday, 2025-06-03 - 3.11.14: Thursday, 2025-10-09 +.. release schedule: ends + 3.11 Lifespan ------------- diff --git a/peps/pep-0693.rst b/peps/pep-0693.rst index 4bfdab160d3..39fed40ea6d 100644 --- a/peps/pep-0693.rst +++ b/peps/pep-0693.rst @@ -32,6 +32,8 @@ Note: the dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.12 development begins: Sunday, 2022-05-08 @@ -50,11 +52,15 @@ Actual: - 3.12.0 candidate 1: Sunday, 2023-08-06 - 3.12.0 candidate 2: Wednesday, 2023-09-06 - 3.12.0 candidate 3: Tuesday, 2023-09-19 -- 3.12.0 final: Monday, 2023-10-02 +- 3.12.0 final: Monday, 2023-10-02 + +.. release schedule: ends Bugfix releases --------------- +.. release schedule: bugfix + Actual: - 3.12.1: Thursday, 2023-12-07 @@ -66,7 +72,10 @@ Actual: - 3.12.7: Tuesday, 2024-10-01 - 3.12.8: Tuesday, 2024-12-03 - 3.12.9: Tuesday, 2025-02-04 -- 3.12.10: Tuesday, 2025-04-08 (final regular bugfix release with binary installers) +- 3.12.10: Tuesday, 2025-04-08 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases --------------------------------- diff --git a/peps/pep-0719.rst b/peps/pep-0719.rst index 52b1169bf90..44eac8deccf 100644 --- a/peps/pep-0719.rst +++ b/peps/pep-0719.rst @@ -33,6 +33,8 @@ Note: the dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.13 development begins: Monday, 2023-05-22 @@ -52,9 +54,13 @@ Actual: - 3.13.0 candidate 3: Tuesday, 2024-10-01 - 3.13.0 final: Monday, 2024-10-07 +.. release schedule: ends + Bugfix releases --------------- +.. release schedule: bugfix + Actual: - 3.13.1: Tuesday, 2024-12-03 @@ -62,6 +68,7 @@ Actual: - 3.13.3: Tuesday, 2025-04-08 - 3.13.4: Tuesday, 2025-06-03 - 3.13.5: Wednesday, 2025-06-11 + (hotfix) - 3.13.6: Wednesday, 2025-08-06 - 3.13.7: Thursday, 2025-08-14 - 3.13.8: Tuesday, 2025-10-07 @@ -75,6 +82,9 @@ Expected: - 3.13.13: Tuesday, 2026-06-09 - 3.13.14: Tuesday, 2026-08-04 - 3.13.15: Tuesday, 2026-10-06 + (Final regular bugfix release with binary installers) + +.. release schedule: ends Source-only security fix releases diff --git a/peps/pep-0745.rst b/peps/pep-0745.rst index 134936793e3..8ca702bb77c 100644 --- a/peps/pep-0745.rst +++ b/peps/pep-0745.rst @@ -33,6 +33,8 @@ The dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.14 development begins: Wednesday, 2024-05-08 @@ -53,6 +55,8 @@ Actual: - 3.14.0 candidate 3: Thursday, 2025-09-18 - 3.14.0 final: Tuesday, 2025-10-07 +.. release schedule: ends + Subsequent bugfix releases every two months. Expected: diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst index 6a67aa7df81..74807181da6 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0790.rst @@ -31,6 +31,8 @@ Release schedule The dates below use a 17-month development period that results in a 12-month release cadence between feature versions, as defined by :pep:`602`. +.. release schedule: feature + Actual: - 3.15 development begins: Wednesday, 2025-05-07 @@ -53,6 +55,8 @@ Expected: - 3.15.0 candidate 2: Tuesday, 2026-09-01 - 3.15.0 final: Thursday, 2026-10-01 +.. release schedule: ends + Subsequent bugfix releases every two months. diff --git a/release_management/.ruff.toml b/release_management/.ruff.toml new file mode 100644 index 00000000000..2aeb6ec8468 --- /dev/null +++ b/release_management/.ruff.toml @@ -0,0 +1,5 @@ +extend = "../.ruff.toml" # Inherit the project-wide settings + +[format] +preview = true +quote-style = "single" diff --git a/release_management/LICENCE.rst b/release_management/LICENCE.rst new file mode 100644 index 00000000000..d68de666acb --- /dev/null +++ b/release_management/LICENCE.rst @@ -0,0 +1,2 @@ +The files in this directory are placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. diff --git a/release_management/__init__.py b/release_management/__init__.py new file mode 100644 index 00000000000..9846219fdc0 --- /dev/null +++ b/release_management/__init__.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +import sys +from dataclasses import dataclass +from pathlib import Path + +try: + import tomllib +except ImportError: + import tomli as tomllib + +TYPE_CHECKING = False +if TYPE_CHECKING: + import datetime as dt + from typing import Literal, TypeAlias + + ReleaseState: TypeAlias = Literal['actual', 'expected'] + ReleaseSchedules: TypeAlias = dict[tuple[str, ReleaseState], list['ReleaseInfo']] + VersionStatus: TypeAlias = Literal[ + 'feature', 'prerelease', 'bugfix', 'security', 'end-of-life' + ] + +RELEASE_DIR = Path(__file__).resolve().parent +ROOT_DIR = RELEASE_DIR.parent +PEP_ROOT = ROOT_DIR / 'peps' + +dc_kw = {'kw_only': True, 'slots': True} if sys.version_info[:2] >= (3, 10) else {} + + +@dataclass(frozen=True, **dc_kw) +class PythonReleases: + metadata: dict[str, VersionMetadata] + releases: dict[str, list[ReleaseInfo]] + + +@dataclass(frozen=True, **dc_kw) +class VersionMetadata: + """Metadata for a given interpreter version (MAJOR.MINOR).""" + + pep: int + status: VersionStatus + branch: str + release_manager: str + start_of_development: dt.date + feature_freeze: dt.date + first_release: dt.date + end_of_bugfix: dt.date # a.k.a. security mode or source-only releases + end_of_life: dt.date + + @classmethod + def from_toml(cls, data: dict[str, int | str | dt.date]): + return cls(**{k.replace('-', '_'): v for k, v in data.items()}) + + +@dataclass(frozen=True, **dc_kw) +class ReleaseInfo: + """Information about a release.""" + + stage: str + state: ReleaseState + date: dt.date + note: str = '' # optional note / comment, displayed in the schedule + + @property + def schedule_bullet(self): + """Return a formatted bullet point for the schedule list.""" + return f'- {self.stage}: {self.date:%A, %Y-%m-%d}' + + +def load_python_releases() -> PythonReleases: + with open(RELEASE_DIR / 'python-releases.toml', 'rb') as f: + python_releases = tomllib.load(f) + all_metadata = { + v: VersionMetadata.from_toml(metadata) + for v, metadata in python_releases['metadata'].items() + } + all_releases = { + v: [ReleaseInfo(**r) for r in releases] + for v, releases in python_releases['release'].items() + } + return PythonReleases(metadata=all_metadata, releases=all_releases) diff --git a/release_management/__main__.py b/release_management/__main__.py new file mode 100644 index 00000000000..5842cab0552 --- /dev/null +++ b/release_management/__main__.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +import argparse + +commands = ( + CMD_FULL_JSON := 'full-json', + CMD_UPDATE_PEPS := 'update-peps', + CMD_RELEASE_CYCLE := 'release-cycle', +) +parser = argparse.ArgumentParser(allow_abbrev=False) +parser.add_argument('COMMAND', choices=commands) + +args = parser.parse_args() +if args.COMMAND == CMD_UPDATE_PEPS: + from release_management.update_release_schedules import update_peps + + raise SystemExit(update_peps()) + +if args.COMMAND == CMD_FULL_JSON: + from release_management import ROOT_DIR + from release_management.serialize import create_release_json + + json_path = ROOT_DIR / 'python-releases.json' + json_path.write_text(create_release_json(), encoding='utf-8') + raise SystemExit(0) + +if args.COMMAND == CMD_RELEASE_CYCLE: + from release_management import ROOT_DIR + from release_management.serialize import create_release_cycle + + json_path = ROOT_DIR / 'release-cycle.json' + json_path.write_text(create_release_cycle(), encoding='utf-8') + raise SystemExit(0) diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml new file mode 100644 index 00000000000..4dc9f1448e6 --- /dev/null +++ b/release_management/python-releases.toml @@ -0,0 +1,3536 @@ +# This file is placed in the public domain or under the +# CC0-1.0-Universal licence, whichever is more permissive. +# +# This document contains the history of every release of the Python project, +# and specifically the CPython intepreter. The data in this file were initially +# compiled in 2025 by Adam Turner, with information primarily sourced from the +# release PEPs and supplemented by the 'releases' section of www.python.org. +# +# The release schedules for Python 3.8 onwards are created from data in this +# document. After editing this file, run the following command to regenerate +# the relevant PEPs: +# +# python -m release_management update-peps +# +# The PEP rendering system, via Sphinx, uses this document to regenerate the +# 'release-cycle' JSON file, found at https://peps.python.org/release-cycle.json, +# and a full JSON representation at https://peps.python.org/python-releases.json, +# This 'release-cycle' JSON file is intended for public consumption. The format +# of this TOML document is not guaranteed and may change without notice. + +# -- Python 1.6 -------------------------------------------------------------- + +# Drake wrote PEP 160, but Hylton served as release manager: +# https://mail.python.org/archives/list/python-dev@python.org/message/FCPLKMFDZUDOQGPAEKGKB2VQYTI4JT7Y/ +# https://jeremyhylton.blogspot.com/2006/05/contributing-to-python.html + +[metadata."1.6"] +pep = 160 +status = "end-of-life" +branch = "" # no branch or tag for 1.6 exists +release-manager = "Guido van Rossum & Jeremy Hylton" +start-of-development = 1999-06-09 +feature-freeze = 2000-08-04 +first-release = 2000-09-05 +end-of-bugfix = 2000-09-05 +end-of-life = 2000-09-05 + +# 1.6.0 alpha {1,2} are not in the PEP, but are found on python-dev at: +# https://mail.python.org/archives/list/python-dev@python.org/message/IOO74NZNEUAHNYJFVK7K3P7RCNMFBE5W/ +# https://mail.python.org/archives/list/python-dev@python.org/message/65J6CYRPPUNCUW523J3EO77RKAY3BS3F/ + +[[release."1.6"]] +stage = "1.6.0 alpha 1" +state = "actual" +date = 2000-03-31 + +[[release."1.6"]] +stage = "1.6.0 alpha 2" +state = "actual" +date = 2000-04-11 + +# PEP 160 records 3 August 2000, but the announcement and source archive date +# from the 4th. +# https://mail.python.org/archives/list/python-dev@python.org/message/IF3YORLE7PTOR2QLMHI6UACGHDHZCAVO/ +# https://legacy.python.org/download/releases/src/python-1.6b1.tar.gz + +[[release."1.6"]] +stage = "1.6.0 beta 1" +state = "actual" +date = 2000-08-04 + +[[release."1.6"]] +stage = "1.6.0 final" +state = "actual" +date = 2000-09-05 + +# 1.6.1 is not in the PEP, but is found on python-announce at: +# https://mail.python.org/archives/list/python-announce-list@python.org/message/NSMG65WRUF5QLJD54H63B57BJFAGI5Y3/ + +[[release."1.6"]] +stage = "1.6.1 final" +state = "actual" +date = 2001-02-25 + +# -- Python 2.0 -------------------------------------------------------------- + +# Python 2.0 feature freeze is listed separately in PEP 200, it isn't beta 1. + +[metadata."2.0"] +pep = 200 +status = "end-of-life" +branch = "2.0" +release-manager = "Guido van Rossum & Jeremy Hylton" +start-of-development = 2000-06-29 +feature-freeze = 2000-08-14 +first-release = 2000-10-16 +end-of-bugfix = 2001-06-22 +end-of-life = 2001-06-22 + +# PEP 200 records 5 September 2000, but the announcement was on the 6th (GMT): +# https://mail.python.org/archives/list/python-dev@python.org/message/3JDD34UGV2L7QP7S37S37RZXXMDOMMIX/ + +[[release."2.0"]] +stage = "2.0.0 beta 1" +state = "actual" +date = 2000-09-06 + +[[release."2.0"]] +stage = "2.0.0 beta 2" +state = "actual" +date = 2000-09-26 + +[[release."2.0"]] +stage = "2.0.0 candidate 1" +state = "actual" +date = 2000-10-09 + +[[release."2.0"]] +stage = "2.0.0 final" +state = "actual" +date = 2000-10-16 + +# 2.0.1 is not in the PEP, but is found on the website at: +# https://www.python.org/downloads/release/python-201/ +# https://www.python.org/ftp/python/2.0.1/ +# https://mail.python.org/archives/list/python-dev@python.org/message/23N7ECY4XTIE3CTTY6LK7K6QLAOD4GLK/ +# https://mail.python.org/archives/list/python-dev@python.org/message/CRIOLY7NPVJCOXWGS4JXQNTKQCUOL23N/ + +[[release."2.0"]] +stage = "2.0.1 candidate 1" +state = "actual" +date = 2001-06-14 + +[[release."2.0"]] +stage = "2.0.1 final" +state = "actual" +date = 2001-06-22 + +# -- Python 2.1 -------------------------------------------------------------- + +[metadata."2.1"] +pep = 226 +status = "end-of-life" +branch = "2.1" +release-manager = "Guido van Rossum & Jeremy Hylton" +start-of-development = 2000-10-16 +feature-freeze = 2001-03-02 +first-release = 2001-04-17 +end-of-bugfix = 2002-04-09 +end-of-life = 2002-04-09 + +[[release."2.1"]] +stage = "2.1.0 alpha 1" +state = "actual" +date = 2001-01-22 + +[[release."2.1"]] +stage = "2.1.0 alpha 2" +state = "actual" +date = 2001-02-02 + +[[release."2.1"]] +stage = "2.1.0 beta 1" +state = "actual" +date = 2001-03-02 + +[[release."2.1"]] +stage = "2.1.0 beta 2" +state = "actual" +date = 2001-03-23 + +[[release."2.1"]] +stage = "2.1.0 candidate 1" +state = "actual" +date = 2001-04-13 + +[[release."2.1"]] +stage = "2.1.0 candidate 2" +state = "actual" +date = 2001-04-15 + +[[release."2.1"]] +stage = "2.1.0 final" +state = "actual" +date = 2001-04-17 + +# 2.1.{1,2,3} are not in the PEP, but are found on the website at: +# https://www.python.org/downloads/release/python-213/ +# https://www.python.org/ftp/python/2.1.1/ +# https://www.python.org/ftp/python/2.1.2/ +# https://www.python.org/ftp/python/2.1.3/ + +[[release."2.1"]] +stage = "2.1.1 candidate 1" +state = "actual" +date = 2002-07-13 + +[[release."2.1"]] +stage = "2.1.1 final" +state = "actual" +date = 2002-07-20 + +[[release."2.1"]] +stage = "2.1.2 candidate 1" +state = "actual" +date = 2002-01-10 + +[[release."2.1"]] +stage = "2.1.2 final" +state = "actual" +date = 2002-01-15 + +[[release."2.1"]] +stage = "2.1.3 final" +state = "actual" +date = 2002-04-09 + +# -- Python 2.2 -------------------------------------------------------------- + +[metadata."2.2"] +pep = 251 +status = "end-of-life" +branch = "2.2" +release-manager = "Barry Warsaw" +start-of-development = 2001-04-18 +feature-freeze = 2001-10-19 +first-release = 2001-12-21 +end-of-bugfix = 2003-05-30 +end-of-life = 2003-05-30 + +[[release."2.2"]] +stage = "2.2.0 alpha 1" +state = "actual" +date = 2001-07-18 + +[[release."2.2"]] +stage = "2.2.0 alpha 2" +state = "actual" +date = 2001-08-22 + +[[release."2.2"]] +stage = "2.2.0 alpha 3" +state = "actual" +date = 2001-09-07 + +[[release."2.2"]] +stage = "2.2.0 alpha 4" +state = "actual" +date = 2001-09-28 + +[[release."2.2"]] +stage = "2.2.0 beta 1" +state = "actual" +date = 2001-10-19 + +[[release."2.2"]] +stage = "2.2.0 beta 2" +state = "actual" +date = 2001-11-14 + +[[release."2.2"]] +stage = "2.2.0 candidate 1" +state = "actual" +date = 2001-12-14 + +[[release."2.2"]] +stage = "2.2.0 final" +state = "actual" +date = 2001-12-21 + +# 2.2.{1,2,3} are not in the PEP, but are found on the website at: +# https://www.python.org/downloads/release/python-221/ +# https://www.python.org/downloads/release/python-222/ +# https://www.python.org/downloads/release/python-223/ +# https://www.python.org/ftp/python/2.2.1/ +# https://www.python.org/ftp/python/2.2.2/ +# https://www.python.org/ftp/python/2.2.3/ + +[[release."2.2"]] +stage = "2.2.1 candidate 1" +state = "actual" +date = 2002-03-18 + +[[release."2.2"]] +stage = "2.2.1 candidate 2" +state = "actual" +date = 2002-03-26 + +[[release."2.2"]] +stage = "2.2.1 final" +state = "actual" +date = 2002-04-10 + +[[release."2.2"]] +stage = "2.2.2 beta 1" +state = "actual" +date = 2002-10-07 + +[[release."2.2"]] +stage = "2.2.2 final" +state = "actual" +date = 2002-10-14 + +[[release."2.2"]] +stage = "2.2.3 candidate 1" +state = "actual" +date = 2003-05-22 + +[[release."2.2"]] +stage = "2.2.3 final" +state = "actual" +date = 2003-05-30 + +# -- Python 2.3 -------------------------------------------------------------- + +[metadata."2.3"] +pep = 283 +status = "end-of-life" +branch = "2.3" +release-manager = "Barry Warsaw, Jeremy Hylton, and Tim Peters" +start-of-development = 2001-12-21 +feature-freeze = 2003-04-25 +first-release = 2003-06-29 +end-of-bugfix = 2008-03-11 +end-of-life = 2008-03-11 + +[[release."2.3"]] +stage = "2.3.0 alpha 1" +state = "actual" +date = 2002-12-31 + +[[release."2.3"]] +stage = "2.3.0 alpha 2" +state = "actual" +date = 2003-02-19 + +[[release."2.3"]] +stage = "2.3.0 beta 1" +state = "actual" +date = 2003-04-25 + +[[release."2.3"]] +stage = "2.3.0 beta 2" +state = "actual" +date = 2003-06-29 + +[[release."2.3"]] +stage = "2.3.0 candidate 1" +state = "actual" +date = 2003-07-18 + +[[release."2.3"]] +stage = "2.3.0 candidate 2" +state = "actual" +date = 2003-07-24 + +[[release."2.3"]] +stage = "2.3.0 final" +state = "actual" +date = 2003-07-29 + +# 2.3.{1,2,3,4,5,6,7} are not in the PEP, but are found on the website at: +# https://www.python.org/downloads/release/python-231/ +# https://www.python.org/downloads/release/python-232/ +# https://www.python.org/downloads/release/python-233/ +# https://www.python.org/downloads/release/python-234/ +# https://www.python.org/downloads/release/python-235/ +# https://www.python.org/downloads/release/python-236/ +# https://www.python.org/downloads/release/python-237/ +# https://www.python.org/ftp/python/2.3.1/ +# https://www.python.org/ftp/python/2.3.2/ +# https://www.python.org/ftp/python/2.3.3/ +# https://www.python.org/ftp/python/2.3.4/ +# https://www.python.org/ftp/python/2.3.5/ +# https://www.python.org/ftp/python/2.3.6/ +# https://www.python.org/ftp/python/2.3.7/ + +[[release."2.3"]] +stage = "2.3.1 candidate 1" +state = "actual" +date = 2003-09-23 + +[[release."2.3"]] +stage = "2.3.1 final" +state = "actual" +date = 2003-09-23 + +[[release."2.3"]] +stage = "2.3.2 candidate 1" +state = "actual" +date = 2003-09-30 + +[[release."2.3"]] +stage = "2.3.2 final" +state = "actual" +date = 2003-10-03 + +[[release."2.3"]] +stage = "2.3.3 candidate 1" +state = "actual" +date = 2003-12-05 + +[[release."2.3"]] +stage = "2.3.3 final" +state = "actual" +date = 2003-12-19 + +[[release."2.3"]] +stage = "2.3.4 candidate 1" +state = "actual" +date = 2004-05-13 + +[[release."2.3"]] +stage = "2.3.4 final" +state = "actual" +date = 2004-05-27 + +[[release."2.3"]] +stage = "2.3.5 candidate 1" +state = "actual" +date = 2004-01-26 + +[[release."2.3"]] +stage = "2.3.5 final" +state = "actual" +date = 2004-02-08 + +[[release."2.3"]] +stage = "2.3.6 candidate 1" +state = "actual" +date = 2006-10-23 + +[[release."2.3"]] +stage = "2.3.6 final" +state = "actual" +date = 2006-11-01 + +[[release."2.3"]] +stage = "2.3.7 candidate 1" +state = "actual" +date = 2008-03-02 + +[[release."2.3"]] +stage = "2.3.7 final" +state = "actual" +date = 2008-03-11 + +# -- Python 2.4 -------------------------------------------------------------- + +[metadata."2.4"] +pep = 320 +status = "end-of-life" +branch = "2.4" +release-manager = "Anthony Baxter" +start-of-development = 2003-07-30 +feature-freeze = 2004-10-15 +first-release = 2004-11-30 +end-of-bugfix = 2008-12-19 +end-of-life = 2008-12-19 + +[[release."2.4"]] +stage = "2.4.0 alpha 1" +state = "actual" +date = 2004-07-09 + +[[release."2.4"]] +stage = "2.4.0 alpha 2" +state = "actual" +date = 2004-08-05 + +[[release."2.4"]] +stage = "2.4.0 alpha 3" +state = "actual" +date = 2004-09-03 + +[[release."2.4"]] +stage = "2.4.0 beta 1" +state = "actual" +date = 2004-10-15 + +[[release."2.4"]] +stage = "2.4.0 beta 2" +state = "actual" +date = 2004-11-03 + +[[release."2.4"]] +stage = "2.4.0 candidate 1" +state = "actual" +date = 2004-11-18 + +[[release."2.4"]] +stage = "2.4.0 final" +state = "actual" +date = 2004-11-30 + +# 2.4.{1,2,3,4,5,6} are not in the PEP, but are found on the website at: +# https://www.python.org/downloads/release/python-241/ +# https://www.python.org/downloads/release/python-242/ +# https://www.python.org/downloads/release/python-243/ +# https://www.python.org/downloads/release/python-244/ +# https://www.python.org/downloads/release/python-245/ +# https://www.python.org/downloads/release/python-246/ +# https://www.python.org/ftp/python/2.4.1/ +# https://www.python.org/ftp/python/2.4.2/ +# https://www.python.org/ftp/python/2.4.3/ +# https://www.python.org/ftp/python/2.4.4/ +# https://www.python.org/ftp/python/2.4.5/ +# https://www.python.org/ftp/python/2.4.6/ + +[[release."2.4"]] +stage = "2.4.1 candidate 1" +state = "actual" +date = 2005-03-10 + +[[release."2.4"]] +stage = "2.4.1 candidate 2" +state = "actual" +date = 2005-03-17 + +[[release."2.4"]] +stage = "2.4.1 final" +state = "actual" +date = 2005-03-30 + +[[release."2.4"]] +stage = "2.4.2 candidate 1" +state = "actual" +date = 2005-09-20 + +[[release."2.4"]] +stage = "2.4.2 final" +state = "actual" +date = 2005-09-27 + +[[release."2.4"]] +stage = "2.4.3 candidate 1" +state = "actual" +date = 2006-03-23 + +[[release."2.4"]] +stage = "2.4.3 final" +state = "actual" +date = 2006-04-15 + +[[release."2.4"]] +stage = "2.4.4 candidate 1" +state = "actual" +date = 2006-10-11 + +[[release."2.4"]] +stage = "2.4.4 final" +state = "actual" +date = 2006-10-18 + +[[release."2.4"]] +stage = "2.4.5 candidate 1" +state = "actual" +date = 2008-03-02 + +[[release."2.4"]] +stage = "2.4.5 final" +state = "actual" +date = 2008-03-11 + +[[release."2.4"]] +stage = "2.4.6 candidate 1" +state = "actual" +date = 2008-12-13 + +[[release."2.4"]] +stage = "2.4.6 final" +state = "actual" +date = 2008-12-19 + +# -- Python 2.5 -------------------------------------------------------------- + +[metadata."2.5"] +pep = 356 +status = "end-of-life" +branch = "2.5" +release-manager = "Anthony Baxter" +start-of-development = 2004-11-30 +feature-freeze = 2006-06-20 +first-release = 2006-09-19 +end-of-bugfix = 2011-05-26 +end-of-life = 2011-05-26 + +[[release."2.5"]] +stage = "2.5.0 alpha 1" +state = "actual" +date = 2006-04-05 + +[[release."2.5"]] +stage = "2.5.0 alpha 2" +state = "actual" +date = 2006-04-27 + +[[release."2.5"]] +stage = "2.5.0 beta 1" +state = "actual" +date = 2006-06-20 + +[[release."2.5"]] +stage = "2.5.0 beta 2" +state = "actual" +date = 2006-07-11 + +[[release."2.5"]] +stage = "2.5.0 beta 3" +state = "actual" +date = 2006-08-03 + +[[release."2.5"]] +stage = "2.5.0 candidate 1" +state = "actual" +date = 2006-08-17 + +[[release."2.5"]] +stage = "2.5.0 candidate 2" +state = "actual" +date = 2006-09-12 + +[[release."2.5"]] +stage = "2.5.0 final" +state = "actual" +date = 2006-09-19 + +# 2.5.{1,2,3,4,5,6} are not in the PEP, but are found on the website at: +# https://www.python.org/downloads/release/python-251/ +# https://www.python.org/downloads/release/python-252/ +# https://www.python.org/downloads/release/python-253/ +# https://www.python.org/downloads/release/python-254/ +# https://www.python.org/downloads/release/python-255/ +# https://www.python.org/downloads/release/python-256/ +# https://www.python.org/ftp/python/2.5.1/ +# https://www.python.org/ftp/python/2.5.2/ +# https://www.python.org/ftp/python/2.5.3/ +# https://www.python.org/ftp/python/2.5.4/ +# https://www.python.org/ftp/python/2.5.5/ +# https://www.python.org/ftp/python/2.5.6/ + +[[release."2.5"]] +stage = "2.5.1 candidate 1" +state = "actual" +date = 2007-04-10 + +[[release."2.5"]] +stage = "2.5.1 final" +state = "actual" +date = 2007-04-19 + +[[release."2.5"]] +stage = "2.5.2 candidate 1" +state = "actual" +date = 2008-02-14 + +[[release."2.5"]] +stage = "2.5.2 final" +state = "actual" +date = 2008-02-21 + +[[release."2.5"]] +stage = "2.5.3 candidate 1" +state = "actual" +date = 2008-12-13 + +[[release."2.5"]] +stage = "2.5.3 final" +state = "actual" +date = 2008-12-19 + +[[release."2.5"]] +stage = "2.5.4 final" +state = "actual" +date = 2008-12-23 + +[[release."2.5"]] +stage = "2.5.5 candidate 1" +state = "actual" +date = 2010-01-14 + +[[release."2.5"]] +stage = "2.5.5 candidate 2" +state = "actual" +date = 2010-01-24 + +[[release."2.5"]] +stage = "2.5.5 final" +state = "actual" +date = 2010-01-31 + +[[release."2.5"]] +stage = "2.5.6 candidate 1" +state = "actual" +date = 2011-04-17 + +[[release."2.5"]] +stage = "2.5.6 final" +state = "actual" +date = 2011-05-26 + +# -- Python 2.6 -------------------------------------------------------------- + +[metadata."2.6"] +pep = 361 +status = "end-of-life" +branch = "2.6" +release-manager = "Barry Warsaw" +start-of-development = 2006-08-18 +feature-freeze = 2008-06-18 +first-release = 2008-10-01 +end-of-bugfix = 2010-08-24 +end-of-life = 2013-10-29 + +[[release."2.6"]] +stage = "2.6.0 alpha 1" +state = "actual" +date = 2008-02-29 + +[[release."2.6"]] +stage = "2.6.0 alpha 2" +state = "actual" +date = 2008-04-02 + +[[release."2.6"]] +stage = "2.6.0 alpha 3" +state = "actual" +date = 2008-05-08 + +[[release."2.6"]] +stage = "2.6.0 beta 1" +state = "actual" +date = 2008-06-18 + +[[release."2.6"]] +stage = "2.6.0 beta 2" +state = "actual" +date = 2008-07-17 + +[[release."2.6"]] +stage = "2.6.0 beta 3" +state = "actual" +date = 2008-08-20 + +[[release."2.6"]] +stage = "2.6.0 candidate 1" +state = "actual" +date = 2008-09-12 + +[[release."2.6"]] +stage = "2.6.0 candidate 2" +state = "actual" +date = 2008-09-17 + +[[release."2.6"]] +stage = "2.6.0 final" +state = "actual" +date = 2008-10-01 + +[[release."2.6"]] +stage = "2.6.1 final" +state = "actual" +date = 2008-12-04 + +[[release."2.6"]] +stage = "2.6.2 candidate 1" +state = "actual" +date = 2009-04-08 + +[[release."2.6"]] +stage = "2.6.2 final" +state = "actual" +date = 2009-04-14 + +[[release."2.6"]] +stage = "2.6.3 candidate 1" +state = "actual" +date = 2009-09-29 + +[[release."2.6"]] +stage = "2.6.3 final" +state = "actual" +date = 2009-10-02 + +[[release."2.6"]] +stage = "2.6.4 candidate 1" +state = "actual" +date = 2009-10-06 + +[[release."2.6"]] +stage = "2.6.4 candidate 2" +state = "actual" +date = 2009-10-18 + +[[release."2.6"]] +stage = "2.6.4 final" +state = "actual" +date = 2009-10-25 + +[[release."2.6"]] +stage = "2.6.5 candidate 1" +state = "actual" +date = 2010-03-01 + +[[release."2.6"]] +stage = "2.6.5 candidate 2" +state = "actual" +date = 2010-03-10 + +[[release."2.6"]] +stage = "2.6.5 final" +state = "actual" +date = 2010-03-19 + +[[release."2.6"]] +stage = "2.6.6 candidate 1" +state = "actual" +date = 2010-08-03 + +[[release."2.6"]] +stage = "2.6.6 candidate 2" +state = "actual" +date = 2010-08-16 + +[[release."2.6"]] +stage = "2.6.6 final" +state = "actual" +date = 2010-08-24 + +[[release."2.6"]] +stage = "2.6.7 candidate 1" +state = "actual" +date = 2011-05-06 + +[[release."2.6"]] +stage = "2.6.7 candidate 2" +state = "actual" +date = 2011-05-21 + +[[release."2.6"]] +stage = "2.6.7 final" +state = "actual" +date = 2011-06-03 + +[[release."2.6"]] +stage = "2.6.8 candidate 1" +state = "actual" +date = 2012-02-23 + +[[release."2.6"]] +stage = "2.6.8 candidate 2" +state = "actual" +date = 2012-03-17 + +[[release."2.6"]] +stage = "2.6.8 final" +state = "actual" +date = 2012-04-10 + +[[release."2.6"]] +stage = "2.6.9 candidate 1" +state = "actual" +date = 2013-10-01 + +[[release."2.6"]] +stage = "2.6.9 final" +state = "actual" +date = 2013-10-29 + +# -- Python 2.7 -------------------------------------------------------------- + +# The end-of-life date is before the final release (2.7.18), per PEP 373: +# > Support officially stopped January 1, 2020, and 2.7.18 code freeze +# > occurred on January 1, 2020, but the final release occurred +# > after that date. + +[metadata."2.7"] +pep = 373 +status = "end-of-life" +branch = "2.7" +release-manager = "Benjamin Peterson" +start-of-development = 2008-10-02 +feature-freeze = 2010-04-03 +first-release = 2010-07-03 +end-of-bugfix = 2020-01-01 +end-of-life = 2020-01-01 + +[[release."2.7"]] +stage = "2.7.0 alpha 1" +state = "actual" +date = 2009-12-05 + +[[release."2.7"]] +stage = "2.7.0 alpha 2" +state = "actual" +date = 2010-01-09 + +[[release."2.7"]] +stage = "2.7.0 alpha 3" +state = "actual" +date = 2010-02-06 + +[[release."2.7"]] +stage = "2.7.0 alpha 4" +state = "actual" +date = 2010-03-06 + +[[release."2.7"]] +stage = "2.7.0 beta 1" +state = "actual" +date = 2010-04-03 + +[[release."2.7"]] +stage = "2.7.0 beta 2" +state = "actual" +date = 2010-05-08 + +[[release."2.7"]] +stage = "2.7.0 candidate 1" +state = "actual" +date = 2010-06-05 + +[[release."2.7"]] +stage = "2.7.0 candidate 2" +state = "actual" +date = 2010-06-19 + +[[release."2.7"]] +stage = "2.7.0 final" +state = "actual" +date = 2010-07-03 + +[[release."2.7"]] +stage = "2.7.1 candidate 1" +state = "actual" +date = 2010-11-13 + +[[release."2.7"]] +stage = "2.7.1 final" +state = "actual" +date = 2010-11-27 + +[[release."2.7"]] +stage = "2.7.2 candidate 1" +state = "actual" +date = 2011-05-29 + +[[release."2.7"]] +stage = "2.7.2 final" +state = "actual" +date = 2011-07-21 + +[[release."2.7"]] +stage = "2.7.3 candidate 1" +state = "actual" +date = 2012-02-23 + +[[release."2.7"]] +stage = "2.7.3 candidate 2" +state = "actual" +date = 2012-03-15 + +[[release."2.7"]] +stage = "2.7.3 final" +state = "actual" +date = 2012-03-09 + +[[release."2.7"]] +stage = "2.7.4 candidate 1" +state = "actual" +date = 2013-03-23 + +[[release."2.7"]] +stage = "2.7.4 final" +state = "actual" +date = 2013-04-06 + +[[release."2.7"]] +stage = "2.7.5 final" +state = "actual" +date = 2013-05-12 + +[[release."2.7"]] +stage = "2.7.6 candidate 1" +state = "actual" +date = 2013-10-26 + +[[release."2.7"]] +stage = "2.7.6 final" +state = "actual" +date = 2013-11-10 + +[[release."2.7"]] +stage = "2.7.7 candidate 1" +state = "actual" +date = 2014-05-17 + +[[release."2.7"]] +stage = "2.7.7 final" +state = "actual" +date = 2014-05-31 + +[[release."2.7"]] +stage = "2.7.8 final" +state = "actual" +date = 2014-06-30 + +[[release."2.7"]] +stage = "2.7.9 candidate 1" +state = "actual" +date = 2014-11-26 + +[[release."2.7"]] +stage = "2.7.9 final" +state = "actual" +date = 2014-12-10 + +[[release."2.7"]] +stage = "2.7.10 candidate 1" +state = "actual" +date = 2015-05-09 + +[[release."2.7"]] +stage = "2.7.10 final" +state = "actual" +date = 2015-05-23 + +[[release."2.7"]] +stage = "2.7.11 candidate 1" +state = "actual" +date = 2015-11-21 + +[[release."2.7"]] +stage = "2.7.11 final" +state = "actual" +date = 2015-12-05 + +[[release."2.7"]] +stage = "2.7.12 final" +state = "actual" +date = 2016-06-25 + +[[release."2.7"]] +stage = "2.7.13 candidate 1" +state = "actual" +date = 2016-12-03 + +[[release."2.7"]] +stage = "2.7.13 final" +state = "actual" +date = 2016-12-17 + +[[release."2.7"]] +stage = "2.7.14 candidate 1" +state = "actual" +date = 2017-08-26 + +[[release."2.7"]] +stage = "2.7.14 final" +state = "actual" +date = 2017-09-16 + +[[release."2.7"]] +stage = "2.7.15 candidate 1" +state = "actual" +date = 2018-04-14 + +[[release."2.7"]] +stage = "2.7.15 final" +state = "actual" +date = 2018-05-01 + +[[release."2.7"]] +stage = "2.7.16 candidate 1" +state = "actual" +date = 2019-02-16 + +[[release."2.7"]] +stage = "2.7.16 final" +state = "actual" +date = 2019-03-02 + +[[release."2.7"]] +stage = "2.7.17 candidate 1" +state = "actual" +date = 2019-10-05 + +[[release."2.7"]] +stage = "2.7.17 final" +state = "actual" +date = 2019-10-19 + +[[release."2.7"]] +stage = "2.7.18 candidate 1" +state = "actual" +date = 2020-04-04 + +[[release."2.7"]] +stage = "2.7.18 final" +state = "actual" +date = 2020-04-20 + +# -- Python 3.0 -------------------------------------------------------------- + +# PEP 361 does not list an end-of-life date. We use the statement from the +# website that 'Python 3.0 is end-of-lifed with the release of Python 3.1.'. +# The latter was first released on 27 June 2009. + +[metadata."3.0"] +pep = 361 +status = "end-of-life" +branch = "3.0" +release-manager = "Barry Warsaw" +start-of-development = 2006-03-14 +feature-freeze = 2008-06-18 +first-release = 2008-12-03 +end-of-bugfix = 2009-06-27 +end-of-life = 2009-06-27 + +[[release."3.0"]] +stage = "3.0.0 alpha 1" +state = "actual" +date = 2007-08-31 + +[[release."3.0"]] +stage = "3.0.0 alpha 2" +state = "actual" +date = 2007-12-06 + +[[release."3.0"]] +stage = "3.0.0 alpha 3" +state = "actual" +date = 2008-02-29 + +[[release."3.0"]] +stage = "3.0.0 alpha 4" +state = "actual" +date = 2008-04-02 + +[[release."3.0"]] +stage = "3.0.0 alpha 5" +state = "actual" +date = 2008-05-08 + +[[release."3.0"]] +stage = "3.0.0 beta 1" +state = "actual" +date = 2008-06-18 + +[[release."3.0"]] +stage = "3.0.0 beta 2" +state = "actual" +date = 2008-07-17 + +[[release."3.0"]] +stage = "3.0.0 beta 3" +state = "actual" +date = 2008-08-20 + +[[release."3.0"]] +stage = "3.0.0 candidate 1" +state = "actual" +date = 2008-09-17 + +[[release."3.0"]] +stage = "3.0.0 candidate 2" +state = "actual" +date = 2008-11-06 + +[[release."3.0"]] +stage = "3.0.0 candidate 3" +state = "actual" +date = 2008-11-21 + +[[release."3.0"]] +stage = "3.0.0 final" +state = "actual" +date = 2008-12-03 + +# 3.0.1 is not in the PEP, but is found on the website at: +# https://www.python.org/downloads/release/python-301/ +# https://www.python.org/ftp/python/3.0.1/ + +[[release."3.0"]] +stage = "3.0.1 final" +state = "actual" +date = 2009-02-13 + +# -- Python 3.1 -------------------------------------------------------------- + +[metadata."3.1"] +pep = 375 +status = "end-of-life" +branch = "3.1" +release-manager = "Benjamin Peterson" +start-of-development = 2008-12-03 +feature-freeze = 2009-05-06 +first-release = 2009-06-27 +end-of-bugfix = 2011-06-11 +end-of-life = 2012-04-09 + +[[release."3.1"]] +stage = "3.1.0 alpha 1" +state = "actual" +date = 2009-03-07 + +[[release."3.1"]] +stage = "3.1.0 alpha 2" +state = "actual" +date = 2009-04-04 + +[[release."3.1"]] +stage = "3.1.0 beta 1" +state = "actual" +date = 2009-05-06 + +[[release."3.1"]] +stage = "3.1.0 candidate 1" +state = "actual" +date = 2009-05-30 + +[[release."3.1"]] +stage = "3.1.0 candidate 2" +state = "actual" +date = 2009-06-13 + +[[release."3.1"]] +stage = "3.1.0 final" +state = "actual" +date = 2009-06-27 + +[[release."3.1"]] +stage = "3.1.1 candidate 1" +state = "actual" +date = 2009-08-13 + +[[release."3.1"]] +stage = "3.1.1 final" +state = "actual" +date = 2009-08-16 + +[[release."3.1"]] +stage = "3.1.2 candidate 1" +state = "actual" +date = 2010-03-06 + +[[release."3.1"]] +stage = "3.1.2 final" +state = "actual" +date = 2010-03-20 + +[[release."3.1"]] +stage = "3.1.3 candidate 1" +state = "actual" +date = 2010-11-13 + +[[release."3.1"]] +stage = "3.1.3 final" +state = "actual" +date = 2010-11-27 + +[[release."3.1"]] +stage = "3.1.4 candidate 1" +state = "actual" +date = 2011-05-29 + +[[release."3.1"]] +stage = "3.1.4 final" +state = "actual" +date = 2011-06-11 + +[[release."3.1"]] +stage = "3.1.5 candidate 1" +state = "actual" +date = 2012-02-23 + +[[release."3.1"]] +stage = "3.1.5 candidate 2" +state = "actual" +date = 2012-03-15 + +# PEP 375 states the date as 2012-04-06, but the website lists 2012-04-09: +# https://www.python.org/downloads/release/python-315/ + +[[release."3.1"]] +stage = "3.1.5 final" +state = "actual" +date = 2012-04-09 + +# -- Python 3.2 -------------------------------------------------------------- + +[metadata."3.2"] +pep = 392 +status = "end-of-life" +branch = "3.2" +release-manager = "Georg Brandl" +start-of-development = 2009-06-27 +feature-freeze = 2010-12-06 +first-release = 2011-02-20 +end-of-bugfix = 2013-05-13 +end-of-life = 2016-02-20 + +[[release."3.2"]] +stage = "3.2.0 alpha 1" +state = "actual" +date = 2010-08-01 + +[[release."3.2"]] +stage = "3.2.0 alpha 2" +state = "actual" +date = 2010-09-06 + +[[release."3.2"]] +stage = "3.2.0 alpha 3" +state = "actual" +date = 2010-10-12 + +[[release."3.2"]] +stage = "3.2.0 alpha 4" +state = "actual" +date = 2010-11-16 + +[[release."3.2"]] +stage = "3.2.0 beta 1" +state = "actual" +date = 2010-12-06 + +[[release."3.2"]] +stage = "3.2.0 beta 2" +state = "actual" +date = 2010-12-20 + +[[release."3.2"]] +stage = "3.2.0 candidate 1" +state = "actual" +date = 2011-01-16 + +[[release."3.2"]] +stage = "3.2.0 candidate 2" +state = "actual" +date = 2011-01-31 + +[[release."3.2"]] +stage = "3.2.0 candidate 3" +state = "actual" +date = 2011-02-14 + +[[release."3.2"]] +stage = "3.2.0 final" +state = "actual" +date = 2011-02-20 + +[[release."3.2"]] +stage = "3.2.1 beta 1" +state = "actual" +date = 2011-05-08 + +[[release."3.2"]] +stage = "3.2.1 candidate 1" +state = "actual" +date = 2011-05-17 + +[[release."3.2"]] +stage = "3.2.1 candidate 2" +state = "actual" +date = 2011-07-03 + +[[release."3.2"]] +stage = "3.2.1 final" +state = "actual" +date = 2011-07-11 + +[[release."3.2"]] +stage = "3.2.2 candidate 1" +state = "actual" +date = 2011-08-14 + +[[release."3.2"]] +stage = "3.2.2 final" +state = "actual" +date = 2011-09-04 + +[[release."3.2"]] +stage = "3.2.3 candidate 1" +state = "actual" +date = 2012-02-25 + +[[release."3.2"]] +stage = "3.2.3 candidate 2" +state = "actual" +date = 2012-03-18 + +[[release."3.2"]] +stage = "3.2.3 final" +state = "actual" +date = 2012-04-11 + +[[release."3.2"]] +stage = "3.2.4 candidate 1" +state = "actual" +date = 2013-03-23 + +[[release."3.2"]] +stage = "3.2.4 final" +state = "actual" +date = 2013-04-06 + +[[release."3.2"]] +stage = "3.2.5 final" +state = "actual" +date = 2013-05-13 + +[[release."3.2"]] +stage = "3.2.6 candidate 1" +state = "actual" +date = 2014-10-04 + +[[release."3.2"]] +stage = "3.2.6 final" +state = "actual" +date = 2014-10-11 + +# -- Python 3.3 -------------------------------------------------------------- + +[metadata."3.3"] +pep = 398 +status = "end-of-life" +branch = "3.3" +release-manager = "Georg Brandl & Ned Deily (3.3.7)" +start-of-development = 2011-02-20 +feature-freeze = 2012-06-27 +first-release = 2012-09-29 +end-of-bugfix = 2014-03-08 +end-of-life = 2017-09-29 + +[[release."3.3"]] +stage = "3.3.0 alpha 1" +state = "actual" +date = 2012-03-05 + +[[release."3.3"]] +stage = "3.3.0 alpha 2" +state = "actual" +date = 2012-04-02 + +[[release."3.3"]] +stage = "3.3.0 alpha 3" +state = "actual" +date = 2012-05-01 + +[[release."3.3"]] +stage = "3.3.0 alpha 4" +state = "actual" +date = 2012-05-31 + +[[release."3.3"]] +stage = "3.3.0 beta 1" +state = "actual" +date = 2012-06-27 + +[[release."3.3"]] +stage = "3.3.0 beta 2" +state = "actual" +date = 2012-08-12 + +[[release."3.3"]] +stage = "3.3.0 candidate 1" +state = "actual" +date = 2012-08-24 + +[[release."3.3"]] +stage = "3.3.0 candidate 2" +state = "actual" +date = 2012-09-09 + +[[release."3.3"]] +stage = "3.3.0 candidate 3" +state = "actual" +date = 2012-09-24 + +[[release."3.3"]] +stage = "3.3.0 final" +state = "actual" +date = 2012-09-29 + +[[release."3.3"]] +stage = "3.3.1 candidate 1" +state = "actual" +date = 2013-03-23 + +[[release."3.3"]] +stage = "3.3.1 final" +state = "actual" +date = 2013-04-06 + +[[release."3.3"]] +stage = "3.3.2 final" +state = "actual" +date = 2013-05-13 + +[[release."3.3"]] +stage = "3.3.3 candidate 1" +state = "actual" +date = 2013-10-27 + +[[release."3.3"]] +stage = "3.3.3 candidate 2" +state = "actual" +date = 2013-11-09 + +[[release."3.3"]] +stage = "3.3.3 final" +state = "actual" +date = 2013-11-16 + +[[release."3.3"]] +stage = "3.3.4 candidate 1" +state = "actual" +date = 2014-01-26 + +[[release."3.3"]] +stage = "3.3.4 final" +state = "actual" +date = 2014-02-09 + +[[release."3.3"]] +stage = "3.3.5 candidate 1" +state = "actual" +date = 2014-02-22 + +[[release."3.3"]] +stage = "3.3.5 candidate 2" +state = "actual" +date = 2014-03-01 + +[[release."3.3"]] +stage = "3.3.5 final" +state = "actual" +date = 2014-03-08 + +[[release."3.3"]] +stage = "3.3.6 candidate 1" +state = "actual" +date = 2014-10-04 + +[[release."3.3"]] +stage = "3.3.6 final" +state = "actual" +date = 2014-10-11 + +[[release."3.3"]] +stage = "3.3.7 candidate 1" +state = "actual" +date = 2017-09-06 + +[[release."3.3"]] +stage = "3.3.7 final" +state = "actual" +date = 2017-09-19 + +# -- Python 3.4 -------------------------------------------------------------- + +[metadata."3.4"] +pep = 429 +status = "end-of-life" +branch = "3.4" +release-manager = "Larry Hastings" +start-of-development = 2012-09-29 +feature-freeze = 2013-11-24 +first-release = 2014-03-16 +end-of-bugfix = 2017-08-09 +end-of-life = 2019-03-18 + +[[release."3.4"]] +stage = "3.4.0 alpha 1" +state = "actual" +date = 2013-08-03 + +[[release."3.4"]] +stage = "3.4.0 alpha 2" +state = "actual" +date = 2013-09-09 + +[[release."3.4"]] +stage = "3.4.0 alpha 3" +state = "actual" +date = 2013-09-29 + +[[release."3.4"]] +stage = "3.4.0 alpha 4" +state = "actual" +date = 2013-10-20 + +[[release."3.4"]] +stage = "3.4.0 beta 1" +state = "actual" +date = 2013-11-24 + +[[release."3.4"]] +stage = "3.4.0 beta 2" +state = "actual" +date = 2014-01-05 + +[[release."3.4"]] +stage = "3.4.0 beta 3" +state = "actual" +date = 2014-01-26 + +[[release."3.4"]] +stage = "3.4.0 candidate 1" +state = "actual" +date = 2014-02-10 + +[[release."3.4"]] +stage = "3.4.0 candidate 2" +state = "actual" +date = 2014-02-23 + +[[release."3.4"]] +stage = "3.4.0 candidate 3" +state = "actual" +date = 2014-03-09 + +[[release."3.4"]] +stage = "3.4.0 final" +state = "actual" +date = 2014-03-16 + +[[release."3.4"]] +stage = "3.4.1 candidate 1" +state = "actual" +date = 2014-05-05 + +[[release."3.4"]] +stage = "3.4.1 final" +state = "actual" +date = 2014-05-18 + +[[release."3.4"]] +stage = "3.4.2 candidate 1" +state = "actual" +date = 2014-09-22 + +[[release."3.4"]] +stage = "3.4.2 final" +state = "actual" +date = 2014-10-06 + +[[release."3.4"]] +stage = "3.4.3 candidate 1" +state = "actual" +date = 2015-02-08 + +[[release."3.4"]] +stage = "3.4.3 final" +state = "actual" +date = 2015-02-25 + +[[release."3.4"]] +stage = "3.4.4 candidate 1" +state = "actual" +date = 2015-12-06 + +[[release."3.4"]] +stage = "3.4.4 final" +state = "actual" +date = 2015-12-20 + +[[release."3.4"]] +stage = "3.4.5 candidate 1" +state = "actual" +date = 2016-06-12 + +[[release."3.4"]] +stage = "3.4.5 final" +state = "actual" +date = 2016-06-26 + +[[release."3.4"]] +stage = "3.4.6 candidate 1" +state = "actual" +date = 2017-01-02 + +[[release."3.4"]] +stage = "3.4.6 final" +state = "actual" +date = 2017-01-17 + +[[release."3.4"]] +stage = "3.4.7 candidate 1" +state = "actual" +date = 2017-07-25 + +[[release."3.4"]] +stage = "3.4.7 final" +state = "actual" +date = 2017-08-09 + +[[release."3.4"]] +stage = "3.4.8 candidate 1" +state = "actual" +date = 2018-01-23 + +[[release."3.4"]] +stage = "3.4.8 final" +state = "actual" +date = 2018-02-04 + +[[release."3.4"]] +stage = "3.4.9 candidate 1" +state = "actual" +date = 2018-07-19 + +[[release."3.4"]] +stage = "3.4.9 final" +state = "actual" +date = 2018-08-02 + +[[release."3.4"]] +stage = "3.4.10 candidate 1" +state = "actual" +date = 2019-03-04 + +[[release."3.4"]] +stage = "3.4.10 final" +state = "actual" +date = 2019-03-18 + +# -- Python 3.5 -------------------------------------------------------------- + +[metadata."3.5"] +pep = 478 +status = "end-of-life" +branch = "3.5" +release-manager = "Larry Hastings" +start-of-development = 2014-03-17 +feature-freeze = 2015-05-24 +first-release = 2015-09-13 +end-of-bugfix = 2017-08-08 +end-of-life = 2020-09-30 + +[[release."3.5"]] +stage = "3.5.0 alpha 1" +state = "actual" +date = 2015-02-08 + +[[release."3.5"]] +stage = "3.5.0 alpha 2" +state = "actual" +date = 2015-03-09 + +[[release."3.5"]] +stage = "3.5.0 alpha 3" +state = "actual" +date = 2015-03-29 + +[[release."3.5"]] +stage = "3.5.0 alpha 4" +state = "actual" +date = 2015-04-19 + +[[release."3.5"]] +stage = "3.5.0 beta 1" +state = "actual" +date = 2015-05-24 + +[[release."3.5"]] +stage = "3.5.0 beta 2" +state = "actual" +date = 2015-05-31 + +[[release."3.5"]] +stage = "3.5.0 beta 3" +state = "actual" +date = 2015-07-05 + +[[release."3.5"]] +stage = "3.5.0 beta 4" +state = "actual" +date = 2015-07-26 + +[[release."3.5"]] +stage = "3.5.0 candidate 1" +state = "actual" +date = 2015-08-10 + +[[release."3.5"]] +stage = "3.5.0 candidate 2" +state = "actual" +date = 2015-08-25 + +[[release."3.5"]] +stage = "3.5.0 candidate 3" +state = "actual" +date = 2015-09-07 + +[[release."3.5"]] +stage = "3.5.0 final" +state = "actual" +date = 2015-09-13 + +[[release."3.5"]] +stage = "3.5.1 candidate 1" +state = "actual" +date = 2015-11-22 + +[[release."3.5"]] +stage = "3.5.1 final" +state = "actual" +date = 2015-12-06 + +[[release."3.5"]] +stage = "3.5.2 candidate 1" +state = "actual" +date = 2016-06-12 + +[[release."3.5"]] +stage = "3.5.2 final" +state = "actual" +date = 2016-06-26 + +[[release."3.5"]] +stage = "3.5.3 candidate 1" +state = "actual" +date = 2017-01-02 + +[[release."3.5"]] +stage = "3.5.3 final" +state = "actual" +date = 2017-01-17 + +[[release."3.5"]] +stage = "3.5.4 candidate 1" +state = "actual" +date = 2017-07-25 + +[[release."3.5"]] +stage = "3.5.4 final" +state = "actual" +date = 2017-08-08 + +[[release."3.5"]] +stage = "3.5.5 candidate 1" +state = "actual" +date = 2018-01-23 + +[[release."3.5"]] +stage = "3.5.5 final" +state = "actual" +date = 2018-02-04 + +[[release."3.5"]] +stage = "3.5.6 candidate 1" +state = "actual" +date = 2018-07-19 + +[[release."3.5"]] +stage = "3.5.6 final" +state = "actual" +date = 2018-08-02 + +[[release."3.5"]] +stage = "3.5.7 candidate 1" +state = "actual" +date = 2019-03-04 + +[[release."3.5"]] +stage = "3.5.7 final" +state = "actual" +date = 2019-03-18 + +[[release."3.5"]] +stage = "3.5.8 candidate 1" +state = "actual" +date = 2019-09-09 + +[[release."3.5"]] +stage = "3.5.8 candidate 2" +state = "actual" +date = 2019-10-12 + +[[release."3.5"]] +stage = "3.5.8 final" +state = "actual" +date = 2019-10-29 + +[[release."3.5"]] +stage = "3.5.9 final" +state = "actual" +date = 2019-11-01 + +[[release."3.5"]] +stage = "3.5.10 candidate 1" +state = "actual" +date = 2020-08-21 + +[[release."3.5"]] +stage = "3.5.10 final" +state = "actual" +date = 2020-09-05 + +# -- Python 3.6 -------------------------------------------------------------- + +[metadata."3.6"] +pep = 494 +status = "end-of-life" +branch = "3.6" +release-manager = "Ned Deily" +start-of-development = 2015-05-24 +feature-freeze = 2016-09-12 +first-release = 2016-12-23 +end-of-bugfix = 2018-12-24 +end-of-life = 2021-12-23 + +[[release."3.6"]] +stage = "3.6.0 alpha 1" +state = "actual" +date = 2016-05-17 + +[[release."3.6"]] +stage = "3.6.0 alpha 2" +state = "actual" +date = 2016-06-13 + +[[release."3.6"]] +stage = "3.6.0 alpha 3" +state = "actual" +date = 2016-07-11 + +[[release."3.6"]] +stage = "3.6.0 alpha 4" +state = "actual" +date = 2016-08-15 + +[[release."3.6"]] +stage = "3.6.0 beta 1" +state = "actual" +date = 2016-09-12 + +[[release."3.6"]] +stage = "3.6.0 beta 2" +state = "actual" +date = 2016-10-10 + +[[release."3.6"]] +stage = "3.6.0 beta 3" +state = "actual" +date = 2016-10-31 + +[[release."3.6"]] +stage = "3.6.0 beta 4" +state = "actual" +date = 2016-11-21 + +[[release."3.6"]] +stage = "3.6.0 candidate 1" +state = "actual" +date = 2016-12-06 + +[[release."3.6"]] +stage = "3.6.0 candidate 2" +state = "actual" +date = 2016-12-16 + +[[release."3.6"]] +stage = "3.6.0 final" +state = "actual" +date = 2016-12-23 + +[[release."3.6"]] +stage = "3.6.1 candidate 1" +state = "actual" +date = 2017-03-05 + +[[release."3.6"]] +stage = "3.6.1 final" +state = "actual" +date = 2017-03-21 + +[[release."3.6"]] +stage = "3.6.2 candidate 1" +state = "actual" +date = 2017-06-17 + +[[release."3.6"]] +stage = "3.6.2 candidate 2" +state = "actual" +date = 2017-07-07 + +[[release."3.6"]] +stage = "3.6.2 final" +state = "actual" +date = 2017-07-17 + +[[release."3.6"]] +stage = "3.6.3 candidate 1" +state = "actual" +date = 2017-09-19 + +[[release."3.6"]] +stage = "3.6.3 final" +state = "actual" +date = 2017-10-03 + +[[release."3.6"]] +stage = "3.6.4 candidate 1" +state = "actual" +date = 2017-12-05 + +[[release."3.6"]] +stage = "3.6.4 final" +state = "actual" +date = 2017-12-19 + +[[release."3.6"]] +stage = "3.6.5 candidate 1" +state = "actual" +date = 2018-03-13 + +[[release."3.6"]] +stage = "3.6.5 final" +state = "actual" +date = 2018-03-28 + +[[release."3.6"]] +stage = "3.6.6 candidate 1" +state = "actual" +date = 2018-06-12 + +[[release."3.6"]] +stage = "3.6.6 final" +state = "actual" +date = 2018-06-27 + +[[release."3.6"]] +stage = "3.6.7 candidate 1" +state = "actual" +date = 2018-09-26 + +[[release."3.6"]] +stage = "3.6.7 candidate 2" +state = "actual" +date = 2018-10-13 + +[[release."3.6"]] +stage = "3.6.7 final" +state = "actual" +date = 2018-10-20 + +[[release."3.6"]] +stage = "3.6.8 candidate 1" +state = "actual" +date = 2018-12-11 + +[[release."3.6"]] +stage = "3.6.8 final" +state = "actual" +date = 2018-12-24 + +[[release."3.6"]] +stage = "3.6.9 candidate 1" +state = "actual" +date = 2019-06-18 + +[[release."3.6"]] +stage = "3.6.9 final" +state = "actual" +date = 2019-07-02 + +[[release."3.6"]] +stage = "3.6.10 candidate 1" +state = "actual" +date = 2019-12-11 + +[[release."3.6"]] +stage = "3.6.10 final" +state = "actual" +date = 2019-12-18 + +[[release."3.6"]] +stage = "3.6.11 candidate 1" +state = "actual" +date = 2020-06-15 + +[[release."3.6"]] +stage = "3.6.11 final" +state = "actual" +date = 2020-06-27 + +[[release."3.6"]] +stage = "3.6.12 final" +state = "actual" +date = 2020-08-17 + +[[release."3.6"]] +stage = "3.6.13 final" +state = "actual" +date = 2021-02-15 + +[[release."3.6"]] +stage = "3.6.14 final" +state = "actual" +date = 2021-06-28 + +[[release."3.6"]] +stage = "3.6.15 final" +state = "actual" +date = 2021-09-04 + +# -- Python 3.7 -------------------------------------------------------------- + +[metadata."3.7"] +pep = 537 +status = "end-of-life" +branch = "3.7" +release-manager = "Ned Deily" +start-of-development = 2016-09-12 +feature-freeze = 2018-01-31 +first-release = 2018-06-27 +end-of-bugfix = 2020-06-27 +end-of-life = 2023-06-27 + +[[release."3.7"]] +stage = "3.7.0 alpha 1" +state = "actual" +date = 2017-09-19 + +[[release."3.7"]] +stage = "3.7.0 alpha 2" +state = "actual" +date = 2017-10-17 + +[[release."3.7"]] +stage = "3.7.0 alpha 3" +state = "actual" +date = 2017-12-05 + +[[release."3.7"]] +stage = "3.7.0 alpha 4" +state = "actual" +date = 2018-01-09 + +[[release."3.7"]] +stage = "3.7.0 beta 1" +state = "actual" +date = 2018-01-31 + +[[release."3.7"]] +stage = "3.7.0 beta 2" +state = "actual" +date = 2018-02-27 + +[[release."3.7"]] +stage = "3.7.0 beta 3" +state = "actual" +date = 2018-03-29 + +[[release."3.7"]] +stage = "3.7.0 beta 4" +state = "actual" +date = 2018-05-02 + +[[release."3.7"]] +stage = "3.7.0 beta 5" +state = "actual" +date = 2018-05-30 + +[[release."3.7"]] +stage = "3.7.0 candidate 1" +state = "actual" +date = 2018-06-12 + +[[release."3.7"]] +stage = "3.7.0 final" +state = "actual" +date = 2018-06-27 + +[[release."3.7"]] +stage = "3.7.1 candidate 1" +state = "actual" +date = 2018-09-26 + +[[release."3.7"]] +stage = "3.7.1 candidate 2" +state = "actual" +date = 2018-10-13 + +[[release."3.7"]] +stage = "3.7.1 final" +state = "actual" +date = 2018-10-20 + +[[release."3.7"]] +stage = "3.7.2 candidate 1" +state = "actual" +date = 2018-12-11 + +[[release."3.7"]] +stage = "3.7.2 final" +state = "actual" +date = 2018-12-24 + +[[release."3.7"]] +stage = "3.7.3 candidate 1" +state = "actual" +date = 2019-03-12 + +[[release."3.7"]] +stage = "3.7.3 final" +state = "actual" +date = 2019-03-25 + +[[release."3.7"]] +stage = "3.7.4 candidate 1" +state = "actual" +date = 2019-06-18 + +[[release."3.7"]] +stage = "3.7.4 candidate 2" +state = "actual" +date = 2019-07-02 + +[[release."3.7"]] +stage = "3.7.4 final" +state = "actual" +date = 2019-07-08 + +[[release."3.7"]] +stage = "3.7.5 candidate 1" +state = "actual" +date = 2019-10-02 + +[[release."3.7"]] +stage = "3.7.5 final" +state = "actual" +date = 2019-10-15 + +[[release."3.7"]] +stage = "3.7.6 candidate 1" +state = "actual" +date = 2019-12-11 + +[[release."3.7"]] +stage = "3.7.6 final" +state = "actual" +date = 2019-12-18 + +[[release."3.7"]] +stage = "3.7.7 candidate 1" +state = "actual" +date = 2020-03-04 + +[[release."3.7"]] +stage = "3.7.7 final" +state = "actual" +date = 2020-03-10 + +[[release."3.7"]] +stage = "3.7.8 candidate 1" +state = "actual" +date = 2020-06-15 + +[[release."3.7"]] +stage = "3.7.8 final" +state = "actual" +date = 2020-06-27 + +[[release."3.7"]] +stage = "3.7.9 final" +state = "actual" +date = 2020-08-17 + +[[release."3.7"]] +stage = "3.7.10 final" +state = "actual" +date = 2021-02-15 + +[[release."3.7"]] +stage = "3.7.11 final" +state = "actual" +date = 2021-06-28 + +[[release."3.7"]] +stage = "3.7.12 final" +state = "actual" +date = 2021-09-04 + +[[release."3.7"]] +stage = "3.7.13 final" +state = "actual" +date = 2022-03-16 + +[[release."3.7"]] +stage = "3.7.14 final" +state = "actual" +date = 2022-09-06 + +[[release."3.7"]] +stage = "3.7.15 final" +state = "actual" +date = 2022-10-11 + +[[release."3.7"]] +stage = "3.7.16 final" +state = "actual" +date = 2022-12-06 + +[[release."3.7"]] +stage = "3.7.17 final" +state = "actual" +date = 2023-06-06 + +# -- Python 3.8 -------------------------------------------------------------- + +[metadata."3.8"] +pep = 569 +status = "end-of-life" +branch = "3.8" +release-manager = "Łukasz Langa" +start-of-development = 2018-01-29 +feature-freeze = 2019-06-04 +first-release = 2019-10-14 +end-of-bugfix = 2021-05-03 +end-of-life = 2024-10-07 + +[[release."3.8"]] +stage = "3.8.0 alpha 1" +state = "actual" +date = 2019-02-03 + +[[release."3.8"]] +stage = "3.8.0 alpha 2" +state = "actual" +date = 2019-02-25 + +[[release."3.8"]] +stage = "3.8.0 alpha 3" +state = "actual" +date = 2019-03-25 + +[[release."3.8"]] +stage = "3.8.0 alpha 4" +state = "actual" +date = 2019-05-06 + +[[release."3.8"]] +stage = "3.8.0 beta 1" +state = "actual" +date = 2019-06-04 + +[[release."3.8"]] +stage = "3.8.0 beta 2" +state = "actual" +date = 2019-07-04 + +[[release."3.8"]] +stage = "3.8.0 beta 3" +state = "actual" +date = 2019-07-29 + +[[release."3.8"]] +stage = "3.8.0 beta 4" +state = "actual" +date = 2019-08-30 + +[[release."3.8"]] +stage = "3.8.0 candidate 1" +state = "actual" +date = 2019-10-01 + +[[release."3.8"]] +stage = "3.8.0 final" +state = "actual" +date = 2019-10-14 + +[[release."3.8"]] +stage = "3.8.1 candidate 1" +state = "actual" +date = 2019-12-10 + +[[release."3.8"]] +stage = "3.8.1 final" +state = "actual" +date = 2019-12-18 + +[[release."3.8"]] +stage = "3.8.2 candidate 1" +state = "actual" +date = 2020-02-10 + +[[release."3.8"]] +stage = "3.8.2 candidate 2" +state = "actual" +date = 2020-02-17 + +[[release."3.8"]] +stage = "3.8.2 final" +state = "actual" +date = 2020-02-24 + +[[release."3.8"]] +stage = "3.8.3 candidate 1" +state = "actual" +date = 2020-04-29 + +[[release."3.8"]] +stage = "3.8.3 final" +state = "actual" +date = 2020-05-13 + +[[release."3.8"]] +stage = "3.8.4 candidate 1" +state = "actual" +date = 2020-06-30 + +[[release."3.8"]] +stage = "3.8.4 final" +state = "actual" +date = 2020-07-13 + +[[release."3.8"]] +stage = "3.8.5 final" +state = "actual" +date = 2020-07-20 +note = "security hotfix" + +[[release."3.8"]] +stage = "3.8.6 candidate 1" +state = "actual" +date = 2020-09-08 + +[[release."3.8"]] +stage = "3.8.6 final" +state = "actual" +date = 2020-09-24 + +[[release."3.8"]] +stage = "3.8.7 candidate 1" +state = "actual" +date = 2020-12-07 + +[[release."3.8"]] +stage = "3.8.7 final" +state = "actual" +date = 2020-12-21 + +[[release."3.8"]] +stage = "3.8.8 candidate 1" +state = "actual" +date = 2021-02-16 + +[[release."3.8"]] +stage = "3.8.8 final" +state = "actual" +date = 2021-02-19 + +[[release."3.8"]] +stage = "3.8.9 final" +state = "actual" +date = 2021-04-02 +note = "security hotfix" + +[[release."3.8"]] +stage = "3.8.10 final" +state = "actual" +date = 2021-05-03 + +[[release."3.8"]] +stage = "3.8.11 final" +state = "actual" +date = 2021-06-28 + +[[release."3.8"]] +stage = "3.8.12 final" +state = "actual" +date = 2021-08-30 + +[[release."3.8"]] +stage = "3.8.13 final" +state = "actual" +date = 2022-03-16 + +[[release."3.8"]] +stage = "3.8.14 final" +state = "actual" +date = 2022-09-06 + +[[release."3.8"]] +stage = "3.8.15 final" +state = "actual" +date = 2022-10-11 + +[[release."3.8"]] +stage = "3.8.16 final" +state = "actual" +date = 2022-12-06 + +[[release."3.8"]] +stage = "3.8.17 final" +state = "actual" +date = 2023-06-06 + + +[[release."3.8"]] +stage = "3.8.18 final" +state = "actual" +date = 2023-08-24 + +[[release."3.8"]] +stage = "3.8.19 final" +state = "actual" +date = 2024-03-19 + +[[release."3.8"]] +stage = "3.8.20 final" +state = "actual" +date = 2024-09-06 +note = "final security release" + +# -- Python 3.9 -------------------------------------------------------------- + +[metadata."3.9"] +pep = 596 +status = "security" +branch = "3.9" +release-manager = "Łukasz Langa" +start-of-development = 2019-06-04 +feature-freeze = 2020-05-18 +first-release = 2020-10-05 +end-of-bugfix = 2022-05-17 +end-of-life = 2025-10-01 + +[[release."3.9"]] +stage = "3.9.0 alpha 1" +state = "actual" +date = 2019-11-19 + +[[release."3.9"]] +stage = "3.9.0 alpha 2" +state = "actual" +date = 2019-12-18 + +[[release."3.9"]] +stage = "3.9.0 alpha 3" +state = "actual" +date = 2020-01-25 + +[[release."3.9"]] +stage = "3.9.0 alpha 4" +state = "actual" +date = 2020-02-26 + +[[release."3.9"]] +stage = "3.9.0 alpha 5" +state = "actual" +date = 2020-03-23 + +[[release."3.9"]] +stage = "3.9.0 alpha 6" +state = "actual" +date = 2020-04-28 + +[[release."3.9"]] +stage = "3.9.0 beta 1" +state = "actual" +date = 2020-05-18 + +# There was no beta 2, the PEP statest that it was recalled. + +[[release."3.9"]] +stage = "3.9.0 beta 3" +state = "actual" +date = 2020-06-09 +note = "beta 2 was recalled." + +[[release."3.9"]] +stage = "3.9.0 beta 4" +state = "actual" +date = 2020-07-03 + +[[release."3.9"]] +stage = "3.9.0 beta 5" +state = "actual" +date = 2020-07-20 + +[[release."3.9"]] +stage = "3.9.0 candidate 1" +state = "actual" +date = 2020-08-11 + +[[release."3.9"]] +stage = "3.9.0 candidate 2" +state = "actual" +date = 2020-09-17 + +[[release."3.9"]] +stage = "3.9.0 final" +state = "actual" +date = 2020-10-05 + +[[release."3.9"]] +stage = "3.9.1 candidate 1" +state = "actual" +date = 2020-11-24 + +[[release."3.9"]] +stage = "3.9.1 final" +state = "actual" +date = 2020-12-07 + +[[release."3.9"]] +stage = "3.9.2 candidate 1" +state = "actual" +date = 2021-02-16 + +[[release."3.9"]] +stage = "3.9.2 final" +state = "actual" +date = 2021-02-19 + +[[release."3.9"]] +stage = "3.9.3 final" +state = "actual" +date = 2021-04-02 +note = "security hotfix; recalled due to bpo-43710" + +[[release."3.9"]] +stage = "3.9.4 final" +state = "actual" +date = 2021-04-04 +note = "ABI compatibility hotfix" + +[[release."3.9"]] +stage = "3.9.5 final" +state = "actual" +date = 2021-05-03 + +[[release."3.9"]] +stage = "3.9.6 final" +state = "actual" +date = 2021-06-28 + +[[release."3.9"]] +stage = "3.9.7 final" +state = "actual" +date = 2021-08-30 + +[[release."3.9"]] +stage = "3.9.8 final" +state = "actual" +date = 2021-11-05 +note = "recalled due to bpo-45235" + +[[release."3.9"]] +stage = "3.9.9 final" +state = "actual" +date = 2021-11-15 + +[[release."3.9"]] +stage = "3.9.10 final" +state = "actual" +date = 2022-01-14 + +[[release."3.9"]] +stage = "3.9.11 final" +state = "actual" +date = 2022-03-16 + +[[release."3.9"]] +stage = "3.9.12 final" +state = "actual" +date = 2022-03-23 + +[[release."3.9"]] +stage = "3.9.13 final" +state = "actual" +date = 2022-05-17 + +[[release."3.9"]] +stage = "3.9.14 final" +state = "actual" +date = 2022-09-06 + +[[release."3.9"]] +stage = "3.9.15 final" +state = "actual" +date = 2022-10-11 + +[[release."3.9"]] +stage = "3.9.16 final" +state = "actual" +date = 2022-12-06 + +[[release."3.9"]] +stage = "3.9.17 final" +state = "actual" +date = 2023-06-06 + +[[release."3.9"]] +stage = "3.9.18 final" +state = "actual" +date = 2023-08-24 + +[[release."3.9"]] +stage = "3.9.19 final" +state = "actual" +date = 2024-03-19 + +[[release."3.9"]] +stage = "3.9.20 final" +state = "actual" +date = 2024-09-06 + +[[release."3.9"]] +stage = "3.9.21 final" +state = "actual" +date = 2024-12-03 + +[[release."3.9"]] +stage = "3.9.22 final" +state = "actual" +date = 2025-04-08 + +[[release."3.9"]] +stage = "3.9.23 final" +state = "actual" +date = 2025-06-03 + +[[release."3.9"]] +stage = "3.9.24 final" +state = "actual" +date = 2025-10-09 + +[[release."3.9"]] +stage = "3.9.25 final" +state = "actual" +date = 2025-10-31 + +# -- Python 3.10 -------------------------------------------------------------- + +[metadata."3.10"] +pep = 619 +status = "security" +branch = "3.10" +release-manager = "Pablo Galindo Salgado" +start-of-development = 2020-05-18 +feature-freeze = 2021-05-03 +first-release = 2021-10-04 +end-of-bugfix = 2023-04-05 +end-of-life = 2026-10-01 + +[[release."3.10"]] +stage = "3.10.0 alpha 1" +state = "actual" +date = 2020-10-05 + +[[release."3.10"]] +stage = "3.10.0 alpha 2" +state = "actual" +date = 2020-11-03 + +[[release."3.10"]] +stage = "3.10.0 alpha 3" +state = "actual" +date = 2020-12-07 + +[[release."3.10"]] +stage = "3.10.0 alpha 4" +state = "actual" +date = 2021-01-04 + +[[release."3.10"]] +stage = "3.10.0 alpha 5" +state = "actual" +date = 2021-02-03 + +[[release."3.10"]] +stage = "3.10.0 alpha 6" +state = "actual" +date = 2021-03-01 + +[[release."3.10"]] +stage = "3.10.0 alpha 7" +state = "actual" +date = 2021-04-06 + +[[release."3.10"]] +stage = "3.10.0 beta 1" +state = "actual" +date = 2021-05-03 + +[[release."3.10"]] +stage = "3.10.0 beta 2" +state = "actual" +date = 2021-05-31 + +[[release."3.10"]] +stage = "3.10.0 beta 3" +state = "actual" +date = 2021-06-17 + +[[release."3.10"]] +stage = "3.10.0 beta 4" +state = "actual" +date = 2021-07-10 + +[[release."3.10"]] +stage = "3.10.0 candidate 1" +state = "actual" +date = 2021-08-03 + +[[release."3.10"]] +stage = "3.10.0 candidate 2" +state = "actual" +date = 2021-09-07 + +[[release."3.10"]] +stage = "3.10.0 final" +state = "actual" +date = 2021-10-04 + +[[release."3.10"]] +stage = "3.10.1" +state = "actual" +date = 2021-12-06 + +[[release."3.10"]] +stage = "3.10.2" +state = "actual" +date = 2022-01-14 + +[[release."3.10"]] +stage = "3.10.3" +state = "actual" +date = 2022-03-16 + +[[release."3.10"]] +stage = "3.10.4" +state = "actual" +date = 2022-03-24 + +[[release."3.10"]] +stage = "3.10.5" +state = "actual" +date = 2022-06-06 + +[[release."3.10"]] +stage = "3.10.6" +state = "actual" +date = 2022-08-02 + +[[release."3.10"]] +stage = "3.10.7" +state = "actual" +date = 2022-09-06 + +[[release."3.10"]] +stage = "3.10.8" +state = "actual" +date = 2022-10-11 + +[[release."3.10"]] +stage = "3.10.9" +state = "actual" +date = 2022-12-06 + +[[release."3.10"]] +stage = "3.10.10" +state = "actual" +date = 2023-02-08 + +[[release."3.10"]] +stage = "3.10.11" +state = "actual" +date = 2023-04-05 + +[[release."3.10"]] +stage = "3.10.12" +state = "actual" +date = 2023-06-06 + +[[release."3.10"]] +stage = "3.10.13" +state = "actual" +date = 2023-08-24 + +[[release."3.10"]] +stage = "3.10.14" +state = "actual" +date = 2024-03-19 + +[[release."3.10"]] +stage = "3.10.15" +state = "actual" +date = 2024-09-07 + +[[release."3.10"]] +stage = "3.10.16" +state = "actual" +date = 2024-12-03 + +[[release."3.10"]] +stage = "3.10.17" +state = "actual" +date = 2025-04-08 + +[[release."3.10"]] +stage = "3.10.18" +state = "actual" +date = 2025-06-03 + +[[release."3.10"]] +stage = "3.10.19" +state = "actual" +date = 2025-10-09 + +# -- Python 3.11 -------------------------------------------------------------- + +[metadata."3.11"] +pep = 664 +status = "security" +branch = "3.11" +release-manager = "Pablo Galindo Salgado" +start-of-development = 2021-05-03 +feature-freeze = 2022-05-08 +first-release = 2022-10-24 +end-of-bugfix = 2024-04-24 +end-of-life = 2027-10-01 + +[[release."3.11"]] +stage = "3.11.0 alpha 1" +state = "actual" +date = 2021-10-05 + +[[release."3.11"]] +stage = "3.11.0 alpha 2" +state = "actual" +date = 2021-11-02 + +[[release."3.11"]] +stage = "3.11.0 alpha 3" +state = "actual" +date = 2021-12-08 + +[[release."3.11"]] +stage = "3.11.0 alpha 4" +state = "actual" +date = 2022-01-14 + +[[release."3.11"]] +stage = "3.11.0 alpha 5" +state = "actual" +date = 2022-02-03 + +[[release."3.11"]] +stage = "3.11.0 alpha 6" +state = "actual" +date = 2022-03-07 + +[[release."3.11"]] +stage = "3.11.0 alpha 7" +state = "actual" +date = 2022-04-05 + +[[release."3.11"]] +stage = "3.11.0 beta 1" +state = "actual" +date = 2022-05-08 + +[[release."3.11"]] +stage = "3.11.0 beta 2" +state = "actual" +date = 2022-05-31 + +[[release."3.11"]] +stage = "3.11.0 beta 3" +state = "actual" +date = 2022-06-01 + +[[release."3.11"]] +stage = "3.11.0 beta 4" +state = "actual" +date = 2022-07-11 + +[[release."3.11"]] +stage = "3.11.0 beta 5" +state = "actual" +date = 2022-07-26 + +[[release."3.11"]] +stage = "3.11.0 candidate 1" +state = "actual" +date = 2022-08-08 + +[[release."3.11"]] +stage = "3.11.0 candidate 2" +state = "actual" +date = 2022-09-12 + +[[release."3.11"]] +stage = "3.11.0 final" +state = "actual" +date = 2022-10-24 + +[[release."3.11"]] +stage = "3.11.1" +state = "actual" +date = 2022-12-06 + +[[release."3.11"]] +stage = "3.11.2" +state = "actual" +date = 2023-02-08 + +[[release."3.11"]] +stage = "3.11.3" +state = "actual" +date = 2023-04-05 + +[[release."3.11"]] +stage = "3.11.4" +state = "actual" +date = 2023-06-06 + +[[release."3.11"]] +stage = "3.11.5" +state = "actual" +date = 2023-08-24 + +[[release."3.11"]] +stage = "3.11.6" +state = "actual" +date = 2023-10-02 + +[[release."3.11"]] +stage = "3.11.7" +state = "actual" +date = 2023-12-04 + +[[release."3.11"]] +stage = "3.11.8" +state = "actual" +date = 2024-02-06 + +[[release."3.11"]] +stage = "3.11.9" +state = "actual" +date = 2024-04-02 + +[[release."3.11"]] +stage = "3.11.10" +state = "actual" +date = 2024-09-07 + +[[release."3.11"]] +stage = "3.11.11" +state = "actual" +date = 2024-12-03 + +[[release."3.11"]] +stage = "3.11.12" +state = "actual" +date = 2025-04-08 + +[[release."3.11"]] +stage = "3.11.13" +state = "actual" +date = 2025-06-03 + +[[release."3.11"]] +stage = "3.11.14" +state = "actual" +date = 2025-10-09 + +# -- Python 3.12 -------------------------------------------------------------- + +[metadata."3.12"] +pep = 693 +status = "bugfix" +branch = "3.12" +release-manager = "Thomas Wouters" +start-of-development = 2022-05-08 +feature-freeze = 2023-05-22 +first-release = 2023-10-02 +end-of-bugfix = 2025-04-08 +end-of-life = 2028-10-01 + +[[release."3.12"]] +stage = "3.12.0 alpha 1" +state = "actual" +date = 2022-10-24 + +[[release."3.12"]] +stage = "3.12.0 alpha 2" +state = "actual" +date = 2022-11-14 + +[[release."3.12"]] +stage = "3.12.0 alpha 3" +state = "actual" +date = 2022-12-06 + +[[release."3.12"]] +stage = "3.12.0 alpha 4" +state = "actual" +date = 2023-01-10 + +[[release."3.12"]] +stage = "3.12.0 alpha 5" +state = "actual" +date = 2023-02-07 + +[[release."3.12"]] +stage = "3.12.0 alpha 6" +state = "actual" +date = 2023-03-07 + +[[release."3.12"]] +stage = "3.12.0 alpha 7" +state = "actual" +date = 2023-04-04 + +[[release."3.12"]] +stage = "3.12.0 beta 1" +state = "actual" +date = 2023-05-22 + +[[release."3.12"]] +stage = "3.12.0 beta 2" +state = "actual" +date = 2023-06-06 + +[[release."3.12"]] +stage = "3.12.0 beta 3" +state = "actual" +date = 2023-06-19 + +[[release."3.12"]] +stage = "3.12.0 beta 4" +state = "actual" +date = 2023-07-11 + +[[release."3.12"]] +stage = "3.12.0 candidate 1" +state = "actual" +date = 2023-08-06 + +[[release."3.12"]] +stage = "3.12.0 candidate 2" +state = "actual" +date = 2023-09-06 + +[[release."3.12"]] +stage = "3.12.0 candidate 3" +state = "actual" +date = 2023-09-19 + +[[release."3.12"]] +stage = "3.12.0 final" +state = "actual" +date = 2023-10-02 + +[[release."3.12"]] +stage = "3.12.1" +state = "actual" +date = 2023-12-07 + +[[release."3.12"]] +stage = "3.12.2" +state = "actual" +date = 2024-02-06 + +[[release."3.12"]] +stage = "3.12.3" +state = "actual" +date = 2024-04-09 + +[[release."3.12"]] +stage = "3.12.4" +state = "actual" +date = 2024-06-06 + +[[release."3.12"]] +stage = "3.12.5" +state = "actual" +date = 2024-08-06 + +[[release."3.12"]] +stage = "3.12.6" +state = "actual" +date = 2024-09-06 + +[[release."3.12"]] +stage = "3.12.7" +state = "actual" +date = 2024-10-01 + +[[release."3.12"]] +stage = "3.12.8" +state = "actual" +date = 2024-12-03 + +[[release."3.12"]] +stage = "3.12.9" +state = "actual" +date = 2025-02-04 + +[[release."3.12"]] +stage = "3.12.10" +state = "actual" +date = 2025-04-08 + +[[release."3.12"]] +stage = "3.12.11" +state = "actual" +date = 2025-06-03 + +# -- Python 3.13 -------------------------------------------------------------- + +[metadata."3.13"] +pep = 719 +status = "bugfix" +branch = "3.13" +release-manager = "Thomas Wouters" +start-of-development = 2023-05-22 +feature-freeze = 2024-05-08 +first-release = 2024-10-07 +end-of-bugfix = 2026-10-07 +end-of-life = 2029-10-01 + +[[release."3.13"]] +stage = "3.13.0 alpha 1" +state = "actual" +date = 2023-10-13 + +[[release."3.13"]] +stage = "3.13.0 alpha 2" +state = "actual" +date = 2023-11-22 + +[[release."3.13"]] +stage = "3.13.0 alpha 3" +state = "actual" +date = 2024-01-17 + +[[release."3.13"]] +stage = "3.13.0 alpha 4" +state = "actual" +date = 2024-02-15 + +[[release."3.13"]] +stage = "3.13.0 alpha 5" +state = "actual" +date = 2024-03-12 + +[[release."3.13"]] +stage = "3.13.0 alpha 6" +state = "actual" +date = 2024-04-09 + +[[release."3.13"]] +stage = "3.13.0 beta 1" +state = "actual" +date = 2024-05-08 + +[[release."3.13"]] +stage = "3.13.0 beta 2" +state = "actual" +date = 2024-06-05 + +[[release."3.13"]] +stage = "3.13.0 beta 3" +state = "actual" +date = 2024-06-27 + +[[release."3.13"]] +stage = "3.13.0 beta 4" +state = "actual" +date = 2024-07-18 + +[[release."3.13"]] +stage = "3.13.0 candidate 1" +state = "actual" +date = 2024-08-01 + +[[release."3.13"]] +stage = "3.13.0 candidate 2" +state = "actual" +date = 2024-09-06 + +[[release."3.13"]] +stage = "3.13.0 candidate 3" +state = "actual" +date = 2024-10-01 + +[[release."3.13"]] +stage = "3.13.0 final" +state = "actual" +date = 2024-10-07 + +[[release."3.13"]] +stage = "3.13.1" +state = "actual" +date = 2024-12-03 + +[[release."3.13"]] +stage = "3.13.2" +state = "actual" +date = 2025-02-04 + +[[release."3.13"]] +stage = "3.13.3" +state = "actual" +date = 2025-04-08 + +[[release."3.13"]] +stage = "3.13.4" +state = "actual" +date = 2025-06-03 + +[[release."3.13"]] +stage = "3.13.5" +state = "actual" +date = 2025-06-11 +note = "hotfix" + +[[release."3.13"]] +stage = "3.13.6" +state = "actual" +date = 2025-08-06 + +[[release."3.13"]] +stage = "3.13.7" +state = "actual" +date = 2025-08-14 + +[[release."3.13"]] +stage = "3.13.8" +state = "actual" +date = 2025-10-07 + +[[release."3.13"]] +stage = "3.13.9" +state = "actual" +date = 2025-10-14 + +[[release."3.13"]] +stage = "3.13.10" +state = "expected" +date = 2025-12-02 + +[[release."3.13"]] +stage = "3.13.11" +state = "expected" +date = 2026-02-03 + +[[release."3.13"]] +stage = "3.13.12" +state = "expected" +date = 2026-04-07 + +[[release."3.13"]] +stage = "3.13.13" +state = "expected" +date = 2026-06-09 + +[[release."3.13"]] +stage = "3.13.14" +state = "expected" +date = 2026-08-04 + +[[release."3.13"]] +stage = "3.13.15" +state = "expected" +date = 2026-10-06 + +# -- Python 3.14 -------------------------------------------------------------- + +[metadata."3.14"] +pep = 745 +status = "prerelease" +branch = "3.14" +release-manager = "Hugo van Kemenade" +start-of-development = 2024-05-08 +feature-freeze = 2025-05-07 +first-release = 2025-10-07 +end-of-bugfix = 2027-10-07 +end-of-life = 2030-10-01 + +[[release."3.14"]] +stage = "3.14.0 alpha 1" +state = "actual" +date = 2024-10-15 + +[[release."3.14"]] +stage = "3.14.0 alpha 2" +state = "actual" +date = 2024-11-19 + +[[release."3.14"]] +stage = "3.14.0 alpha 3" +state = "actual" +date = 2024-12-17 + +[[release."3.14"]] +stage = "3.14.0 alpha 4" +state = "actual" +date = 2025-01-14 + +[[release."3.14"]] +stage = "3.14.0 alpha 5" +state = "actual" +date = 2025-02-11 + +[[release."3.14"]] +stage = "3.14.0 alpha 6" +state = "actual" +date = 2025-03-14 + +[[release."3.14"]] +stage = "3.14.0 alpha 7" +state = "actual" +date = 2025-04-08 + +[[release."3.14"]] +stage = "3.14.0 beta 1" +state = "actual" +date = 2025-05-07 + +[[release."3.14"]] +stage = "3.14.0 beta 2" +state = "actual" +date = 2025-05-26 + +[[release."3.14"]] +stage = "3.14.0 beta 3" +state = "actual" +date = 2025-06-17 + +[[release."3.14"]] +stage = "3.14.0 beta 4" +state = "actual" +date = 2025-07-08 + +[[release."3.14"]] +stage = "3.14.0 candidate 1" +state = "actual" +date = 2025-07-22 + +[[release."3.14"]] +stage = "3.14.0 candidate 2" +state = "actual" +date = 2025-08-14 + +[[release."3.14"]] +stage = "3.14.0 candidate 3" +state = "actual" +date = 2025-09-18 + +[[release."3.14"]] +stage = "3.14.0 final" +state = "actual" +date = 2025-10-07 + +# -- Python 3.15 -------------------------------------------------------------- + +[metadata."3.15"] +pep = 790 +status = "feature" +branch = "main" +release-manager = "Hugo van Kemenade" +start-of-development = 2025-05-07 +feature-freeze = 2026-05-05 +first-release = 2026-10-01 +end-of-bugfix = 2028-10-01 +end-of-life = 2031-10-01 + +[[release."3.15"]] +stage = "3.15.0 alpha 1" +state = "actual" +date = 2025-10-14 + +[[release."3.15"]] +stage = "3.15.0 alpha 2" +state = "expected" +date = 2025-11-18 + +[[release."3.15"]] +stage = "3.15.0 alpha 3" +state = "expected" +date = 2025-12-16 + +[[release."3.15"]] +stage = "3.15.0 alpha 4" +state = "expected" +date = 2026-01-13 + +[[release."3.15"]] +stage = "3.15.0 alpha 5" +state = "expected" +date = 2026-02-10 + +[[release."3.15"]] +stage = "3.15.0 alpha 6" +state = "expected" +date = 2026-03-10 + +[[release."3.15"]] +stage = "3.15.0 alpha 7" +state = "expected" +date = 2026-04-07 + +[[release."3.15"]] +stage = "3.15.0 beta 1" +state = "expected" +date = 2026-05-05 + +[[release."3.15"]] +stage = "3.15.0 beta 2" +state = "expected" +date = 2026-05-26 + +[[release."3.15"]] +stage = "3.15.0 beta 3" +state = "expected" +date = 2026-06-16 + +[[release."3.15"]] +stage = "3.15.0 beta 4" +state = "expected" +date = 2026-07-14 + +[[release."3.15"]] +stage = "3.15.0 candidate 1" +state = "expected" +date = 2026-07-28 + +[[release."3.15"]] +stage = "3.15.0 candidate 2" +state = "expected" +date = 2026-09-01 + +[[release."3.15"]] +stage = "3.15.0 final" +state = "expected" +date = 2026-10-01 diff --git a/release_management/serialize.py b/release_management/serialize.py new file mode 100644 index 00000000000..3418098278c --- /dev/null +++ b/release_management/serialize.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +import dataclasses +import json + +from release_management import ROOT_DIR, load_python_releases + +TYPE_CHECKING = False +if TYPE_CHECKING: + from release_management import VersionMetadata + + +def create_release_json() -> str: + python_releases = dataclasses.asdict(load_python_releases()) + return json.dumps( + python_releases, + indent=2, + sort_keys=False, + ensure_ascii=False, + default=str, + ) + + +def create_release_cycle() -> str: + metadata = load_python_releases().metadata + all_versions = sorted( + ((d.first_release, v) for v, d in metadata.items()), reverse=True + ) + versions = [v for _date, v in all_versions if version_to_tuple(v) >= (2, 6)] + release_cycle = {version: version_info(metadata[version]) for version in versions} + rc_json = json.dumps(release_cycle, indent=2, sort_keys=False, ensure_ascii=False) + return f'{rc_json}\n' + + +def version_to_tuple(version: str, /) -> tuple[int, ...]: + return tuple(map(int, version.split('.'))) + + +def version_info(metadata: VersionMetadata, /) -> dict[str, str | int]: + end_of_life = metadata.end_of_life.isoformat() + if metadata.status != 'end-of-life': + end_of_life = end_of_life.removesuffix('-01') + return { + 'branch': metadata.branch, + 'pep': metadata.pep, + 'status': metadata.status, + 'first_release': metadata.first_release.isoformat(), + 'end_of_life': end_of_life, + 'release_manager': metadata.release_manager, + } diff --git a/release_management/update_release_schedules.py b/release_management/update_release_schedules.py new file mode 100644 index 00000000000..aeb33583dac --- /dev/null +++ b/release_management/update_release_schedules.py @@ -0,0 +1,179 @@ +"""Update release schedules in PEPs. + +The ``python-releases.toml`` data is treated as authoritative for the given +versions in ``VERSIONS_TO_REGENERATE``. Each PEP must contain markers for the +start and end of each release schedule (feature, bugfix, and security, as +appropriate). These are: + + .. release schedule: feature + .. release schedule: bugfix + .. release schedule: security + .. release schedule: ends + +This script will use the dates in the [[release."{version}"]] tables to create +and update the release schedule lists in each PEP. + +Optionally, to add a comment or note to a particular release, use the ``note`` +field, which will append the given text in brackets to the relevant line. + +Usage: + + $ python -m release_management update-peps + $ # or + $ make regen-all +""" + +from __future__ import annotations + +import datetime as dt + +from release_management import ( + PEP_ROOT, + ReleaseInfo, + VersionMetadata, + load_python_releases, +) + +TYPE_CHECKING = False +if TYPE_CHECKING: + from collections.abc import Iterator + + from release_management import ReleaseSchedules, ReleaseState, VersionMetadata + +TODAY = dt.date.today() + +SKIPPED_VERSIONS = frozenset({ + '1.6', + '2.0', + '2.1', + '2.2', + '2.3', + '2.4', + '2.5', + '2.6', + '2.7', + '3.0', + '3.1', + '3.2', + '3.3', + '3.4', + '3.5', + '3.6', + '3.7', +}) + + +def update_peps() -> None: + python_releases = load_python_releases() + for version, metadata in python_releases.metadata.items(): + if version in SKIPPED_VERSIONS: + continue + schedules = create_schedules( + version, + python_releases.releases[version], + metadata.start_of_development, + metadata.end_of_bugfix, + ) + update_pep(metadata, schedules) + + +def create_schedules( + version: str, + releases: list[ReleaseInfo], + start_of_development: dt.date, + bugfix_ends: dt.date, +) -> ReleaseSchedules: + schedules: ReleaseSchedules = { + ('feature', 'actual'): [], + ('feature', 'expected'): [], + ('bugfix', 'actual'): [], + ('bugfix', 'expected'): [], + ('security', 'actual'): [], + } + + # first entry into the dictionary + db_state: ReleaseState = 'actual' if TODAY >= start_of_development else 'expected' + schedules['feature', db_state].append( + ReleaseInfo( + stage=f'{version} development begins', + state=db_state, + date=start_of_development, + ) + ) + + for release_info in releases: + if release_info.stage.startswith(f'{version}.0'): + schedules['feature', release_info.state].append(release_info) + elif release_info.date <= bugfix_ends: + schedules['bugfix', release_info.state].append(release_info) + else: + assert release_info.state == 'actual', release_info + schedules['security', release_info.state].append(release_info) + + return schedules + + +def update_pep(metadata: VersionMetadata, schedules: ReleaseSchedules) -> None: + pep_path = PEP_ROOT.joinpath(f'pep-{metadata.pep:0>4}.rst') + pep_lines = iter(pep_path.read_text(encoding='utf-8').splitlines()) + output_lines: list[str] = [] + schedule_name = '' + for line in pep_lines: + output_lines.append(line) + if line.startswith('.. ') and 'schedule' in line: + assert line.startswith('.. release schedule: ') + schedule_name = line.removeprefix('.. release schedule: ') + assert schedule_name in {'feature', 'bugfix', 'security'} + output_lines += generate_schedule_lists( + schedules, + schedule_name=schedule_name, + feature_freeze_date=metadata.feature_freeze, + ) + + # skip source lines until the end of schedule marker + while True: + line = next(pep_lines, None) + if line == '.. release schedule: ends': + output_lines.append(line) + break + if line is None: + raise ValueError('No end of schedule marker found!') + + if not schedule_name: + raise ValueError('No schedule markers found!') + + output_lines.append('') # trailing newline + with open(pep_path, 'wb') as f: + f.write(b'\n'.join(line.encode('utf-8') for line in output_lines)) + + +def generate_schedule_lists( + schedules: ReleaseSchedules, + *, + schedule_name: str, + feature_freeze_date: dt.date = dt.date.min, +) -> Iterator[str]: + state: ReleaseState + for state in 'actual', 'expected': + if not schedules.get((schedule_name, state)): + continue + + yield '' + if schedule_name != 'security': + yield f'{state.title()}:' + yield '' + for release_info in schedules[schedule_name, state]: + yield release_info.schedule_bullet + if release_info.note: + yield f' ({release_info.note})' + if release_info.date == feature_freeze_date: + yield ' (No new features beyond this point.)' + + if schedule_name == 'bugfix': + yield ' (Final regular bugfix release with binary installers)' + + yield '' + + +if __name__ == '__main__': + update_peps() diff --git a/requirements.txt b/requirements.txt index ffcbe1a8aa6..07d631cee6f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,6 @@ sphinx-notfound-page >= 1.0.2 # For tests pytest pytest-cov + +# For python-releases.toml +tomli >= 1.1.0 ; python_version < "3.11" From caa914849ff77625e4159e446ee846adbb636c34 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:42:56 +0200 Subject: [PATCH 16/70] PEP 745: Add planned bugfix releases for Python 3.14 to python-releases.toml (#4693) --- peps/pep-0745.rst | 7 ++- release_management/python-releases.toml | 60 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/peps/pep-0745.rst b/peps/pep-0745.rst index 8ca702bb77c..95a24212487 100644 --- a/peps/pep-0745.rst +++ b/peps/pep-0745.rst @@ -57,7 +57,10 @@ Actual: .. release schedule: ends -Subsequent bugfix releases every two months. +Bugfix releases +--------------- + +.. release schedule: bugfix Expected: @@ -73,7 +76,9 @@ Expected: - 3.14.10: Tuesday, 2027-06-01 - 3.14.11: Tuesday, 2027-08-03 - 3.14.12: Tuesday, 2027-10-05 + (Final regular bugfix release with binary installers) +.. release schedule: ends Source-only security fix releases --------------------------------- diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 4dc9f1448e6..0231d4e5640 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3452,6 +3452,66 @@ stage = "3.14.0 final" state = "actual" date = 2025-10-07 +[[release."3.14"]] +stage = "3.14.1" +state = "expected" +date = 2025-12-02 + +[[release."3.14"]] +stage = "3.14.2" +state = "expected" +date = 2026-02-03 + +[[release."3.14"]] +stage = "3.14.3" +state = "expected" +date = 2026-04-07 + +[[release."3.14"]] +stage = "3.14.4" +state = "expected" +date = 2026-06-09 + +[[release."3.14"]] +stage = "3.14.5" +state = "expected" +date = 2026-08-04 + +[[release."3.14"]] +stage = "3.14.6" +state = "expected" +date = 2026-10-06 + +[[release."3.14"]] +stage = "3.14.7" +state = "expected" +date = 2026-12-01 + +[[release."3.14"]] +stage = "3.14.8" +state = "expected" +date = 2027-02-02 + +[[release."3.14"]] +stage = "3.14.9" +state = "expected" +date = 2027-04-06 + +[[release."3.14"]] +stage = "3.14.10" +state = "expected" +date = 2027-06-01 + +[[release."3.14"]] +stage = "3.14.11" +state = "expected" +date = 2027-08-03 + +[[release."3.14"]] +stage = "3.14.12" +state = "expected" +date = 2027-10-05 + # -- Python 3.15 -------------------------------------------------------------- [metadata."3.15"] From 93d25e48655824bc0618eba36381ffac19e55467 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:13:06 +0200 Subject: [PATCH 17/70] PEP 596: Mark Python 3.9 as end-of-life in python-releases.toml (#4694) --- release_management/python-releases.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 0231d4e5640..5e8a0483d4f 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -2511,14 +2511,14 @@ note = "final security release" [metadata."3.9"] pep = 596 -status = "security" +status = "end-of-life" branch = "3.9" release-manager = "Łukasz Langa" start-of-development = 2019-06-04 feature-freeze = 2020-05-18 first-release = 2020-10-05 end-of-bugfix = 2022-05-17 -end-of-life = 2025-10-01 +end-of-life = 2025-10-31 [[release."3.9"]] stage = "3.9.0 alpha 1" From a0e2739f8d7622c4a250173acbbfd4d66e7736c0 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:25:45 +0200 Subject: [PATCH 18/70] Mark Python 3.12 as security and 3.14 as bugfix (#4696) --- release_management/python-releases.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 5e8a0483d4f..8d572b6f506 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3066,7 +3066,7 @@ date = 2025-10-09 [metadata."3.12"] pep = 693 -status = "bugfix" +status = "security" branch = "3.12" release-manager = "Thomas Wouters" start-of-development = 2022-05-08 @@ -3368,7 +3368,7 @@ date = 2026-10-06 [metadata."3.14"] pep = 745 -status = "prerelease" +status = "bugfix" branch = "3.14" release-manager = "Hugo van Kemenade" start-of-development = 2024-05-08 From b4332032bdc8ca9ae317b5986b7417e3beadef8c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 10 Nov 2025 18:02:30 +0100 Subject: [PATCH 19/70] PEP 793: Mark as Accepted (#4695) --- peps/pep-0793.rst | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/peps/pep-0793.rst b/peps/pep-0793.rst index 5caee64aee5..868e14945e6 100644 --- a/peps/pep-0793.rst +++ b/peps/pep-0793.rst @@ -2,12 +2,13 @@ PEP: 793 Title: PyModExport: A new entry point for C extension modules Author: Petr Viktorin Discussions-To: https://discuss.python.org/t/93444 -Status: Draft +Status: Accepted Type: Standards Track Created: 23-May-2025 Python-Version: 3.15 Post-History: `14-Mar-2025 `__, `27-May-2025 `__, +Resolution: `23-Oct-2025 `__ Abstract @@ -633,14 +634,11 @@ Example Reference Implementation ======================== -A draft implementation is available in a -`GitHub branch `_. - +Implementation is tracked in +`GitHub issue #140550 `_. -Open Issues -=========== - -(Add yours!) +A draft implementation was available in a +`GitHub branch `_. Rejected Ideas From 6774537a36e8903fa31e9135b67d36c8fddf2736 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Wed, 12 Nov 2025 05:59:17 -0600 Subject: [PATCH 20/70] PEP 13: Clarify tie resolution (#4672) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-0013.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/peps/pep-0013.rst b/peps/pep-0013.rst index 1d59dcb6120..37b45c0be9d 100644 --- a/peps/pep-0013.rst +++ b/peps/pep-0013.rst @@ -107,8 +107,8 @@ A council election consists of two phases: * Phase 2: Each core team member can assign zero to five stars to each candidate. Voting is performed anonymously. The outcome of the vote is determined using the `STAR voting system `__, - modified to use the `Multi-winner Bloc STAR `__) - approach. If a tie occurs, it may + modified to use the `Multi-winner Bloc STAR `__ + approach. If a tie that is not automatically resolved by the election software occurs, it may be resolved by mutual agreement among the candidates, or else the winner will be chosen at random. @@ -358,6 +358,9 @@ History of amendments Adopted Multi-winner Bloc STAR voting for council elections. * `2024-12-10 `__: Added a one-week deadline for seconding a vote of no confidence. +* `2025-11-12 `__: + Clarified that the software used for elections may resolve ties + automatically if possible. From b6bc20583cbd4d4b8585deaa5f070fd1a2397421 Mon Sep 17 00:00:00 2001 From: David Foster Date: Wed, 12 Nov 2025 09:44:51 -0500 Subject: [PATCH 21/70] PEP 747: Link to mypy reference implementation (#4700) --- peps/pep-0747.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/peps/pep-0747.rst b/peps/pep-0747.rst index 9bccabfef2b..06a16262432 100644 --- a/peps/pep-0747.rst +++ b/peps/pep-0747.rst @@ -516,12 +516,14 @@ Reference Implementation Pyright (version 1.1.379) provides a reference implementation for ``TypeForm``. -Mypy contributors also `plan to implement `__ -support for ``TypeForm``. +Mypy (`commit 1b7e71`_; Nov 3, 2025) provides +a reference implementation for ``TypeForm``. A reference implementation of the runtime component is provided in the ``typing_extensions`` module. +.. _commit 1b7e71: https://github.com/python/mypy/commit/1b7e717ecc56cd13d76bc110a1db2796e8b3c918 + Rejected Ideas ============== From 61e615e77162268a64e9c69f33f19eed14daabbb Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 13 Nov 2025 10:00:01 +0100 Subject: [PATCH 22/70] 804: Clarify role of `query` commands and version mappings (#3) --- peps/pep-0804.rst | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index dfaf23fc4a4..5b7cc4ea1bc 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -34,8 +34,7 @@ used in other ecosystems, which would allow to: - Including the needed external dependencies *with the package names used by the relevant system package manager on the user's system* in error messages emitted by Python package installers and build frontends, - as well as allowing the user to query for those names directly to obtain install - instructions. + as well as allowing the user to obtain install instructions for those packages. Packaging ecosystems like Linux distros, conda, Homebrew, Spack, and Nix need full sets of dependencies for Python packages, and have tools like pyp2rpm_ @@ -150,7 +149,7 @@ standalone tool, or as a new subcommand in any Python development workflow tool (e.g., Pip, Poetry, Hatch, PDM, uv). To this end, each ecosystem mapping can provide a list of package managers -known to be compatible, with templated instructions on how to install and query +known to be compatible, with templated instructions on how to install and query installed packages. The provided install command templates are paired with query command templates so those tools can check whether the needed packages are already present without having to attempt an install operation (which might be expensive and have unintended @@ -254,8 +253,16 @@ which package managers are available in the ecosystem and how to use them. This take a list of dictionaries, with each of them reporting the following fields: - ``name`` (string), unique identifier for this package manager. Usually, the executable name. + - ``commands`` (list of dictionaries), the commands to run to install the mapped package(s) and - check whether they are already installed. + check whether they are already installed. Two types of commands are proposed: + + - ``install``, to generate install instructions. The exit code MUST be ``0`` on success. + + - ``query``, to check whether a given package is already installed. If the package is + installed, the command MUST result in an exit code of ``0``. Otherwise, an exit code + of ``1`` SHOULD be used. + - ``specifier_syntax``: instructions on how to map a subset of PEP 440 specifiers to the target package manager. Three levels of support are offered: name-only, exact-version-only, and version-range compatibility (with per-operator translations). @@ -270,6 +277,26 @@ contain mapping documents named ``{ecosystem-identifier}.mapping.json``. The cen registry and known ecosystem documents MAY also be distributed in this directory, as ``registry.json`` and ``known-ecosystems.json``, respectively. +.. note:: + + The ``specifier_syntax`` mappings are meant to provide interoperability between + ecosystems where choosing which package version to install is possible. For + example, this is not the case in many Linux distributions, where each distro + release commits to a package version during its lifecycle (although often + with the necessary security backports). + + In these cases, the ``install`` command could be used, optimistically, in + "name-only" mode, hoping that the OS-provided version is a good fit. A more + pessimistic alternative would be to use the ``query`` command first to see if + the available version matches the project constraints, and then install the + package by name. + + Even in those cases, perfect 1:1 version matching is not always possible due to how + different ecosystems map upstream releases to repackaged versions (e.g. the epoch + had to be bumped to accommodate a change of release schema). In that regard, we do + not encode explicit mapping semantics for epochs or pre-releases. + + Known ecosystems ---------------- @@ -560,7 +587,8 @@ Each entry in this list is defined as a dictionary with these fields: * - ``commands`` - ``dict[Literal['install', 'query'], dict[Literal['command', 'requires_elevation', 'multiple_specifiers'], list[str] | bool | Literal['always', 'name-only', 'never']]]`` - Commands used to install or query the given package(s). Only two keys - are allowed: ``install`` and ``query``. Their value is a dictionary + are allowed: ``install`` (to generate install instructions) and ``query`` (to + check whether a given package is already installed). Their value is a dictionary with: - a required key ``command`` that takes a list of strings From 5aea6db9252a9058bd28e709ae754536aba7cac3 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 13 Nov 2025 10:00:13 +0100 Subject: [PATCH 23/70] 804: Clarify mapping filenames and versioning (#4) --- peps/pep-0804.rst | 59 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 5b7cc4ea1bc..5900fa57563 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -267,8 +267,9 @@ take a list of dictionaries, with each of them reporting the following fields: the target package manager. Three levels of support are offered: name-only, exact-version-only, and version-range compatibility (with per-operator translations). -Each mapping MUST have a canonical URL for online retrieval. These mappings -MAY also be packaged for offline distribution in each platform. The authors +Each mapping MUST have a canonical URL for online retrieval, with the +filename following the rules described in the section below. These +mappings MAY also be packaged for offline distribution in each platform. The authors recommend placing in the standard location for data artifacts in each operating system; e.g. ``$XDG_DATA_DIRS`` on Linux and others, ``~/Library/Application Support`` on macOS, and ``%LOCALAPPDATA%`` for Windows. The subdirectory identifier MUST @@ -303,15 +304,51 @@ Known ecosystems The list of known ecosystems has two roles: 1. Reporting the canonical URL for its mapping. -2. Assigning a short identifier to each ecosystem. This is the identifier - that MUST be used in the mapping filenames mentioned above so they can be - found in the local filesystem. +2. Assigning a unique, short identifier to each ecosystem. -For ecosystems corresponding to Linux distributions, the identifier MUST be the -one reported by their `os-release `__ -``ID`` parameter. For other ecosystems, it MUST be decided during the submission to -the list of known ecosystems document. It MUST only use the characters allowed in -``os-release``'s ``ID`` field, as per this regex ``[a-z0-9\-_.]+``. +Ecosystem identifiers +^^^^^^^^^^^^^^^^^^^^^ + +The identifier MUST conform to this regex: ``[a-z0-9\-_.]+(\+[a-z0-9\-_.])?``. +In other words, a first field optionally followed by a second, separated +by a ``+`` symbol. + +For ecosystems corresponding to Linux distributions, +the first field MUST correspond to the ``ID`` string as specified in the +`os-release `__ +specification. If provided, the second field MUST correspond to the +``VERSION_ID`` string if relevant. + +Since the version field is optional, tools SHOULD try to access the versioned +identifier but fallback to the name-only identifier if not found. + +The complete filename MUST be ``{identifier}.mapping.json``. + +Some examples: + +.. list-table:: + + * - Ecosystem + - Identifier + - Filename + * - Debian Bookworm + - ``debian+12`` + - ``debian+12.mapping.json`` + * - Fedora 40 + - ``fedora+40`` + - ``fedora+40.mapping.json`` + * - Ubuntu 24.04 + - ``ubuntu+24.04`` + - ``ubuntu+24.04.mapping.json`` + * - Arch Linux (rolling) + - ``arch`` + - ``arch.mapping.json`` + * - Homebrew + - ``homebrew`` + - ``homebrew.mapping.json`` + * - conda-forge + - ``conda-forge`` + - ``conda-forge.mapping.json`` Schema details -------------- @@ -432,7 +469,7 @@ The known ecosystems list is specified by the following * - Required - True -This dictionary maps non-empty string keys referring to the ecosystem identifiers +This dictionary maps non-empty string keys referring to the ecosystem *identifiers* to a sub-dictionary defined as: .. list-table:: From 15e5afba2e5679c4541c02986bd00c463023c4be Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 13 Nov 2025 10:00:23 +0100 Subject: [PATCH 24/70] 804: Mapping reusability and customization (#5) --- peps/pep-0804.rst | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 5900fa57563..a9441d5b416 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -1327,6 +1327,78 @@ We suggest simply checking that the provided identifiers are well-formed. Future work may choose to also enforce that the identifiers are recognized as canonical, once the central registry has matured with significant adoption. +Inheritance and cross-referenced mappings +----------------------------------------- + +A potential improvement to improve the reusability of mappings is to provide a +mechanism to inherit a parent mapping and extend it or replace it with additional +values. The authors have decided to not add this feature given the implied complexity +(e.g. URL resolution, nested dependencies, possibilities of broken resources). Instead, +the following alternatives are proposed: + +- For mapping authors, automate the generation of derived mappings via scripting and + cronjobs. For example, simple logic such as fetching the parent mapping, applying the + necesary modifications and republishing it to the target location should not result + in much maintenance burden. + +- For end-users wishing to extend a given mapping with custom overrides, client-side + tools should implement the necessary affordances to do this easily. For example, + a tool such as ``pyproject-external`` could provide the following CLI flags or + environment variables: + + - ``--use-mapping`` / ``_USE_MAPPING``: Use the given local or + remote mapping instead of the the canonical location. + + - ``--patch-mapping`` / ``_PATCH_MAPPING``: Given a local or + remote mapping, replace the matching keys in the canonical location + and append the non-matching ones. + + - ``--extend-mapping`` / ``_EXTEND_MAPPING``: Given a local + or remote mapping, append its contents to the canonical one. Assuming + the tool allows the user to pick different mapping options if more than + one is available, this option enriches the set of options without + complete overrides. + +So, for example, given a package with this ``external`` table: + +.. code-block:: toml + + [external] + build-requires = [ + "dep:virtual/compiler/c", + ] + host-requires = [ + "dep:generic/libffi", + ] + +And a target ecosystem that maps ``dep:virtual/compiler/c`` to ``gcc`` +but ``clang`` is preferred, the following mapping override could be provided: + +.. code-block:: json + + { + "$schema": "https://raw.githubusercontent.com/jaimergp/external-metadata-mappings/main/schemas/external-mapping.schema.json", + "schema_version": 1, + "name": "ecosystem override", + "description": "Mapping override for my ecosystem of choice", + "mappings": [ + { + "id": "dep:virtual/compiler/c", + "description": "Clang override", + "specs": "clang" + } + ] + } + +Then, it would be used like this: + +.. code-block:: shell + + $ python -m my-tool show \ + sdist/cryptography-46.0.2.tar.gz \ + --output install-command \ + --patch-mapping=my-override.mapping.json + Tracking package name changes ----------------------------- From 3ede647287e06b691716d3c4433304af234159e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 13 Nov 2025 10:36:56 +0100 Subject: [PATCH 25/70] PEP 814: Add frozendict built-in type (#4699) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .github/CODEOWNERS | 1 + peps/pep-0814.rst | 347 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 peps/pep-0814.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ad28f35ce2a..a003f6914b4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -687,6 +687,7 @@ peps/pep-0807.rst @dstufft peps/pep-0809.rst @zooba peps/pep-0810.rst @pablogsal @DinoV @Yhg1s peps/pep-0811.rst @sethmlarson @gpshead +peps/pep-0814.rst @vstinner @corona10 # ... peps/pep-2026.rst @hugovk # ... diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst new file mode 100644 index 00000000000..d35cc8bbd80 --- /dev/null +++ b/peps/pep-0814.rst @@ -0,0 +1,347 @@ +PEP: 814 +Title: Add frozendict built-in type +Author: Victor Stinner , Donghee Na +Status: Draft +Type: Standards Track +Created: 12-Nov-2025 +Python-Version: 3.15 + +Abstract +======== + +A new public immutable type ``frozendict`` is added to the ``builtins`` +module. + +We expect ``frozendict`` to be safe by design, as it prevents any unintended +modifications. This addition benefits not only CPython’s standard +library, but also third-party maintainers who can take advantage of a +reliable, immutable dictionary type. + + +Rationale +========= + +The proposed ``frozendict`` type: + +* implements the ``collections.abc.Mapping`` protocol, +* supports pickling. + +The following use cases illustrate why an immutable mapping is +desirable: + +* Immutable mappings are hashable which allows their use as dictionary + keys or set elements. + +* This hashable property permits functions decorated with + ``@functools.lru_cache()`` to accept immutable mappings as arguments. + Unlike an immutable mapping, passing a plain ``dict`` to such a function + results in error. + +* Using an immutable mapping as a function parameter's default value + avoids the problem of mutable default values. + +* Immutable mappings can be used to safely share dictionaries across + thread and asynchronous task boundaries. The immutability makes it + easier to reason about threads and asynchronous tasks. + +There are already third-party ``frozendict`` and ``frozenmap`` packages +available on PyPI, proving that there is demand for +immutable mappings. + + +Specification +============= + +A new public immutable type ``frozendict`` is added to the ``builtins`` +module. It is not a ``dict`` subclass but inherits directly from +``object``. + + +Construction +------------ + +``frozendict`` implements a ``dict``-like construction API: + +* ``frozendict()`` creates a new empty immutable mapping. + +* ``frozendict(**kwargs)`` creates a mapping from ``**kwargs``, + e.g. ``frozendict(x=1, y=2)``. + +* ``frozendict(collection)`` creates a mapping from the passed + collection object. The passed collection object can be: + + - a ``dict``, + - another ``frozendict``, + - or an iterable of key/value tuples. + +The insertion order is preserved. + + +Iteration +--------- + +As ``frozendict`` implements the standard ``collections.abc.Mapping`` +protocol, so all expected methods of iteration are supported:: + + assert list(m.items()) == [('foo', 'bar')] + assert list(m.keys()) == ['foo'] + assert list(m.values()) == ['bar'] + assert list(m) == ['foo'] + +Iterating on ``frozendict``, as on ``dict``, uses the insertion order. + + +Hashing +------- + +``frozendict`` instances can be hashable just like tuple objects:: + + hash(frozendict(foo='bar')) # works + hash(frozendict(foo=['a', 'b', 'c'])) # error, list is not hashable + +The hash value does not depend on the items' order. It is computed on +keys and values. Pseudo-code of ``hash(frozendict)``:: + + hash(frozenset(frozendict.items())) + +Equality test does not depend on the items' order either. Example:: + + >>> a = frozendict(x=1, y=2) + >>> b = frozendict(y=2, x=1) + >>> hash(a) == hash(b) + True + >>> a == b + True + + +Typing +------ + +It is possible to use the standard typing notation for ``frozendict``\ s:: + + m: frozendict[str, int] = frozendict(x=1) + + +Representation +-------------- + +``frozendict`` will not use a special syntax for its representation. +The ``repr()`` of a ``frozendict`` instance looks like this: + + >>> frozendict(x=1, y=2) + frozendict({'x': 1, 'y': 2}) + + +C API +----- + +Add the following APIs: + +* ``PyFrozenDict_Type`` +* ``PyFrozenDict_New(collection)`` function +* ``PyFrozenDict_Check()`` macro +* ``PyFrozenDict_CheckExact()`` macro + +Even if ``frozendict`` is not a ``dict`` subclass, it can be used with +``PyDict_GetItemRef()`` and similar "PyDict_Get" functions. + +Passing a ``frozendict`` to ``PyDict_SetItem()`` or ``PyDict_DelItem()`` +fails with ``TypeError``. ``PyDict_Check()`` on a ``frozendict`` is +false. + +Exposing the C API helps authors of C extensions supporting +``frozendict`` when they need to support thread-safe immutable +containers. It will be important since +:pep:`779` (Criteria for supported status for free-threaded Python) was +accepted, people need this for their migration. + + +Differences between ``dict`` and ``frozendict`` +=============================================== + +* ``dict`` has more methods than ``frozendict``: + + * ``__delitem__(key)`` + * ``__setitem__(key, value)`` + * ``clear()`` + * ``pop(key)`` + * ``popitem()`` + * ``setdefault(key, value)`` + * ``update(*args, **kwargs)`` + +* A ``frozendict`` can be hashed with ``hash(frozendict)`` if all keys + and values can be hashed. + + +Possible candidates for ``frozendict`` in the stdlib +==================================================== + +We have identified several stdlib modules where adopting ``frozendict`` +can enhance safety and prevent unintended modifications by design. We +also believe that there are additional potential use cases beyond the +ones listed below. + +Note: it remains possible to bind again a variable to a new modified +``frozendict`` or a new mutable ``dict``. + +Python modules +-------------- + +Replace ``dict`` with ``frozendict`` in function results: + +* ``email.headerregistry``: ``ParameterizedMIMEHeader.params()`` + (replace ``MappingProxyType``) +* ``enum``: ``EnumType.__members__()`` (replace ``MappingProxyType``) + +Replace ``dict`` with ``frozendict`` for constants: + +* ``_opcode_metadata``: ``_specializations``, ``_specialized_opmap``, + ``opmap`` +* ``_pydatetime``: ``specs`` (in ``_format_time()``) +* ``_pydecimal``: ``_condition_map`` +* ``bdb``: ``_MonitoringTracer.EVENT_CALLBACK_MAP`` +* ``dataclasses``: ``_hash_action`` +* ``dis``: ``deoptmap``, ``COMPILER_FLAG_NAMES`` +* ``functools``: ``_convert`` +* ``gettext``: ``_binary_ops``, ``_c2py_ops`` +* ``imaplib``: ``Commands``, ``Mon2num`` +* ``json.decoder``: ``_CONSTANTS``, ``BACKSLASH`` +* ``json.encoder``: ``ESCAPE_DCT`` +* ``json.tool``: ``_group_to_theme_color`` +* ``locale``: ``locale_encoding_alias``, ``locale_alias``, + ``windows_locale`` +* ``opcode``: ``_cache_format``, ``_inline_cache_entries`` +* ``optparse``: ``_builtin_cvt`` +* ``platform``: ``_ver_stages``, ``_default_architecture`` +* ``plistlib``: ``_BINARY_FORMAT`` +* ``ssl``: ``_PROTOCOL_NAMES`` +* ``stringprep``: ``b3_exceptions`` +* ``symtable``: ``_scopes_value_to_name`` +* ``tarfile``: ``PAX_NUMBER_FIELDS``, ``_NAMED_FILTERS`` +* ``token``: ``tok_name``, ``EXACT_TOKEN_TYPES`` +* ``tomllib._parser``: ``BASIC_STR_ESCAPE_REPLACEMENTS`` +* ``typing``: ``_PROTO_ALLOWLIST`` + +Extension modules +----------------- + +Replace ``dict`` with ``frozendict`` for constants: + +* ``errno``: ``errorcode`` + + +Relationship to PEP 416 frozendict +================================== + +Since 2012 (:pep:`416`), the Python ecosystem has evolved: + +* ``asyncio`` was added in 2014 (Python 3.4) +* Free threading was added in 2024 (Python 3.13) +* ``concurrent.interpreters`` was added in 2025 (Python 3.14) + +There are now more use cases to share immutable mappings. + +``frozendict`` now preserves the insertion order, whereas PEP 416 +``frozendict`` was unordered (as :pep:`603` ``frozenmap``). ``frozendict`` +relies on the ``dict`` implementation which preserves the insertion +order since Python 3.6. + +The first motivation to add ``frozendict`` was to implement a sandbox +in Python. It's no longer the case in this PEP. + +``types.MappingProxyType`` was added in 2012 (Python 3.3). This type is +not hashable and it's not possible to inherit from it. It's also easy to +retrieve the original dictionary which can be mutated, for example using +``gc.get_referents()``. + + +Relationship to PEP 603 frozenmap +================================= + +``collections.frozenmap`` has different properties than frozendict: + +* ``frozenmap`` items are unordered, whereas ``frozendict`` preserves + the insertion order. +* ``frozenmap`` has additional methods: + + * ``including(key, value)`` + * ``excluding(key)`` + * ``union(mapping=None, **kw)`` + +========== ============== ============== +Complexity ``frozenmap`` ``frozendict`` +========== ============== ============== +Lookup *O*\ (log *n*) *O*\ (1) +Copy *O*\ (1) *O*\ (*n*) +========== ============== ============== + + +Reference Implementation +======================== + +* The reference implementation is still a work-in-progress. +* ``frozendict`` shares most of its code with the ``dict`` type. +* Add ``PyFrozenDictObject`` which inherits from ``PyDictObject`` and + has an additional ``ma_hash`` member. + + +Thread Safety +============= + +Once the ``frozendict`` is created, it is immutable and can be shared +safely between threads without any synchronization. + + +Future Work +=========== + +We are also going to make ``frozendict`` to be more efficient in terms +of memory usage and performance compared to ``dict`` in future. + + +Rejected Ideas +============== + +Inherit from dict +----------------- + +If ``frozendict`` inherits from ``dict``, it would become possible to +call ``dict`` methods to mutate an immutable ``frozendict``. For +example, it would be possible to call +``dict.__setitem__(frozendict, key, value)``. + +It may be possible to prevent modifying ``frozendict`` using ``dict`` +methods, but that would require to explicitly exclude ``frozendict`` +which can affect ``dict`` performance. Also, there is a higher risk of +forgetting to exclude ``frozendict`` in some methods. + +If ``frozendict`` does not inherit from ``dict``, there is no such +issue. + + +New syntax for ``frozendict`` literals +-------------------------------------- + +Various syntaxes have been proposed to write ``frozendict`` literals. + +A new syntax can be added later if needed. + + +References +========== + +* :pep:`416` (``frozendict``) +* :pep:`603` (``collections.frozenmap``) + + +Acknowledgements +================ + +This PEP is based on prior work from Yury Selivanov (:pep:`603`). + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From cec08ccf29d1ece8f13e59ce5151f98b04515412 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 13 Nov 2025 14:38:56 +0100 Subject: [PATCH 26/70] PEP 814: Add Reference Impl. and Discussions-To (#4701) --- peps/pep-0814.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index d35cc8bbd80..36cfc0a7c85 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -1,6 +1,7 @@ PEP: 814 Title: Add frozendict built-in type Author: Victor Stinner , Donghee Na +Discussions-To: https://discuss.python.org/t/104854 Status: Draft Type: Standards Track Created: 12-Nov-2025 @@ -279,10 +280,10 @@ Copy *O*\ (1) *O*\ (*n*) Reference Implementation ======================== -* The reference implementation is still a work-in-progress. +* https://github.com/python/cpython/pull/141508 * ``frozendict`` shares most of its code with the ``dict`` type. -* Add ``PyFrozenDictObject`` which inherits from ``PyDictObject`` and - has an additional ``ma_hash`` member. +* Add ``PyFrozenDictObject`` structure which inherits from + ``PyDictObject`` and has an additional ``ma_hash`` member. Thread Safety From 67b709ac84918b7d10902de55fc6bcc0e2cfd4bd Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:48:23 +0000 Subject: [PATCH 27/70] PEP 545: Use new translation dashboard link (#4706) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0545.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/peps/pep-0545.rst b/peps/pep-0545.rst index 1a5204ca690..633d497b44a 100644 --- a/peps/pep-0545.rst +++ b/peps/pep-0545.rst @@ -322,9 +322,11 @@ and http://zanata.org/. python-docs-translations '''''''''''''''''''''''' -The `python-docs-translations GitHub organization `_ -is home to several useful translation tools such as the translations -`dashboard `_. +The `python-docs-translations GitHub organization `__ +is home to several useful translation tools including +`translations.python.org `__ +(`python-docs-translations/dashboard +`__). Documentation Contribution Agreement From 9590e948d2f965b2e0f27e5d217294a50b603a50 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 14 Nov 2025 14:21:52 -0500 Subject: [PATCH 28/70] PEP 808: Partially Dynamic Metadata (#4598) Co-authored-by: Paul Moore Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Co-authored-by: Cristian Le --- .github/CODEOWNERS | 2 +- peps/pep-0808.rst | 393 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 peps/pep-0808.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a003f6914b4..269770261b8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -683,7 +683,7 @@ peps/pep-0804.rst @pradyunsg # ... peps/pep-0806.rst @JelleZijlstra peps/pep-0807.rst @dstufft -# ... +peps/pep-0808.rst @FFY00 peps/pep-0809.rst @zooba peps/pep-0810.rst @pablogsal @DinoV @Yhg1s peps/pep-0811.rst @sethmlarson @gpshead diff --git a/peps/pep-0808.rst b/peps/pep-0808.rst new file mode 100644 index 00000000000..d422d65f279 --- /dev/null +++ b/peps/pep-0808.rst @@ -0,0 +1,393 @@ +PEP: 808 +Title: Partially dynamic project metadata +Author: Henry Schreiner , + Cristian Le +Sponsor: Filipe Laíns +PEP-Delegate: Paul Moore +Status: Draft +Type: Standards Track +Topic: Packaging +Created: 19-Sep-2025 +Post-History: `17-Apr-2025 `__ + + +Abstract +======== + +This PEP relaxes the constraint on dynamic metadata listed in the ``[project]`` +section in ``pyproject.toml`` to allow the static portion of mixed metadata to +be defined in the normal location if the field is a table or array by having +the dynamic fields extend the static ones. + +This allows users to opt into allowing a backend to extend metadata while still +keeping the static portions of the metadata defined in the standard location in +``pyproject.toml``, and allows inspection tools to still be able to process the +static portions of the metadata. + + +Motivation +========== + +In the core metadata specification originally set out in :pep:`621`, metadata +can be specified in three ways. First, it can be listed in the ``[project]`` +table. This makes it statically inferable, meaning any tool (not just the +build backend) can reliably compute the value. Second, a field can be listed in +the ``project.dynamic`` list, which allows the build backend to compute the +value. Finally, a value could be missing from both the ``project`` table and +the ``project.dynamic`` list, in which case the matching metadata is guaranteed +to be empty. + +This system provided two important benefits to Python packaging. A standard +specification that all major backends have now adopted makes teaching much +easier; a single tutorial is now sufficient to cover the metadata portion of +configuring any backend. Users can now switch from a general purpose backend to +a specialized backend without changing their static metadata. Tooling like +schema validation tools can verify and catch configuration mistakes. + +The second benefit is improved support for static tools that read the source +files looking for metadata. This is useful for dependency chain analysis, such +as creating "used by" and "uses" graphs. It is used for code quality tooling to +detect the minimum supported version of Python. It is used by cibuildwheel_ to +automatically avoid building wheels that are not supported. It is not used, +however, to avoid wheel builds when the SDist is available; that was addressed +by METADATA 2.2, which a ``Dynamic`` field in the SDist metadata that lets a +tool know if the metadata can change when making a wheel - this is an easy +mistake to make due to the similarity of the names. + +Due to the rapidly increasing popularity of the project table, support from all +major backends, and a rise of backends supporting complex compiled extensions, +an issue with the restrictions applied in :pep:`621` is becoming more apparent. +In PEP 621, the metadata choice is all-or-nothing; metadata must be completely +static, or listed in the dynamic field and completely absent from the static +definition. For the most common use cases, this is fine; there is little +benefit to set the ``version`` statically if you are going to override it +dynamically. If you are using a custom README processor to filter or modify the +README for proper display, it's not a big deal to have to specify the +configuration in a custom ``tool.*`` section. But there is a specific class of +cases where the all-or-nothing approach is problematic: lists of items where +the backend needs to add items are currently forced to be fully dynamically +specified (that is, in a backend-specific configuration location). This causes +both of the original benefits (standard location and static tooling support) to +be lost. + +Rationale +========= + + +:pep:`621` includes the following statement: + + In an earlier version of this PEP, tools were allowed to extend data for + fields. For instance, build back-ends could take the version number and add + a local version for when they built the wheel. Tools could also add more + trove classifiers for things like the license or supported Python versions. + + In the end, though, it was thought better to start out stricter and + contemplate loosening how static the data could be considered based on + real-world usage. + +In this PEP, we are proposing a limited and explicit loosening of the +restrictions on the ``[project]`` table and ``project.dynamic`` list. + +Every list and every table with arbitrary keys will now be allowed to be +specified both statically, in the ``[project]`` table, and in the +``project.dynamic`` list. If it is present in both places, the build backend +can extend list items and add new keys, but not modify existing list items or +strings. + + +Use Cases +--------- + +There is an entire class of metadata fields where advanced use cases +would really benefit from a relaxation of this rule. Here are some use +cases that have come up: + +- Pinning dependency requirements when building the wheel. When building + PyTorch_ extensions, for example, the version you build with adds a constraint + to the wheel you create that is not present with the SDist. +- Generating extra scripts from a build system (this is a currently proposed in + scikit-build-core_). +- Adding entry points dynamically (validate-pyproject-schema-store_ could have + used this to generate an entry point for each schema present in the package.) +- Adding dependencies or optional dependencies based on configuration (such as + making an all dependency, or reading dependencies from dependency-groups, for + example). Adding constraints can also be useful; pybind11_ uses adds a ``global`` + extra that pins ``pybind11-global==``, as both packages are in the + same repository and released in sync. toga_ is a collection of packages that + currently is unable to set any static dependencies due to the same sort of + pinning problem. +- Adding classifiers; some backends can compute classifiers from other places + and inject them (Poetry_ being the best known example). +- Adding license files to the wheel based on what libraries are linked in (this + is an active discussion in followup to :pep:`639`). +- Adding SBom's when building - :pep:`770` had to remove the ``pyproject.toml`` + field specifically because you *want* the build tool to add these, so the + ``[project]`` table setting would be useless, you'd almost never be able to + use it. +- Adding generated modules to ``import-names`` or ``import-namespaces``. + +All of these use cases have a similar feature: they are adding something +dynamically to a fixed list (possibly a narrower pin for the dependency case). + +You can implement these today, but it requires providing a completely separate +configuration location for the non-extended portion, and static analysis tools +lose the ability to detect anything. Since the current solution is to move all +the metadata out of the standard field, this proposal will increase the +availability of metadata for static tooling. + + +Example: pinning +---------------- + +For example, let's say you want to allow an imaginary build backend +(``my-build-backend``) to pin to the supported build of PyTorch_. Before this +PEP, you could do this: + +.. code-block:: toml + + [project] + dynamic = ["dependencies"] + + [tool.my-build-backend] + original-dependencies = ["torch", "packaging"] + pin-to-build-versions = ["torch=={exact}"] + +Which would effectively expand to the following SDist metadata: + +.. code-block:: text + + Dynamic: Requires-Dist + Requires-Dist: packaging + Requires-Dist: torch + +Which would then could make a wheel with this: + +.. code-block:: text + + Requires-Dist: packaging + Requires-Dist: torch + Requires-Dist: torch==2.8.0 + +Static tooling no longer can tell that ``torch`` and ``packaging`` are runtime +dependencies, and the build backend had to duplicate the dependency table, +making it harder for users to learn and read; the standardized place proposed +by :pep:`621` and adopted by all major build backends is lost. + +With this PEP, this could now be specified like this: + +.. code-block:: toml + + [project] + dependencies = ["torch", "packaging"] + dynamic = ["dependencies"] + + [tool.my-build-backend] + pin-to-build-versions = ["torch=={exact}"] + +Static tooling can now detect the static dependencies, and the build backend no +longer needs to create and document a new location for the standard +``project.dependencies`` field (the ``original-dependencies`` field above, for +example). + + + +Future Updates +-------------- + +Loosening this rule to allow purely additive metadata should address many of +the use cases that have been seen in practice. If further changes are needed, +this can be revisited in a future PEP; this PEP neither recommends or precludes +future updates like this. + +Terminology +=========== + +The keywords "MUST", "MUST NOT", "REQUIRED", +"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" +in this document are to be interpreted as described in :rfc:`2119`. + +Specification +============= + +Any field that is comprised of a list or a table with arbitrary entries will +now be allowed to be present in both the ``[project]`` table and the +``project.dynamic`` list. If a field is present in both places, then the build +backend is allowed to insert entries into the list or table, but not remove +entries, reorder entries, or modify the entries. Tables of arrays allow adding +a new table entry or extending an existing array according to the rules above. + +The fields that are arrays or tables with arbitrary entries are: + +* ``authors``, ``maintainers``: New author tables can be added to the list. + Existing authors cannot be modified (list of tables with pre-defined keys). +* ``classifiers``: Classifiers can be added to the list. +* ``dependencies``: New dependencies can be added, including more tightly + constrained existing dependencies. +* ``entry-points``: Entry points can be added, to either new or existing + groups. Existing entry points cannot be changed or removed. +* ``scripts``, ``gui-scripts``: New scripts can be added. Existing ones cannot + be changed or removed. +* ``keywords``: Keywords can be added to the list. +* ``license-files``: Files can be added to the list. +* ``optional-dependencies``: A new extra or new items can be added to a + existing extra. +* ``urls``: New urls can be added. Existing ones cannot be changed or removed. +* ``import-names``, ``import-namespaces``: New import names or namespaces can + be added. Existing ones cannot be modified or removed. + +To add items, users must opt-in by listing the field in ``dynamic``; without +that, the metadata continues to be entirely static. + +A backend SHOULD error if a field is specified and it does not support +extending that field, to protect against possible user error. We recommend +being as strict as possible to avoid unnecessary entries in the ``dynamic`` +list. + +Static analysis tools, when detecting a field is both specified and in the +``project.dynamic`` array, SHOULD assume the field is incomplete, allowing for +new entries to be present when the package is built. + +The ``Dynamic`` field, as specified in :pep:`643`, is unaffected by this PEP, +and backends can continue to fill it as they chose. However, a backend MUST +ensure that both the SDist and the wheel metadata include the static metadata +portion of the project table. + +Reference Implementation +======================== + +The choice to support dynamic metadata for each field is already left up to +backends, and this PEP simply relaxes restrictions on what a backend is allowed +to do with dynamic metadata. + +The pyproject-metadata_ project, which is used by +several build backends, will need to modify the correctness check to account +for the possible extensions; this is in `a draft PR `__. + +The dynamic-metadata_ project, which provides a plugin +system that backends can use to share dynamic metadata plugins, was designed to +allow this possibility, and a similar PR to the one above will allow additive +metadata. + +Backwards Compatibility +======================= + +This does not affect any existing ``pyproject.toml``'s, since this was strictly +not allowed before this PEP. + +When users adopt this in a ``pyproject.toml``, the backend must support it; an +error will be correctly generated if it doesn't following the previous +standard. Frontends were never required to throw an error, though some +frontends may need to be updated to benefit from the partially static metadata. +Some frontends and other tooling may need updating, such as schema +validation, just like other ``pyproject.toml`` PEPs. + +Using metadata from SDists or wheels is unaffected. The METADATA version does +not need to be incremented. + +Security Implications +===================== + +There are no security concerns that are not already present, as this just adds +a static component to existing dynamic metadata support. + +How to Teach This +================= + +The current guides that state metadata must not be listed in both ``[project]`` +and ``project.dynamic`` can be updated to say that some fields can be extended +by ``project.dynamic``. Since dynamic metadata is already an advanced concept, +this will likely not affect most existing tutorial material aimed at +introductory packaging. + +The ``pyproject.toml`` `specification `__ will be updated to +include the behavior of fields when specified and also listed in the dynamic +field. + +It should also be noted that specifying something in dynamic will require any +tool that requires the full metadata to invoke the backend even if it is +partially statically specified, so it should not be used unless necessary. + + +Rejected Ideas +============== + +Special case some fields without adding dynamic +----------------------------------------------- + +This has come up specifically for the pinning build dependency use case, but +could also be applied to more of the use cases listed. This would not cover all +the use cases seen, though, and an explicit, opt-in approach is better for +static tooling. + + +Include string fields +--------------------- + +Some string fields could also be extended. Most notably, the ``license`` field +would benefit from being extendable, and due to the semantics of SPDX +expressions, extension could be defined through ``AND``. This was not added to +this PEP because that would require individual fields to have custom semantics. + +The other string fields, namely ``version`` and ``requires-python`` (``name`` +is not allowed to be specified dynamically), have less reason to be extended. +Fixed key tables, like the deprecated ``license.text``/``license.file`` or +``readme.text``/``readme.file`` also have no clear benefit being partially +dynamic. + + +Fully remove restrictions on backends +------------------------------------- + +Another option would be to simply allow backends to do whatever they wanted if +a field is statically defined and in the dynamic array. This would sacrifice +the ability for static tooling to infer anything about the field, and could +potentially confuse users by allowing the backend to ignore or change what they +entered. This is not worse than the status quo for static tooling and dynamic +metadata, but the current proposal improves the ability of static tooling to +infer some things about dynamic fields. Knowing some of the dependencies is +better for most applications than not knowing anything about the dependencies, +for example. + +Allow simplifications +--------------------- + +An earlier draft of this PEP had a clause allowing backends to simplify some +types of fields; most notably dependency specifiers would have allowed +"tightening", such as ``torch`` being replaced by ``torch>=1.2``, for example. +. This was removed due to it being impossible to ensure a variation will +resolve identically on all resolvers within the current specification, and to +simplify the contract with backends. Any other simplifications would be purely +cosmetic, and so were left out. The order in the current PEP is now required to +match the original static metadata, with the dynamic portion only allowing +insertions. + + +Add a general mechanism to specify dynamic-metadata +--------------------------------------------------- + +This PEP does not cover methods to specify dynamic metadata; that continues to +be entirely up to the backend. An earlier draft proposal did this, but it was +deemed better to develop that as a library (dynamic-metadata_, for the curious) +instead. This may be revisited in the future. + +References +========== + +.. _cibuildwheel: https://cibuildwheel.pypa.io +.. _pyprojectspec: https://packaging.python.org/en/latest/specifications/pyproject-toml +.. _pyproject-metadata: https://github.com/pypa/pyproject-metadata +.. _pyprojectmetadatapr: https://github.com/pypa/pyproject-metadata/pull/241 +.. _dynamic-metadata: https://github.com/scikit-build/dynamic-metadata +.. _PyTorch: https://pytorch.org/ +.. _scikit-build-core: https://github.com/scikit-build/scikit-build-core +.. _validate-pyproject-schema-store: https://pypi.org/project/validate-pyproject-schema-store/ +.. _pybind11: https://github.com/pybind/pybind11 +.. _Poetry: https://python-poetry.org/ +.. _setuptools: https://github.com/pypa/setuptools +.. _toga: https://github.com/beeware/toga + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From 4eded5f6e572479af0947cc85ac1edcc1690c1bb Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Fri, 14 Nov 2025 14:39:46 -0600 Subject: [PATCH 29/70] PEP 811: Adopt similar nomination process to core team (#4686) --- peps/pep-0811.rst | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/peps/pep-0811.rst b/peps/pep-0811.rst index 88ffee687e3..f832d2e71c0 100644 --- a/peps/pep-0811.rst +++ b/peps/pep-0811.rst @@ -59,10 +59,9 @@ Onboarding new contributors to the PSRT Unlike most open-source contributions, the work of the PSRT doesn't happen in the open. Instead, most work occurs privately by a trusted group to limit -access to undisclosed -vulnerability reports. Given the sensitive nature of this work, it appears opaque from the outside, and -it's difficult to get started as a newcomer and to understand the -expectations of the group. +access to undisclosed vulnerability reports. Given the sensitive nature of this +work, it appears opaque from the outside, and it's difficult to get started as a +newcomer and to understand the expectations of the group. In practice this has meant that relatively few new members join the PSRT, which over time could negatively impact the group's ability to triage reports @@ -110,8 +109,7 @@ decide who should be admitted, but there must be a system responsible for evaluating PSRT membership. This PEP proposes limiting PSRT membership only to active coordinators -of security vulnerabilities that are core developers or triagers, -members involved in oversight (Steering Council), +of security vulnerabilities, members involved in oversight (Steering Council), and members who need information about security releases (Release Managers). Activity is recommended as the metric for membership to avoid adding additional @@ -154,12 +152,14 @@ Specification PSRT Membership Policy ---------------------- -The Python Steering Council may add or remove members and admins of the PSRT. -New PSRT members must be core team members, triagers, or PSF staff, -and must be `proposed to and accepted`_ by the Steering Council. +The PSRT will run nominations `similar to core team nominations`_, where +a nomination of a new member is brought to the PSRT by an existing PSRT member +and then that nomination is voted on by existing PSRT members. New members +are expected to be drawn from core developers, triagers, or PSF staff. +It is granted by receiving at least two-thirds positive votes from a vote of +existing PSRT members that is open for one week and is not vetoed by the +Steering Council. -Once the Steering Council votes on a membership change to the PSRT then -PSRT admins will enact the change. A list of PSRT members will be published publicly and kept up-to-date by PSRT admins. @@ -167,6 +167,7 @@ Once per year the Steering Council will receive a report of inactive members of the PSRT with the recommendation to remove the inactive users from the PSRT. "Inactive" is defined here as a member who hasn't coordinated or commented on a vulnerability report in the past year since the last report was generated. +The Steering Council may remove members of the PSRT with a simple vote. Members of the PSRT who are a Release Manager or Steering Council member may remain in the PSRT regardless of inactivity in vulnerability reports. @@ -176,11 +177,7 @@ in the past year and without an exemption for minimum activity (Steering Council Release Managers) prior to publication of this PEP. At the time of writing, this would reduce the PSRT membership size to ~15 members from ~30. -This PEP also proposes not removing members of the PSRT who are active but -not yet core team members or triagers, allowing them to be "legacied" in -to the new PSRT Membership Policy. - -.. _proposed to and accepted: https://github.com/python/steering-council/ +.. _similar to core team nominations: https://devguide.python.org/core-team/join-team/ PSRT Admins ~~~~~~~~~~~ @@ -236,7 +233,7 @@ following additional responsibilities: * Managing the GitHub team, mailing list, Discord channel, and other PSRT venues to ensure they are synchronized with the canonical list of - PSRT members determined by the Steering Council. + PSRT members. * On a yearly basis, providing the Steering Council with a report including a list of inactive PSRT members. From 6da8478c6b81fd1f68ecb36340f59b38d4b4eea1 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 14 Nov 2025 16:33:34 -0500 Subject: [PATCH 30/70] PEP 808: add discussion link (#4707) --- peps/pep-0808.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/peps/pep-0808.rst b/peps/pep-0808.rst index d422d65f279..79cb422f327 100644 --- a/peps/pep-0808.rst +++ b/peps/pep-0808.rst @@ -4,11 +4,13 @@ Author: Henry Schreiner , Cristian Le Sponsor: Filipe Laíns PEP-Delegate: Paul Moore +Discussions-To: https://discuss.python.org/t/104883 Status: Draft Type: Standards Track Topic: Packaging Created: 19-Sep-2025 -Post-History: `17-Apr-2025 `__ +Post-History: `17-Apr-2025 `__, + `14-Nov-2025 `__ Abstract From daa024d807e6fe58131c590888a7641c44f120d2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 15 Nov 2025 04:53:54 +0100 Subject: [PATCH 31/70] PEP 814: Minor tweaks (#4703) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-0814.rst | 89 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 10 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 36cfc0a7c85..6ca46195a05 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -6,6 +6,8 @@ Status: Draft Type: Standards Track Created: 12-Nov-2025 Python-Version: 3.15 +Post-History: `13-Nov-2025 `__ + Abstract ======== @@ -75,6 +77,15 @@ Construction - another ``frozendict``, - or an iterable of key/value tuples. +* ``frozendict(collection, **kwargs)`` combines the two previous + constructions. + +Keys must be hashable and so immutable, but values can be mutable. +Using immutable values creates a hashtable ``frozendict``. + +Creating a ``frozendict`` from a ``dict``, ``frozendict(dict)``, has a +complexity of *O*\ (*n*): items are copied (shallow copy). + The insertion order is preserved. @@ -92,8 +103,8 @@ protocol, so all expected methods of iteration are supported:: Iterating on ``frozendict``, as on ``dict``, uses the insertion order. -Hashing -------- +Hashing and Comparison +---------------------- ``frozendict`` instances can be hashable just like tuple objects:: @@ -114,6 +125,64 @@ Equality test does not depend on the items' order either. Example:: >>> a == b True +It's possible to compare ``frozendict`` to ``dict``. Example:: + + >>> frozendict(x=1, y=2) == dict(x=1, y=2) + True + + +Union operators +--------------- + +It's possible to join two ``frozendict``, or a ``frozendict`` with a +``dict``, with the merge (``|``) operator. Example:: + + >>> frozendict(x=1) | frozendict(y=1) + frozendict({'x': 1, 'y': 1}) + >>> frozendict(x=1) | dict(y=1) + frozendict({'x': 1, 'y': 1}) + +If some keys are in common, the values of the right operand are taken:: + + >>> frozendict(x=1, y=2) | frozendict(y=5) + frozendict({'x': 1, 'y': 5}) + +The update operator ``|=`` does not modify a ``frozendict`` in-place, but +creates a new ``frozendict``:: + + >>> d = frozendict(x=1) + >>> copy = d + >>> d |= frozendict(y=2) + >>> d + frozendict({'x': 1, 'y': 2}) + >>> copy # left unchanged + frozendict({'x': 1}) + +See also :pep:`584` "Add Union Operators To dict". + + +Copy +---- + +``frozencopy.copy()`` returns a shallow copy. In CPython, it simply +returns the same ``frozendict`` (new reference). + +Use ``copy.deepcopy()`` to get a deep copy. + +Example:: + + >>> import copy + >>> d = frozendict(mutable=[]) + >>> shallow_copy = d.copy() + >>> deep_copy = copy.deepcopy(d) + >>> d['mutable'].append('modified') + >>> d + frozendict({'mutable': ['modified']}) + >>> shallow_copy # modified! + frozendict({'mutable': ['modified']}) + >>> deep_copy # unchanged + frozendict({'mutable': []}) + Typing ------ @@ -138,10 +207,12 @@ C API Add the following APIs: -* ``PyFrozenDict_Type`` -* ``PyFrozenDict_New(collection)`` function +* ``PyAnyDict_Check(op)`` macro +* ``PyAnyDict_CheckExact(op)`` macro * ``PyFrozenDict_Check()`` macro * ``PyFrozenDict_CheckExact()`` macro +* ``PyFrozenDict_New(collection)`` function +* ``PyFrozenDict_Type`` Even if ``frozendict`` is not a ``dict`` subclass, it can be used with ``PyDict_GetItemRef()`` and similar "PyDict_Get" functions. @@ -269,12 +340,10 @@ Relationship to PEP 603 frozenmap * ``excluding(key)`` * ``union(mapping=None, **kw)`` -========== ============== ============== -Complexity ``frozenmap`` ``frozendict`` -========== ============== ============== -Lookup *O*\ (log *n*) *O*\ (1) -Copy *O*\ (1) *O*\ (*n*) -========== ============== ============== + These methods to mutate a ``frozenmap`` have a complexity of *O*\ (1). + +* A mapping lookup (``mapping[key]``) has a complexity of *O*\ (log *n*) + with ``frozenmap`` and a complexity of *O*\ (1) with ``frozendict``. Reference Implementation From edbdf98411061b065c77ccf32552c6f4db45be29 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 17 Nov 2025 15:46:15 +0100 Subject: [PATCH 32/70] PEP 814: Fix typos (#4708) --- peps/pep-0814.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 6ca46195a05..ce8470f0aa6 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -80,8 +80,8 @@ Construction * ``frozendict(collection, **kwargs)`` combines the two previous constructions. -Keys must be hashable and so immutable, but values can be mutable. -Using immutable values creates a hashtable ``frozendict``. +Keys must be hashable and therefore immutable, but values can be mutable. +Using immutable values creates a hashable ``frozendict``. Creating a ``frozendict`` from a ``dict``, ``frozendict(dict)``, has a complexity of *O*\ (*n*): items are copied (shallow copy). @@ -164,7 +164,7 @@ See also :pep:`584` "Add Union Operators To dict". Copy ---- -``frozencopy.copy()`` returns a shallow copy. In CPython, it simply +``frozendict.copy()`` returns a shallow copy. In CPython, it simply returns the same ``frozendict`` (new reference). Use ``copy.deepcopy()`` to get a deep copy. From 93f3c3071bec3b83eaa602a5bcd9e7d9786c8d2b Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Mon, 17 Nov 2025 14:48:57 -0500 Subject: [PATCH 33/70] PEP 808: backwards compatibility example update (#4710) --- peps/pep-0808.rst | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/peps/pep-0808.rst b/peps/pep-0808.rst index 79cb422f327..fb4e0ed4725 100644 --- a/peps/pep-0808.rst +++ b/peps/pep-0808.rst @@ -273,6 +273,9 @@ metadata. Backwards Compatibility ======================= +Using metadata from SDists or wheels is unaffected. The METADATA version does +not need to be incremented. + This does not affect any existing ``pyproject.toml``'s, since this was strictly not allowed before this PEP. @@ -283,8 +286,27 @@ frontends may need to be updated to benefit from the partially static metadata. Some frontends and other tooling may need updating, such as schema validation, just like other ``pyproject.toml`` PEPs. -Using metadata from SDists or wheels is unaffected. The METADATA version does -not need to be incremented. +Static analysis tools may require updating to handle this change. Tools should +check the dynamic table first, like this: + +.. code-block:: + + match pyproject["project"]: + # New in PEP 808 + case {my.key: value, "dynamic": dyn} if my.key in dyn: + print(f"Partial {my.key}: {value}") + case {"dynamic": dyn} if my.key in dyn: + print(f"Fully dynamic {my.key}") + case {my.key: value}: + print(f"Fully static {my.key}: {value}") + case _: + print(f"No metadata for {my.key}") + +Before this PEP, tools could reverse the order of the dynamic and static +blocks, assuming that an entry in the project table meant it could not be +dynamic. If they do this, they will now incorrectly assume they have all the +metadata for a field, when they in fact only have part of it. + Security Implications ===================== From 63d3a9c2c0152bcf10324ee3dd837970a9dc3dce Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 18 Nov 2025 13:42:31 -0500 Subject: [PATCH 34/70] PEP 808: how to teach update (#4711) Co-authored-by: Carol Willing --- peps/pep-0808.rst | 98 +++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/peps/pep-0808.rst b/peps/pep-0808.rst index fb4e0ed4725..8294a8dbf15 100644 --- a/peps/pep-0808.rst +++ b/peps/pep-0808.rst @@ -17,9 +17,9 @@ Abstract ======== This PEP relaxes the constraint on dynamic metadata listed in the ``[project]`` -section in ``pyproject.toml`` to allow the static portion of mixed metadata to -be defined in the normal location if the field is a table or array by having -the dynamic fields extend the static ones. +section in ``pyproject.toml``. It is now permitted to define a static portion +of a dynamic metadata field in the ``[project]`` table as long as the field is +a table or array. This allows users to opt into allowing a backend to extend metadata while still keeping the static portions of the metadata defined in the standard location in @@ -52,9 +52,9 @@ as creating "used by" and "uses" graphs. It is used for code quality tooling to detect the minimum supported version of Python. It is used by cibuildwheel_ to automatically avoid building wheels that are not supported. It is not used, however, to avoid wheel builds when the SDist is available; that was addressed -by METADATA 2.2, which a ``Dynamic`` field in the SDist metadata that lets a -tool know if the metadata can change when making a wheel - this is an easy -mistake to make due to the similarity of the names. +by METADATA 2.2, which added a ``Dynamic`` field in the SDist metadata that +lets a tool know if the metadata can change when making a wheel - this is an +easy mistake to make due to the similarity of the names. Due to the rapidly increasing popularity of the project table, support from all major backends, and a rise of backends supporting complex compiled extensions, @@ -65,8 +65,8 @@ definition. For the most common use cases, this is fine; there is little benefit to set the ``version`` statically if you are going to override it dynamically. If you are using a custom README processor to filter or modify the README for proper display, it's not a big deal to have to specify the -configuration in a custom ``tool.*`` section. But there is a specific class of -cases where the all-or-nothing approach is problematic: lists of items where +configuration in a custom ``[tool.*]`` section. But there is a specific class +of cases where the all-or-nothing approach is problematic: lists of items where the backend needs to add items are currently forced to be fully dynamically specified (that is, in a backend-specific configuration location). This causes both of the original benefits (standard location and static tooling support) to @@ -93,8 +93,7 @@ restrictions on the ``[project]`` table and ``project.dynamic`` list. Every list and every table with arbitrary keys will now be allowed to be specified both statically, in the ``[project]`` table, and in the ``project.dynamic`` list. If it is present in both places, the build backend -can extend list items and add new keys, but not modify existing list items or -strings. +can extend list items and add new keys, but not modify existing entries. Use Cases @@ -107,26 +106,27 @@ cases that have come up: - Pinning dependency requirements when building the wheel. When building PyTorch_ extensions, for example, the version you build with adds a constraint to the wheel you create that is not present with the SDist. -- Generating extra scripts from a build system (this is a currently proposed in +- Generating extra scripts from a build system (this is currently proposed in scikit-build-core_). - Adding entry points dynamically (validate-pyproject-schema-store_ could have - used this to generate an entry point for each schema present in the package.) + used this to generate an entry point for each schema present in the package). - Adding dependencies or optional dependencies based on configuration (such as making an all dependency, or reading dependencies from dependency-groups, for - example). Adding constraints can also be useful; pybind11_ uses adds a ``global`` + example). Adding constraints can also be useful; pybind11_ uses a ``global`` extra that pins ``pybind11-global==``, as both packages are in the same repository and released in sync. toga_ is a collection of packages that - currently is unable to set any static dependencies due to the same sort of + is currently unable to set any static dependencies due to the same sort of pinning problem. - Adding classifiers; some backends can compute classifiers from other places and inject them (Poetry_ being the best known example). - Adding license files to the wheel based on what libraries are linked in (this - is an active discussion in followup to :pep:`639`). -- Adding SBom's when building - :pep:`770` had to remove the ``pyproject.toml`` + is an active discussion in follow-up to :pep:`639`). +- Adding SBOMs when building - :pep:`770` had to remove the ``pyproject.toml`` field specifically because you *want* the build tool to add these, so the - ``[project]`` table setting would be useless, you'd almost never be able to - use it. -- Adding generated modules to ``import-names`` or ``import-namespaces``. + ``[project]`` table setting would be useless, you would almost never be able + to use it. +- Adding generated modules to ``import-names`` or ``import-namespaces`` is + another example. All of these use cases have a similar feature: they are adding something dynamically to a fixed list (possibly a narrower pin for the dependency case). @@ -141,7 +141,7 @@ availability of metadata for static tooling. Example: pinning ---------------- -For example, let's say you want to allow an imaginary build backend +For example, say you want to allow an imaginary build backend (``my-build-backend``) to pin to the supported build of PyTorch_. Before this PEP, you could do this: @@ -162,7 +162,7 @@ Which would effectively expand to the following SDist metadata: Requires-Dist: packaging Requires-Dist: torch -Which would then could make a wheel with this: +Which could then make a wheel with this: .. code-block:: text @@ -170,8 +170,8 @@ Which would then could make a wheel with this: Requires-Dist: torch Requires-Dist: torch==2.8.0 -Static tooling no longer can tell that ``torch`` and ``packaging`` are runtime -dependencies, and the build backend had to duplicate the dependency table, +Static tooling can no longer tell that ``torch`` and ``packaging`` are runtime +dependencies, and the build backend has to duplicate the dependency table, making it harder for users to learn and read; the standardized place proposed by :pep:`621` and adopted by all major build backends is lost. @@ -198,8 +198,8 @@ Future Updates Loosening this rule to allow purely additive metadata should address many of the use cases that have been seen in practice. If further changes are needed, -this can be revisited in a future PEP; this PEP neither recommends or precludes -future updates like this. +this can be revisited in a future PEP; this PEP neither recommends nor +precludes future updates like this. Terminology =========== @@ -231,13 +231,13 @@ The fields that are arrays or tables with arbitrary entries are: be changed or removed. * ``keywords``: Keywords can be added to the list. * ``license-files``: Files can be added to the list. -* ``optional-dependencies``: A new extra or new items can be added to a +* ``optional-dependencies``: A new extra or new items can be added to an existing extra. * ``urls``: New urls can be added. Existing ones cannot be changed or removed. * ``import-names``, ``import-namespaces``: New import names or namespaces can be added. Existing ones cannot be modified or removed. -To add items, users must opt-in by listing the field in ``dynamic``; without +To add items, users must opt in by listing the field in ``dynamic``; without that, the metadata continues to be entirely static. A backend SHOULD error if a field is specified and it does not support @@ -250,7 +250,7 @@ Static analysis tools, when detecting a field is both specified and in the new entries to be present when the package is built. The ``Dynamic`` field, as specified in :pep:`643`, is unaffected by this PEP, -and backends can continue to fill it as they chose. However, a backend MUST +and backends can continue to fill it as they choose. However, a backend MUST ensure that both the SDist and the wheel metadata include the static metadata portion of the project table. @@ -263,7 +263,7 @@ to do with dynamic metadata. The pyproject-metadata_ project, which is used by several build backends, will need to modify the correctness check to account -for the possible extensions; this is in `a draft PR `__. +for the possible extensions; this is in `a draft PR `__. The dynamic-metadata_ project, which provides a plugin system that backends can use to share dynamic metadata plugins, was designed to @@ -276,15 +276,15 @@ Backwards Compatibility Using metadata from SDists or wheels is unaffected. The METADATA version does not need to be incremented. -This does not affect any existing ``pyproject.toml``'s, since this was strictly -not allowed before this PEP. +This does not affect any existing ``pyproject.toml`` files, since this was +strictly not allowed before this PEP. When users adopt this in a ``pyproject.toml``, the backend must support it; an -error will be correctly generated if it doesn't following the previous +error will be correctly generated if it doesn't, following the previous standard. Frontends were never required to throw an error, though some frontends may need to be updated to benefit from the partially static metadata. Some frontends and other tooling may need updating, such as schema -validation, just like other ``pyproject.toml`` PEPs. +validators, just like other ``pyproject.toml`` PEPs. Static analysis tools may require updating to handle this change. Tools should check the dynamic table first, like this: @@ -317,19 +317,26 @@ a static component to existing dynamic metadata support. How to Teach This ================= +If you currently have dynamic metadata, but some list or table entries are +known statically, you can now make that explicit by adding the static entry in +the ``[project]`` table, while also keeping the entry in the +``project.dynamic`` list to allow the dynamic portion to be added by your build +backend. + The current guides that state metadata must not be listed in both ``[project]`` -and ``project.dynamic`` can be updated to say that some fields can be extended -by ``project.dynamic``. Since dynamic metadata is already an advanced concept, -this will likely not affect most existing tutorial material aimed at -introductory packaging. +and ``project.dynamic`` can be updated to say that lists and tables marked with +``project.dynamic`` can still have static entries. Since dynamic metadata is +already an advanced concept, this will likely not affect most existing tutorial +material aimed at introductory packaging. The ``pyproject.toml`` `specification `__ will be updated to include the behavior of fields when specified and also listed in the dynamic field. -It should also be noted that specifying something in dynamic will require any -tool that requires the full metadata to invoke the backend even if it is -partially statically specified, so it should not be used unless necessary. +It should also be noted that specifying something in ``dynamic`` will require +any tool that needs the full metadata to invoke the backend even if it is +partially statically specified. So, it should not be used unless necessary, +just like any other dynamic metadata. Rejected Ideas @@ -378,12 +385,11 @@ Allow simplifications An earlier draft of this PEP had a clause allowing backends to simplify some types of fields; most notably dependency specifiers would have allowed "tightening", such as ``torch`` being replaced by ``torch>=1.2``, for example. -. This was removed due to it being impossible to ensure a variation will -resolve identically on all resolvers within the current specification, and to -simplify the contract with backends. Any other simplifications would be purely -cosmetic, and so were left out. The order in the current PEP is now required to -match the original static metadata, with the dynamic portion only allowing -insertions. +This was removed due to it being impossible to ensure a variation will resolve +identically on all resolvers within the current specification, and to simplify +the contract with backends. Any other simplifications would be purely cosmetic, +and so were left out. The order in the current PEP is now required to match the +original static metadata, with the dynamic portion only allowing insertions. Add a general mechanism to specify dynamic-metadata From e5904d87bd312c214c3c91f1991d9f2fbcdb034b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 20 Nov 2025 01:04:18 +0200 Subject: [PATCH 35/70] PEP 790: 3.15.0a2 was released on 2025-11-19 (#4712) --- peps/pep-0790.rst | 2 +- release_management/python-releases.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0790.rst b/peps/pep-0790.rst index 74807181da6..210f0a92c2a 100644 --- a/peps/pep-0790.rst +++ b/peps/pep-0790.rst @@ -37,10 +37,10 @@ Actual: - 3.15 development begins: Wednesday, 2025-05-07 - 3.15.0 alpha 1: Tuesday, 2025-10-14 +- 3.15.0 alpha 2: Wednesday, 2025-11-19 Expected: -- 3.15.0 alpha 2: Tuesday, 2025-11-18 - 3.15.0 alpha 3: Tuesday, 2025-12-16 - 3.15.0 alpha 4: Tuesday, 2026-01-13 - 3.15.0 alpha 5: Tuesday, 2026-02-10 diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 8d572b6f506..3a1bad73ca1 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3532,8 +3532,8 @@ date = 2025-10-14 [[release."3.15"]] stage = "3.15.0 alpha 2" -state = "expected" -date = 2025-11-18 +state = "actual" +date = 2025-11-19 [[release."3.15"]] stage = "3.15.0 alpha 3" From 2fe508d6060cc8bbf233c475c52916ece9f51ef7 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 20 Nov 2025 19:04:47 +0200 Subject: [PATCH 36/70] PEP 693: Generate security releases from TOML (#4713) --- peps/pep-0693.rst | 4 ++++ release_management/python-releases.toml | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/peps/pep-0693.rst b/peps/pep-0693.rst index 39fed40ea6d..8ac0d92fe9f 100644 --- a/peps/pep-0693.rst +++ b/peps/pep-0693.rst @@ -82,9 +82,13 @@ Source-only security fix releases Provided irregularly on an as-needed basis until October 2028. +.. release schedule: security + - 3.12.11: Tuesday, 2025-06-03 - 3.12.12: Thursday, 2025-10-09 +.. release schedule: ends + 3.12 Lifespan ------------- diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 3a1bad73ca1..df750c4b0c9 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3205,6 +3205,11 @@ stage = "3.12.11" state = "actual" date = 2025-06-03 +[[release."3.12"]] +stage = "3.12.12" +state = "actual" +date = 2025-10-09 + # -- Python 3.13 -------------------------------------------------------------- [metadata."3.13"] From 8a00411879e2fd08c0a918356604de40d6a41867 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 21 Nov 2025 13:02:30 +0100 Subject: [PATCH 37/70] PEP 814: Deferred Ideas (#4714) --- peps/pep-0814.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index ce8470f0aa6..e6a2ad7d363 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -389,6 +389,9 @@ If ``frozendict`` does not inherit from ``dict``, there is no such issue. +Deferred Ideas +============== + New syntax for ``frozendict`` literals -------------------------------------- @@ -396,6 +399,15 @@ Various syntaxes have been proposed to write ``frozendict`` literals. A new syntax can be added later if needed. +Method to convert ``dict`` to ``frozendict`` +-------------------------------------------- + +Different methods have been proposed to convert a mutable ``dict`` to an +immutable ``frozendict`` with *O*\ (1) complexity. + +We consider that such method can be added later if needed, but it +doesn't have to be added right now. + References ========== From 167448055ccb02d089023a5df0140e54ef3722bb Mon Sep 17 00:00:00 2001 From: jaimergp Date: Sun, 23 Nov 2025 19:15:13 +0100 Subject: [PATCH 38/70] 804: Some more details about naming guidelines for RPM and Debian ecosystems (#9) --- peps/pep-0804.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index a9441d5b416..a4e09fe971a 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -71,10 +71,17 @@ Ubuntu and CentOS in a Docker container. RPM-based distributions, like Fedora, can use a `rule-based implementation `__ (``NameConvertor``) in pyp2rpm_. The main rule is that the RPM name for a PyPI package is -``f"python-{pypi_package_name}"``. This seems to work quite well; there are a -few variants like Python version specific names, where the prefix contains the -Python major and minor version numbers (e.g. ``python311-`` instead of -``python-``). +typically ``f"python3-{pypi_package_name}"``. The rare exceptions include packages that +primarily distribute an application, which drop the prefix, (e.g., the Black formatter +is simply ``black``, not ``python3-black``), and variants for different Python versions +(e.g., in RHEL 9 ``setuptools`` can be found as ``python3-setuptools`` for Python 3.9, +but ``python3.11-setuptools`` and ``python3.12-setuptools`` are also available). More details +are available in `Fedora's packaging guidelines for Python `__. + +Debian packages typically follow a ``f"python3-{import_name}"`` naming scheme, with some +exceptions: some sub-communities have an infix (e.g., Django packages go under +``f"python3-django-*"``), and applications are often distributed by their name, with no +``python3-`` prefix. Additional details are available in `Debian's Python Policy `__. Gentoo follows a similar approach to naming Python packages, using the ``dev-python/`` category and some `well-specified rules `__. @@ -134,8 +141,7 @@ instructions for its external build dependencies (C/C++/Fortran compilers, OpenBLAS, pkg-config): - Debian/Ubuntu: ``sudo apt install -y gcc g++ gfortran libopenblas-dev liblapack-dev pkg-config python3-pip python3-dev`` -- Fedora: ``sudo dnf install gcc-gfortran python3-devel openblas-devel lapack-devel pkgconfig`` -- CentOS/RHEL: ``sudo yum install gcc-gfortran python3-devel openblas-devel lapack-devel pkgconfig`` +- Fedora/CentOS/RHEL: ``sudo dnf install gcc-gfortran python3-devel openblas-devel lapack-devel pkgconfig`` - Arch Linux: ``sudo pacman -S gcc-fortran openblas pkgconf`` - Homebrew on macOS: ``brew install gfortran openblas pkg-config`` From da7480b9fb21a05b16f434f9f57cbc385b73e93f Mon Sep 17 00:00:00 2001 From: jaimergp Date: Sun, 23 Nov 2025 19:15:51 +0100 Subject: [PATCH 39/70] 804: Address feedback in `package_managers` (#8) --- peps/pep-0804.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index a4e09fe971a..2f2e7e133bd 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -651,10 +651,10 @@ Each entry in this list is defined as a dictionary with these fields: - ``never``, default in ``query``. - Exactly one of the ``command`` items MUST include a ``{}`` placeholder, - which will be replaced by the mapped package identifier(s). The + Exactly one of the ``command`` items MUST be the ``{}`` placeholder, + which will be replaced by the mapped package specifier(s). The ``install`` command SHOULD support the placeholder being replaced by - multiple identifiers, ``query`` MUST only receive a single identifier + multiple specifiers, ``query`` MUST only receive a single specifier per command. - True * - ``specifier_syntax`` @@ -686,7 +686,7 @@ Each entry in this list is defined as a dictionary with these fields: the value MUST include the ``{version}`` placeholder, and MAY include ``{name}``. - - the key ``{and}`` takes a string used to join multiple version + - the key ``and`` takes a string used to join multiple version constraints in a single token, or ``None`` if only a single constraint can be used per token. In the latter case, the different constraints will be "exploded" into several tokens using the From 74fc8718e2d32d425a14c3b469309ada51253c53 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:18:57 +0000 Subject: [PATCH 40/70] PEP 101: Instruct the Release Manager to change ``python-releases.toml`` (#4697) --- peps/pep-0101.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/peps/pep-0101.rst b/peps/pep-0101.rst index abe5af9afce..de46c473135 100644 --- a/peps/pep-0101.rst +++ b/peps/pep-0101.rst @@ -726,9 +726,7 @@ permissions. - Update the `issue tracker`_ for the new branch: add the new version to the versions list. - - Update the `devguide - `__ - to reflect the new branches and versions. + - Update python-releases.toml_ to reflect the new branches and versions. - Create a PR to update the supported releases table on the `downloads page `__ (see @@ -812,10 +810,10 @@ else does them. Some of those tasks include: * https://www.python.org/downloads/release/python-336/ -- In the `developer's guide - `__, - set the branch status to end-of-life - and update or remove references to the branch elsewhere in the devguide. +- In python-releases.toml_, set the branch status to end-of-life. + +- Update or remove references to the branch in the `developer's guide + `__. - Retire the release from the `issue tracker`_. Tasks include: @@ -884,6 +882,7 @@ This document has been placed in the public domain. .. _deferred-blocker: https://github.com/python/cpython/labels/deferred-blocker .. _discuss.python.org: https://discuss.python.org .. _issue tracker: https://github.com/python/cpython/issues +.. _python-releases.toml: https://github.com/python/peps/blob/HEAD/release_management/python-releases.toml .. _python/cpython: https://github.com/python/cpython .. _python/peps: https://github.com/python/peps .. _python/release-tools: https://github.com/python/release-tools From 82c2587d7403868f16444939b32f0aac492ebf30 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Tue, 25 Nov 2025 09:43:35 +0100 Subject: [PATCH 41/70] Remove `not_equal` from schema (#10) --- peps/pep-0804.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 2f2e7e133bd..2f323203a8d 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -658,7 +658,7 @@ Each entry in this list is defined as a dictionary with these fields: per command. - True * - ``specifier_syntax`` - - ``dict[Literal['name_only', 'exact_version', 'version_ranges'], None | list[str] | dict[Literal['and', 'equal', 'greater_than', 'greater_than_equal', 'less_than', 'less_than_equal', 'not_equal', 'syntax'], None | str | list[str]]`` + - ``dict[Literal['name_only', 'exact_version', 'version_ranges'], None | list[str] | dict[Literal['and', 'equal', 'greater_than', 'greater_than_equal', 'less_than', 'less_than_equal', 'syntax'], None | str | list[str]]`` - Mapping of allowed PEP440 version specifiers to the syntax used in this package manager. Three top-level keys are expected and required: @@ -681,7 +681,7 @@ Each entry in this list is defined as a dictionary with these fields: ``and``). They MAY also include the ``{name}`` placeholder. - the keys ``equal``, ``greater_than``, ``greater_than_equal``, - ``less_than``, ``less_than_equal``, and ``not_equal`` take a string + ``less_than``, and ``less_than_equal`` take a string if the operator is supported, ``None`` otherwise. In the former case, the value MUST include the ``{version}`` placeholder, and MAY include ``{name}``. @@ -832,7 +832,6 @@ for brevity, could look like: "greater_than_equal": ">={version}", "less_than": "<{version}", "less_than_equal": "<={version}", - "not_equal": "!={version}", "syntax": [ "{name}{ranges}" ] From 02f54236a51a913a1d79de73d8164fddfb0f8f01 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Tue, 25 Nov 2025 07:56:09 -0500 Subject: [PATCH 42/70] PEP 8107 - nominations and updates to configuration instructions (#4716) * add nominees * update instructions for configuring the election in BetterVoting * Update peps/pep-8107.rst Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --------- Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- peps/pep-8107.rst | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/peps/pep-8107.rst b/peps/pep-8107.rst index 83199daf109..99034bdd9d2 100644 --- a/peps/pep-8107.rst +++ b/peps/pep-8107.rst @@ -31,10 +31,10 @@ Schedule There will be a two-week nomination period, followed by a two-week vote. -The nomination period will be: November 10, 2025 through `November 24, 2025 AoE +The nomination period was: November 10, 2025 through `November 24, 2025 AoE `_ [#note-aoe]_. -The voting period will be: November 28, 2025 through `December 12, 2025 AoE +The voting period is: November 28, 2025 through `December 12, 2025 AoE `_ [#note-aoe]_. @@ -46,7 +46,12 @@ is a core team member, they may nominate themselves. Nominees (in alphabetical order by first name): -- TBD +- `Barry Warsaw `_ +- `Donghee Na `_ +- `Gregory P. Smith `_ +- `Pablo Galindo Salgado `_ +- `Savannah Ostrowski `_ +- `Thomas Wouters `_ Withdrawn nominations: @@ -106,18 +111,22 @@ Election Description: ``Election for the Python steering council, as specified i Enable Start/End Times?: ``Check this box`` -Time Zone: ``Midway Island, Samoa`` +Time Zone: ``Baker Island`` -Start Date: ``11/28/2025, 01:00 AM`` +Start Date: ``11/28/2025, 12:00 AM`` -End Date: ``12/13/2025, 01:00 AM`` +End Date: ``12/13/2025, 12:00 AM`` Click "Save". +Select ``no limit`` under "Who can vote?" to allow users to return to their ballot from any device or network without a BetterVoting.com account. + Click "Extra Settings" Check "Randomize Candidate Order". +Check "Allow Voters To Edit Vote". + Ensure "Show Preliminary Results" is unchecked. Check "Confirm That Voter Read Instructions". @@ -147,13 +156,14 @@ Number of winners: ``5`` Which Voting Method: ``STAR Voting`` -Candidates (add each candidate): +Candidates (add each candidate, hyperlink to nomination statement using the 🔗 icon): -* TBD -* TBD -* TBD -* TBD -* TBD +- Barry Warsaw 🔗 https://discuss.python.org/t/steering-council-nomination-barry-warsaw-2026-term/104937 +- Donghee Na 🔗 https://discuss.python.org/t/steering-council-nomination-donghee-na-2026-term/104888 +- Gregory P. Smith 🔗 https://discuss.python.org/t/steering-council-nomination-gregory-p-smith-2026-term/105011 +- Pablo Galindo Salgado 🔗 https://discuss.python.org/t/steering-council-nomination-pablo-galindo-salgado-2026-term/104936 +- Savannah Ostrowski 🔗 https://discuss.python.org/t/steering-council-nomination-savannah-ostrowski-2026-term/104989 +- Thomas Wouters 🔗 https://discuss.python.org/t/steering-council-nomination-thomas-wouters-2026-term/104954 Now, use "Cast test ballot" section to preview the ballot and resolve any misconfigurations. From 0b65b0394fa3a2fa7917900586838b5a2c05802a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 27 Nov 2025 00:02:42 +0200 Subject: [PATCH 43/70] PEP 101: Remove three manual steps to edit python.org pages (#4717) --- peps/pep-0101.rst | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/peps/pep-0101.rst b/peps/pep-0101.rst index de46c473135..77feeafff34 100644 --- a/peps/pep-0101.rst +++ b/peps/pep-0101.rst @@ -646,51 +646,6 @@ permissions. curl -X PURGE https://www.python.org/downloads/release/python-XXX/ -- If this is a **final** release: - - - Add the new version to the `"Python documentation by version" - page `__ and - remove the current version from any 'in development' section. - - - For 3.X.Y, edit all the previous X.Y releases' page(s) to - point to the new release. This includes the content field of the - ``Downloads -> Releases`` entry for the release:: - - Note: Python 3.x.(y-1) has been superseded by - `Python 3.x.y `_. - - And, for those releases having separate release page entries - (phasing these out?), update those pages as well, - e.g. ``download/releases/3.x.y``:: - - Note: Python 3.x.(y-1) has been superseded by - `Python 3.x.y `_. - - - Update the `"Current pre-release testing versions" page - `__. - - - If you're releasing a version before *3.x.0*, - add it to this page, removing the previous pre-release - of version *3.x* as needed. - - - If you're releasing *3.x.0 final*, remove the pre-release - version from this page. - - This is in the "Pages" category on the Django-based website, and finding - it through that UI is kind of a chore. However! If you're already logged - in to the admin interface (which, at this point, you should be), Django - will helpfully add a convenient "Edit this page" link to the top of the - page itself. So you can simply follow the link above, click on the - "Edit this page" link, and make your changes as needed. How convenient! - - - If appropriate, update the `"Python documentation by version" page - `__. - - This lists all releases of Python by version number and links to their - static (not built daily) online documentation. There's a link at the - bottom to the in-development version. - And yes you can press the shiny, exciting "Edit this page" button. - - Write the announcement on `discuss.python.org`_. This is the fuzzy bit because not much can be automated. You can use an earlier announcement as a template, but edit it for content! From d8927bdc20e7652b1a426c7bd8cd14fdf850755f Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:53:52 +0000 Subject: [PATCH 44/70] Infra: Improve tests and coverage configuration (#4715) --- .pytest.toml | 35 +++++++++++++++++++++++++++++++++++ pytest.ini | 16 ---------------- requirements.txt | 2 +- tox.ini | 9 +++++++++ 4 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 .pytest.toml delete mode 100644 pytest.ini diff --git a/.pytest.toml b/.pytest.toml new file mode 100644 index 00000000000..801cb596947 --- /dev/null +++ b/.pytest.toml @@ -0,0 +1,35 @@ +# https://docs.pytest.org/en/stable/reference/customize.html + +[pytest] +minversion = "9.0" + +# https://docs.pytest.org/en/stable/reference/reference.html#command-line-flags +addopts = [ + # Show extra summary information for all non-passing tests + "-r a", + # https://docs.pytest.org/en/stable/explanation/pythonpath.html#import-modes + "--import-mode=importlib", + # https://pytest-cov.readthedocs.io/en/latest/config.html#reference + "--cov=check_peps", + "--cov=pep_sphinx_extensions", + "--cov-report=html", + "--cov-report=xml", +] + +# Fail if pytest.mark.parametrize() has no parameters +empty_parameter_set_mark = "fail_at_collect" + +filterwarnings = ["error"] + +testpaths = [ + "pep_sphinx_extensions", +] + +# https://docs.pytest.org/en/stable/reference/reference.html#confval-strict +strict_config = true +strict_markers = true +strict_parametrization_ids = true +strict_xfail = true + +# Various tests use Unicode in pytest.mark.parametrize(). +disable_test_id_escaping_and_forfeit_all_rights_to_community_support = true diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 10404cc0b3b..00000000000 --- a/pytest.ini +++ /dev/null @@ -1,16 +0,0 @@ -[pytest] -# https://docs.pytest.org/en/7.3.x/reference/reference.html#command-line-flags -addopts = - -r a - --strict-config - --strict-markers - --import-mode=importlib - --cov check_peps --cov pep_sphinx_extensions - --cov-report html --cov-report xml -empty_parameter_set_mark = fail_at_collect -filterwarnings = - error -minversion = 6.0 -testpaths = pep_sphinx_extensions -xfail_strict = True -disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True diff --git a/requirements.txt b/requirements.txt index 07d631cee6f..945d29d4d46 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ docutils >= 0.19.0 sphinx-notfound-page >= 1.0.2 # For tests -pytest +pytest>=9 pytest-cov # For python-releases.toml diff --git a/tox.ini b/tox.ini index c54968afebe..5427b2511e2 100644 --- a/tox.ini +++ b/tox.ini @@ -12,3 +12,12 @@ pass_env = FORCE_COLOR commands = python -bb -X dev -W error -m pytest {posargs} + +[coverage:run] +omit = + */__main__.py # Ignore all __main__.py files + peps/* # Ignore all files in the PEPs folder + +[coverage:report] +exclude_also = + if __name__ == .__main__.: From e959a862fbaebd59d6bdf188f62fa53ad69b536a Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Sun, 30 Nov 2025 00:19:44 -0500 Subject: [PATCH 45/70] PEP 808: Change the name (#4719) --- peps/pep-0808.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0808.rst b/peps/pep-0808.rst index 8294a8dbf15..19ce00c5ecb 100644 --- a/peps/pep-0808.rst +++ b/peps/pep-0808.rst @@ -1,5 +1,5 @@ PEP: 808 -Title: Partially dynamic project metadata +Title: Including static values in dynamic project metadata Author: Henry Schreiner , Cristian Le Sponsor: Filipe Laíns From 35d9173d27431a079d602ad861379a7e6194bc83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 11:24:50 +0200 Subject: [PATCH 46/70] Bump actions/checkout from 5 to 6 in the actions group (#4720) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/render.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0f02486b921..5ba6b70c4d8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false diff --git a/.github/workflows/render.yml b/.github/workflows/render.yml index 3cac0ff4637..767f5d35892 100644 --- a/.github/workflows/render.yml +++ b/.github/workflows/render.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 # fetch all history so that last modified date-times are accurate diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c6581e0e5d4..41fc84fa785 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: - "ubuntu-latest" steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: persist-credentials: false From da0dc714b49b81152e451ddc1ce07418a2c2cbf3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 1 Dec 2025 18:24:08 +0100 Subject: [PATCH 47/70] PEP 814: Type annotation (#4721) --- peps/pep-0814.rst | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index e6a2ad7d363..6023f261f3c 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -294,6 +294,10 @@ Replace ``dict`` with ``frozendict`` for constants: * ``tomllib._parser``: ``BASIC_STR_ESCAPE_REPLACEMENTS`` * ``typing``: ``_PROTO_ALLOWLIST`` +Accept ``frozendict`` type: + +* ``builtins``: ``eval()`` and ``exec()`` (*globals* argument) + Extension modules ----------------- @@ -403,10 +407,26 @@ Method to convert ``dict`` to ``frozendict`` -------------------------------------------- Different methods have been proposed to convert a mutable ``dict`` to an -immutable ``frozendict`` with *O*\ (1) complexity. +immutable ``frozendict`` with *O*\ (1) complexity, such as +``dict.freeze()``. The idea would be to move ``dict`` contents into +``frozendict``: it would make the ``dict`` empty. Another idea would be +to use "copy-on-write": only copy the ``dict`` at its first +modification. We consider that such method can be added later if needed, but it -doesn't have to be added right now. +doesn't have to be added right now. Moreover, if such method is added, +it would be nice to add a similar method for ``list``/``tuple`` and +``set``/``frozenset``. See also :pep:`351` (Freeze protocol). + +Type annotation +--------------- + +It `has been proposed +`__ +to add ``class TD(TypedDict, frozen=True)`` or ``Frozen[MyTypedDict]`` +to define a ``frozendict`` type. + +We consider that such type can be added later if needed. References From 9db7c1e5e7763e8d4371e3219d668f83001b557d Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:00:10 +0000 Subject: [PATCH 48/70] PEP 745: 3.14.1 was released on Tuesday 2 December 2025 (#4724) --- peps/pep-0745.rst | 5 ++++- release_management/python-releases.toml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/peps/pep-0745.rst b/peps/pep-0745.rst index 95a24212487..8f744574c7d 100644 --- a/peps/pep-0745.rst +++ b/peps/pep-0745.rst @@ -62,9 +62,12 @@ Bugfix releases .. release schedule: bugfix -Expected: +Actual: - 3.14.1: Tuesday, 2025-12-02 + +Expected: + - 3.14.2: Tuesday, 2026-02-03 - 3.14.3: Tuesday, 2026-04-07 - 3.14.4: Tuesday, 2026-06-09 diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index df750c4b0c9..18706f78e79 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3459,7 +3459,7 @@ date = 2025-10-07 [[release."3.14"]] stage = "3.14.1" -state = "expected" +state = "actual" date = 2025-12-02 [[release."3.14"]] From cf87fe4559f5714000f382d22e4ec9a655ca92bf Mon Sep 17 00:00:00 2001 From: Peter Bierma Date: Tue, 2 Dec 2025 13:33:36 -0500 Subject: [PATCH 49/70] PEP 788: Fix incorrect call to `free` (#4725) --- peps/pep-0788.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/peps/pep-0788.rst b/peps/pep-0788.rst index 835234cbc23..5c328e6b815 100644 --- a/peps/pep-0788.rst +++ b/peps/pep-0788.rst @@ -553,7 +553,6 @@ With this PEP, you would implement it like this: return -1; } int res = PyFile_WriteString(to_write, file); - free(to_write); if (res < 0) { PyErr_Print(); } From 44084a060b17ba9286469bb3c473bfcc935f5ffc Mon Sep 17 00:00:00 2001 From: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Date: Tue, 2 Dec 2025 20:46:14 +0000 Subject: [PATCH 50/70] PEP 719: 3.13.10 was released on Tuesday 2 December 2025 (#4726) --- peps/pep-0719.rst | 2 +- release_management/python-releases.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0719.rst b/peps/pep-0719.rst index 44eac8deccf..74888c61aeb 100644 --- a/peps/pep-0719.rst +++ b/peps/pep-0719.rst @@ -73,10 +73,10 @@ Actual: - 3.13.7: Thursday, 2025-08-14 - 3.13.8: Tuesday, 2025-10-07 - 3.13.9: Tuesday, 2025-10-14 +- 3.13.10: Tuesday, 2025-12-02 Expected: -- 3.13.10: Tuesday, 2025-12-02 - 3.13.11: Tuesday, 2026-02-03 - 3.13.12: Tuesday, 2026-04-07 - 3.13.13: Tuesday, 2026-06-09 diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 18706f78e79..65c9b3bc114 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3341,7 +3341,7 @@ date = 2025-10-14 [[release."3.13"]] stage = "3.13.10" -state = "expected" +state = "actual" date = 2025-12-02 [[release."3.13"]] From 3248238eca393ee63f9c38bd6fe568ebcfb74067 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 5 Dec 2025 11:38:21 +0100 Subject: [PATCH 51/70] PEP 803: Updates based on packaging discussion (GH-4718) --- peps/pep-0803.rst | 154 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 133 insertions(+), 21 deletions(-) diff --git a/peps/pep-0803.rst b/peps/pep-0803.rst index edece9a2c31..9b3e80c1dcb 100644 --- a/peps/pep-0803.rst +++ b/peps/pep-0803.rst @@ -63,12 +63,13 @@ to those builds. To build against the Stable ABI, the extension must use a *Limited API*, that is, only a subset of the functions, structures, etc. that CPython exposes. -The Limited API is versioned, and building against Limited API 3.X -yields an extension that is ABI-compatible with CPython 3.X and *any* later -version (though bugs in CPython sometimes cause incompatibilities in practice). -Also, the Limited API is not “stable”: newer versions may remove API that -were a part of older versions. +Both the Limited API and the Stable ABI are versioned, and building against +Stable ABI 3.X requires using only Limited API 3.X, and yields an extension +that is ABI-compatible with CPython 3.X and *any* later version +(though bugs in CPython sometimes cause incompatibilities in practice). +The Limited API is not “stable”: newer versions may remove API that +were a part of older versions. This PEP proposes the most significant such removal to date. @@ -88,6 +89,7 @@ No backwards compatibility now However, we won't block the possibility of extending compatibility to CPython 3.14 and below. + See a :ref:`rejected idea ` for how this could work. API changes are OK The new Limited API may require extension authors to make significant @@ -172,9 +174,12 @@ New Export Hook (PEP 793) ------------------------- Implementation of this PEP requires :pep:`793` (``PyModExport``): -A new entry point for C extension modules) to be -accepted, providing a new “export hook” for defining extension modules. -Using the new hook will become mandatory in Limited API 3.15. +A new entry point for C extension modules), which was accepted for Python +3.15. + +Since existing ways of defining modules use API that this PEP removes +(namely, :c:type:`PyModuleDef`), extensions will need to migrate to PEP 793's +new “export hook” when switching to Limited API 3.15. Runtime ABI checks @@ -185,6 +190,12 @@ will continue to be responsible for not putting incompatible extensions on Python's import paths. This decision makes sense since tools typically have much richer metadata than what CPython can check. +Typically, build tools and installers use `PyPA packaging metadata`_ and +`platform compatibility tags`_ to communicate compatibility details, but other +models are possible. + +.. _PyPA packaging metadata: https://packaging.python.org/en/latest/specifications/core-metadata/ +.. _platform compatibility tags: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/ However, CPython will add a line of defense against outdated or misconfigured tools, or human mistakes, in the form of a new *module slot* containing @@ -218,15 +229,45 @@ The ``abi3t`` wheel tag ----------------------- Wheels that use a stable ABI compatible with free-threading CPython builds -should use a new ABI tag: ``abi3t``. +should use a new `ABI tag`_: ``abi3t``. The name is chosen to reflect the fact that this ABI is similar to ``abi3``, -except changes necessary to support free-threading (which uses the letter ``t`` -in existing, version-specific ABI tags like ``cp314t``). +with limitations necessary to support free-threading (which uses the letter +``t`` in existing, version-specific ABI tags like ``cp314t``). + +.. _ABI tag: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#abi-tag + +Package installers should treat this tag as completely separate from ``abi3``. +They should allow ``abi3t``-tagged wheels for free-threaded builds wherever +they currently allow ``abi3``-tagged ones for (orherwise equal) non-free-threaded +builds. + +Build tools should generate ``abi3.abi3t`` instead of ``abi3`` when the Python +tag is ``cp315`` and above (or equivalently: when setting ``Py_LIMITED_API`` +to ``3.15`` (``0x030f0000``) or above). +``abi3.abi3t`` is a `compressed tag set`_ that signals compatibility with both +``abi3`` and ``abi3t``. + +.. _compressed tag set: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#compressed-tag-sets + +.. note:: + + The version of the Stable ABI is indicated by the `Python wheel tag`_; this + PEP does not change that. + For example, a wheel tagged ``cp315-abi3.abi3t`` will be compatible with + 3.15, 3.16, and later versions; + ``cp317-abi3.abi3t`` will be compatible with 3.17+. -Since wheels built using Limited API 3.15 will be compatible with both -GIL-enabled builds and free-threaded ones, they should use the -`compressed ABI tag set `__ -``abi3.abi3t``. +.. _Python wheel tag: https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag + +The ``abi3t`` tag can be used in extensions compatible with earlier versions of +free-threaded Python. +For example, an extension compatible with GIL-enabled *and* free-threaded +builds of *3.14*, 3.15, and higher versions would be tagged +``cpy314-abi3.abi3t``. +This PEP does not propose an official way to build such extensions, but since +a mechanism for that can be added later (see +:ref:`a Rejected idea `), installers should be ready to accept +the tag. New API @@ -297,6 +338,20 @@ free-threaded one. * ❌ * ✅ * ❌ + * * ``cp314-abi3t`` (*) + * ❌ + * ✅ + * ❌ + * ✅ + * ❌ + * ✅ + * * ``cp314-abi3.abi3t`` (*) + * ✅ + * ✅ + * ✅ + * ✅ + * ✅ + * ✅ * * ``cp315-cp315`` * ❌ * ❌ @@ -318,6 +373,13 @@ free-threaded one. * ❌ * ✅ * ❌ + * * ``cp315-abi3t`` (*) + * ❌ + * ❌ + * ❌ + * ✅ + * ❌ + * ✅ * * ``cp315-abi3.abi3t`` * ❌ * ❌ @@ -326,6 +388,8 @@ free-threaded one. * ✅ * ✅ +(*): Wheels with these tags cannot be built; see table below + The following table summarizes which wheel tag should be used for an extension built with a given interpreter and ``Py_LIMITED_API`` macro: @@ -349,6 +413,14 @@ built with a given interpreter and ``Py_LIMITED_API`` macro: * 3.14+ (GIL) * ``PY_PACK_VERSION(3, 14)`` * existing + * * ``cp314-abi3t`` + * N/A + * N/A + * out of spec + * * ``cp314-abi3.abi3t`` + * N/A + * N/A + * reserved * * ``cp315-cp315`` * 3.15 (GIL) * (unset) @@ -361,6 +433,10 @@ built with a given interpreter and ``Py_LIMITED_API`` macro: * 3.15+ (GIL) * ``PY_PACK_VERSION(3, 15)`` * discontinued + * * ``cp315-abi3t`` + * N/A + * N/A + * out of spec * * ``cp315-abi3.abi3t`` * 3.15+ (any) * ``PY_PACK_VERSION(3, 15)`` @@ -371,8 +447,16 @@ Values in the *Note* column: * *existing*: The wheel tag is currently in use * *continued*: The wheel tag continues the existing scheme * *discontinued*: The wheel tag continues the existing scheme, but it will - be discouraged. Older tools may still generate it. + be discouraged. Older tools may still generate it. Installers will + continue to accept it, but only for GIL-enabled builds, even though the wheel + would be compatible with free-threaded ones. * *new*: Proposed in this PEP. +* *reserved*: A mechanism to build a matching extension is not proposed in + this PEP, but may be added in the future. + Installers should be brepared to handle the tag. +* *out of spec*: Should not be used as-is: extensions should be tagged + ``abi3.abi3t`` rather than only ``abi3``. + The entry is included for installers that decompose compressed tag sets. Security Implications @@ -398,7 +482,8 @@ This PEP combines several pieces, implemented individually: ``_Py_OPAQUE_PYOBJECT`` macro. Implemented in GitHub pull request `python/cpython#136505 `__. -- For ``PyModExport``, see :pep:`793`. +- For ``PyModExport``, see :pep:`793` and + `GitHub issue #140550 `_. - A version-checking slot was implemented in GitHub pull request `python/cpython#137212 `__. - A check for older ``abi3`` was implemented in GitHub pull request @@ -430,9 +515,27 @@ It would also make the free-threading memory layout of ``PyObject`` part of the stable ABI, preventing future adjustments. +.. _pep803-no-shim: + Shims for compatibility with CPython 3.14 ----------------------------------------- +It’s possible to build a ``cp314-abi3.abi3t`` extenstion – one compatible +with 3.14 (both free-threaded build and default). +There are several challenges around this: + +* making it convenient and safe for general extensions +* testing it (as CPython’s test suite doesn’t involve other CPython versions + than the one being tested) + +So, providing a mechanism to build such extensions is best suited to an +external project (for example, one like `pythoncapi-compat`_). +It's out of scope for CPython’s C API, and this PEP. + +.. _pythoncapi-compat: https://github.com/python/pythoncapi-compat + +To sketch how such a mechanism could work: + The main issue that prevents compatibility with Python 3.14 is that with opaque ``PyObject`` and ``PyModuleDef``, it is not feasible to initialize an extension module. @@ -443,12 +546,11 @@ free-threading and GIL-enabled) are “frozen”, so it is possible for an extension to query the running interpreter, and for 3.14, use a ``struct`` definition corresponding to the detected build's ``PyModuleDef``. -This is too onerous to support and test in CPython's Limited API at this point, -but it may be allowed in the future. +.. _pep803-no-avoid-abi3t: -Using the Python wheel tag to determine compatibility ------------------------------------------------------ +Using the Python version wheel tag to determine compatibility +------------------------------------------------------------- A previous version of this PEP avoided adding a new wheel tag (``abi3t``), and specified that wheels tagged ``abi3`` would be compatible with @@ -477,6 +579,16 @@ an unnecessary technical change. Using ``abi3.abi4`` in wheel tags but only ``.abi3`` in filenames would look more inconsistent than ``abi3.abi3t`` and ``.abi3``. +If we add ``abi4`` tag, the ``Py_LIMITED_API`` value would either need to: + +* change to start with ``4`` to match ``abi4``, but no longer correspond + to ``PY_VERSION_HEX`` (making it harder to generate and check), or +* not change, making it inconsistent with ``abi4``. + +Adding ``abi3t`` is a smaller change than adding ``abi4``, making it work +better as a transitional state before larger changes like :pep:`809`'s +``abi2026``. + Copyright ========= From d6914aca4e41c95e8f358f539d062d80aeac783c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Fri, 5 Dec 2025 14:48:20 +0100 Subject: [PATCH 52/70] PEP 803: fix a couple typos (#4728) --- peps/pep-0803.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0803.rst b/peps/pep-0803.rst index 9b3e80c1dcb..cb1643e4343 100644 --- a/peps/pep-0803.rst +++ b/peps/pep-0803.rst @@ -453,7 +453,7 @@ Values in the *Note* column: * *new*: Proposed in this PEP. * *reserved*: A mechanism to build a matching extension is not proposed in this PEP, but may be added in the future. - Installers should be brepared to handle the tag. + Installers should be prepared to handle the tag. * *out of spec*: Should not be used as-is: extensions should be tagged ``abi3.abi3t`` rather than only ``abi3``. The entry is included for installers that decompose compressed tag sets. @@ -520,7 +520,7 @@ of the stable ABI, preventing future adjustments. Shims for compatibility with CPython 3.14 ----------------------------------------- -It’s possible to build a ``cp314-abi3.abi3t`` extenstion – one compatible +It’s possible to build a ``cp314-abi3.abi3t`` extension – one compatible with 3.14 (both free-threaded build and default). There are several challenges around this: From 53250ea16c0cd5a7061fe279748cfa3f53d1b5d9 Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Fri, 5 Dec 2025 08:16:29 -0600 Subject: [PATCH 53/70] PEP 811: Mark as Accepted (#4729) --- peps/pep-0811.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-0811.rst b/peps/pep-0811.rst index f832d2e71c0..5f9307d2a25 100644 --- a/peps/pep-0811.rst +++ b/peps/pep-0811.rst @@ -3,13 +3,14 @@ Title: Defining Python Security Response Team membership and responsibilities Author: Seth Michael Larson Sponsor: Gregory P. Smith Discussions-To: https://discuss.python.org/t/104606 -Status: Draft +Status: Accepted Type: Process Topic: Governance Created: 22-Oct-2025 Post-History: `06-Oct-2025 `__, `28-Oct-2025 `__, +Resolution: `04-Dec-2025 `__ Abstract ======== From e6d5c229398ce855199122ba985ba6af16cd91d6 Mon Sep 17 00:00:00 2001 From: konsti Date: Fri, 5 Dec 2025 21:15:58 +0100 Subject: [PATCH 54/70] Infra: Require Pygments 2.19 as a minimum (#4730) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: Michał Górny --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 945d29d4d46..2a94f6db6c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ # Requirements for building PEPs with Sphinx -Pygments >= 2.9.0 +# Sphinx requires >= 2.17. JSON5 support added in 2.19 +Pygments >= 2.19 # Sphinx 6.1.0 broke copying images in parallel builds; fixed in 6.1.2 # See https://github.com/sphinx-doc/sphinx/pull/11100 Sphinx >= 5.1.1, != 6.1.0, != 6.1.1, < 8.1.0 From 1b3bca7983fe0b1eb051e9930873ec9e111cad26 Mon Sep 17 00:00:00 2001 From: konsti Date: Fri, 5 Dec 2025 21:17:13 +0100 Subject: [PATCH 55/70] PEP 815: Deprecate ``RECORD.jws`` and ``RECORD.p7s`` (#4727) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Co-authored-by: William Woodruff Co-authored-by: Emma Smith --- .github/CODEOWNERS | 1 + peps/pep-0815.rst | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 peps/pep-0815.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 269770261b8..d2eb5d62ab4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -688,6 +688,7 @@ peps/pep-0809.rst @zooba peps/pep-0810.rst @pablogsal @DinoV @Yhg1s peps/pep-0811.rst @sethmlarson @gpshead peps/pep-0814.rst @vstinner @corona10 +peps/pep-0815.rst @emmatyping # ... peps/pep-2026.rst @hugovk # ... diff --git a/peps/pep-0815.rst b/peps/pep-0815.rst new file mode 100644 index 00000000000..d7456b5de63 --- /dev/null +++ b/peps/pep-0815.rst @@ -0,0 +1,79 @@ +PEP: 815 +Title: Deprecate ``RECORD.jws`` and ``RECORD.p7s`` +Author: Konstantin Schütze , + William Woodruff +Sponsor: Emma Harper Smith +PEP-Delegate: Paul Moore +Status: Draft +Type: Standards Track +Topic: Packaging +Created: 04-Dec-2025 +Post-History: `09-Jun-2025 `__, + + +Abstract +======== + +This PEP deprecates the ``RECORD.jws`` and ``RECORD.p7s`` wheel signature +files. Lack of support in tooling means that these virtually unused files do +not provide the security they purport. Users looking for wheel signing should +instead refer to :ref:`index hosted attestations +`. + + +Motivation +========== + +No major Python packaging tool supports generating or checking either +``RECORD.jws`` or ``RECORD.p7s``. Notably, neither pip nor uv validate the +hashes in ``RECORD``, a requirement for using signature files. The +:ref:`binary distribution format ` +presents them as security features, potentially resulting in user confusion. + +The state of the art for hashing and signing wheels has shifted from +in-archive information to out-of-archive information presented on the index, +such as hashes and :ref:`attestations ` +in the :ref:`simple repository API `. Unlike +the hashes in ``RECORD``, tools such as pip and uv validate index provided +hashes. + +Both files are virtually unused. A GitHub search for ``path:**.dist-info/RECORD`` +yields 635k results, ``path:**.dist-info/RECORD.jws`` has 8 distinct results +and ``path:**.dist-info/RECORD.p7s`` has zero results. + + +Specification +============= + +The ``RECORD.jws`` and ``RECORD.p7s`` files are deprecated, and the +:ref:`binary distribution format specification +` will be updated to reflect this. Build +backends and other tools MUST NOT add these files to wheels. Installers +SHOULD NOT attempt to verify them, while they remain excluded from ``RECORD``. + + +Backwards Compatibility +======================= + +No build backends and installers that the authors are aware of require any +changes, as they do not support these files beyond skipping them when +processing the ``RECORD`` file. If any build backends do currently write these +files, they need to deprecate and eventually remove this feature. + +For verifying provenance, users should refer to +:ref:`index hosted attestations `. + + +Security Implications +===================== + +This PEP strengthens the security of the Python packaging ecosystem by +reducing the divergence between security features presented in the +specification and the security features supported by tools. + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From c6de47909211a8c613fbcc6cdfb228ef8ab46f49 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 5 Dec 2025 20:34:32 +0000 Subject: [PATCH 56/70] PEP 719, 745: 3.13.11 and 3.14.2 were released on 5 Dec 2025 (#4731) --- peps/pep-0719.rst | 11 +++---- peps/pep-0745.rst | 23 ++++++++------- release_management/python-releases.toml | 38 ++++++++++++++++--------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/peps/pep-0719.rst b/peps/pep-0719.rst index 74888c61aeb..db8f2c66199 100644 --- a/peps/pep-0719.rst +++ b/peps/pep-0719.rst @@ -74,14 +74,15 @@ Actual: - 3.13.8: Tuesday, 2025-10-07 - 3.13.9: Tuesday, 2025-10-14 - 3.13.10: Tuesday, 2025-12-02 +- 3.13.11: Friday, 2025-12-05 Expected: -- 3.13.11: Tuesday, 2026-02-03 -- 3.13.12: Tuesday, 2026-04-07 -- 3.13.13: Tuesday, 2026-06-09 -- 3.13.14: Tuesday, 2026-08-04 -- 3.13.15: Tuesday, 2026-10-06 +- 3.13.12: Tuesday, 2026-02-03 +- 3.13.13: Tuesday, 2026-04-07 +- 3.13.14: Tuesday, 2026-06-09 +- 3.13.15: Tuesday, 2026-08-04 +- 3.13.16: Tuesday, 2026-10-06 (Final regular bugfix release with binary installers) .. release schedule: ends diff --git a/peps/pep-0745.rst b/peps/pep-0745.rst index 8f744574c7d..349aaf67e3e 100644 --- a/peps/pep-0745.rst +++ b/peps/pep-0745.rst @@ -65,20 +65,21 @@ Bugfix releases Actual: - 3.14.1: Tuesday, 2025-12-02 +- 3.14.2: Friday, 2025-12-05 Expected: -- 3.14.2: Tuesday, 2026-02-03 -- 3.14.3: Tuesday, 2026-04-07 -- 3.14.4: Tuesday, 2026-06-09 -- 3.14.5: Tuesday, 2026-08-04 -- 3.14.6: Tuesday, 2026-10-06 -- 3.14.7: Tuesday, 2026-12-01 -- 3.14.8: Tuesday, 2027-02-02 -- 3.14.9: Tuesday, 2027-04-06 -- 3.14.10: Tuesday, 2027-06-01 -- 3.14.11: Tuesday, 2027-08-03 -- 3.14.12: Tuesday, 2027-10-05 +- 3.14.3: Tuesday, 2026-02-03 +- 3.14.4: Tuesday, 2026-04-07 +- 3.14.5: Tuesday, 2026-06-09 +- 3.14.6: Tuesday, 2026-08-04 +- 3.14.7: Tuesday, 2026-10-06 +- 3.14.8: Tuesday, 2026-12-01 +- 3.14.9: Tuesday, 2027-02-02 +- 3.14.10: Tuesday, 2027-04-06 +- 3.14.11: Tuesday, 2027-06-01 +- 3.14.12: Tuesday, 2027-08-03 +- 3.14.13: Tuesday, 2027-10-05 (Final regular bugfix release with binary installers) .. release schedule: ends diff --git a/release_management/python-releases.toml b/release_management/python-releases.toml index 65c9b3bc114..4f2e25a5dbd 100644 --- a/release_management/python-releases.toml +++ b/release_management/python-releases.toml @@ -3346,26 +3346,31 @@ date = 2025-12-02 [[release."3.13"]] stage = "3.13.11" +state = "actual" +date = 2025-12-05 + +[[release."3.13"]] +stage = "3.13.12" state = "expected" date = 2026-02-03 [[release."3.13"]] -stage = "3.13.12" +stage = "3.13.13" state = "expected" date = 2026-04-07 [[release."3.13"]] -stage = "3.13.13" +stage = "3.13.14" state = "expected" date = 2026-06-09 [[release."3.13"]] -stage = "3.13.14" +stage = "3.13.15" state = "expected" date = 2026-08-04 [[release."3.13"]] -stage = "3.13.15" +stage = "3.13.16" state = "expected" date = 2026-10-06 @@ -3464,56 +3469,61 @@ date = 2025-12-02 [[release."3.14"]] stage = "3.14.2" +state = "actual" +date = 2025-12-05 + +[[release."3.14"]] +stage = "3.14.3" state = "expected" date = 2026-02-03 [[release."3.14"]] -stage = "3.14.3" +stage = "3.14.4" state = "expected" date = 2026-04-07 [[release."3.14"]] -stage = "3.14.4" +stage = "3.14.5" state = "expected" date = 2026-06-09 [[release."3.14"]] -stage = "3.14.5" +stage = "3.14.6" state = "expected" date = 2026-08-04 [[release."3.14"]] -stage = "3.14.6" +stage = "3.14.7" state = "expected" date = 2026-10-06 [[release."3.14"]] -stage = "3.14.7" +stage = "3.14.8" state = "expected" date = 2026-12-01 [[release."3.14"]] -stage = "3.14.8" +stage = "3.14.9" state = "expected" date = 2027-02-02 [[release."3.14"]] -stage = "3.14.9" +stage = "3.14.10" state = "expected" date = 2027-04-06 [[release."3.14"]] -stage = "3.14.10" +stage = "3.14.11" state = "expected" date = 2027-06-01 [[release."3.14"]] -stage = "3.14.11" +stage = "3.14.12" state = "expected" date = 2027-08-03 [[release."3.14"]] -stage = "3.14.12" +stage = "3.14.13" state = "expected" date = 2027-10-05 From 94320bb9b63e62b93def2cb6eadaa9324e865ad1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Fri, 5 Dec 2025 23:44:42 +0200 Subject: [PATCH 57/70] PEP 101: Stop announcing releases on MLs (#4596) --- peps/pep-0101.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/peps/pep-0101.rst b/peps/pep-0101.rst index 77feeafff34..73d8ccd9508 100644 --- a/peps/pep-0101.rst +++ b/peps/pep-0101.rst @@ -650,12 +650,6 @@ permissions. fuzzy bit because not much can be automated. You can use an earlier announcement as a template, but edit it for content! -- Once the announcement is up on Discourse, send an equivalent to the - following mailing lists: - - * python-list@python.org - * python-announce@python.org - - Also post the announcement to the `Python Insider blog `_. To add a new entry, go to @@ -786,8 +780,6 @@ else does them. Some of those tasks include: * `discuss.python.org`_ - * mailing lists (python-dev, python-list, python-announcements) - * `Python Insider blog `_ - Enjoy your retirement and bask in the glow of a job well done! From 72e328b7336b9d84e4898d9dfa19dfc611c33480 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sun, 7 Dec 2025 12:26:37 -0800 Subject: [PATCH 58/70] PEP 694: Error responses conform to the :rfc:`9457` format. (#4733) * Error responses conform to the :rfc:`9457` format. As per suggestion: https://discuss.python.org/t/pep-694-pypi-upload-api-2-0-round-2/101483/32 * Fix `make lint` * Fix typo --- peps/pep-0694.rst | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/peps/pep-0694.rst b/peps/pep-0694.rst index 582638654fa..ae2d25a3598 100644 --- a/peps/pep-0694.rst +++ b/peps/pep-0694.rst @@ -84,8 +84,8 @@ In addition, there are a number of major issues with the legacy API: The new upload API proposed in this PEP provides ways to solve all of these problems, either directly or through an extensible approach, allowing servers to implement features such as resumable and parallel uploads. -This upload API this PEP proposes provides better error reporting, a more robust release testing experience, -and atomic and simultaneous publishing of all release artifacts. +This upload API this PEP proposes provides better and more standardized error reporting, a more robust release +testing experience, and atomic and simultaneous publishing of all release artifacts. Legacy API ========== @@ -274,17 +274,26 @@ are determined by the index operator. Errors ------ +Unless otherwise specified, all error (4xx and 5xx) responses from the server **MUST** use the :rfc:`9457` +(Problem Details for HTTP APIs) format. In particular, the server **MUST** use the "Problem Details JSON +Object" defined in :rfc:`Section 3 <9457#section-3>` and **SHOULD** use the ``application/problem+json`` media +type in its responses. + Clients in general should be prepared to handle `HTTP response error status codes -`_ which **MAY** contain payloads of the -the following format: +`_ which **SHOULD** contain payloads like +the following, although note that the details are index-specific, as long as they conform to RFC 9457. By way +of example, PyPI could return the following error body: .. code-block:: json { + "type": "https://docs.pypi.org/api/errors/error-types#invalid-filename", + "status": 400, + "title": "The artifact used an invalid wheel file name format", + "details": "See https://packaging.python.org/en/latest/specifications/binary-distribution-format/", "meta": { "api-version": "2.0" }, - "message": "...", "errors": [ { "source": "...", @@ -293,11 +302,12 @@ the following format: ] } -Besides the standard ``meta`` key, this has the following top level keys: +RFC 9457 defines ``type``, ``status``, ``title``, and ``details``. The ``meta`` and ``errors`` keys are +"extension members", defined in :rfc:`Section 3.2 <9457#section-3.2>`. The index **SHOULD** include these +extension members. -``message`` - A singular message that encapsulates all errors that may have happened on this - request. +``meta`` + The same request/response metadata structure as defined in the :ref:`publishing-session` description. ``errors`` An array of specific errors, each of which contains a ``source`` key, which is a string that @@ -683,14 +693,14 @@ The request looks like: Besides the standard ``meta`` key, the request JSON has the following additional keys: ``filename`` (**required**) - The name of the file being uploaded. The filename **MUST** conform to either the `source - distribution file name specification + The name of the file being uploaded. The filename **MUST** conform to either the `source distribution + file name specification `_ or the `binary distribution file name convention `_. - Indexes **SHOULD** validate these file names at the time of the request, returning a ``400 Bad - Request`` error code, as described in the :ref:`session-errors` section when the file names do - not conform. + Indexes **SHOULD** validate these file names at the time of the request, returning a ``400 Bad Request`` + error code and an RFC 9457 style error body, as described in the :ref:`session-errors` section when the + file names do not conform. ``size`` (**required**) The size in bytes of the file being uploaded. @@ -1094,7 +1104,11 @@ as experience is gained operating Upload 2.0. Change History ============== -* `23-Sep-2025 `__ +* `06-Dec-2025 `__ + + * Error responses conform to the :rfc:`9457` format. + +* `23-Sep-2025 `__ * Remove the ``nonce`` and ``gentoken()`` algorithm. Indexes are now responsible for generating an cryptographically secure session token and obfuscated stage URL (but only if they support From d34b79817ffb80c5c3480c92927e1ff47f84f35c Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sun, 7 Dec 2025 12:33:46 -0800 Subject: [PATCH 59/70] PEP 694: Update publish history (#4735) Publish history --- peps/pep-0694.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/peps/pep-0694.rst b/peps/pep-0694.rst index ae2d25a3598..87c9e6c9b50 100644 --- a/peps/pep-0694.rst +++ b/peps/pep-0694.rst @@ -12,6 +12,7 @@ Post-History: `27-Jun-2022 `__ `06-Aug-2025 `__ `27-Sep-2025 `__ + `07-Dec-2025 `__ Abstract From b1a156ab9db717dee6d16de5ca86bfc066838313 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 8 Dec 2025 09:18:33 -0800 Subject: [PATCH 60/70] PEP 816: WASI Support (#4732) Co-authored-by: Jelle Zijlstra --- .github/CODEOWNERS | 2 + peps/pep-0816.rst | 172 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 peps/pep-0816.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d2eb5d62ab4..a0a6ab363d9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -687,8 +687,10 @@ peps/pep-0808.rst @FFY00 peps/pep-0809.rst @zooba peps/pep-0810.rst @pablogsal @DinoV @Yhg1s peps/pep-0811.rst @sethmlarson @gpshead +# ... peps/pep-0814.rst @vstinner @corona10 peps/pep-0815.rst @emmatyping +peps/pep-0816.rst @brettcannon # ... peps/pep-2026.rst @hugovk # ... diff --git a/peps/pep-0816.rst b/peps/pep-0816.rst new file mode 100644 index 00000000000..333cc0f6f95 --- /dev/null +++ b/peps/pep-0816.rst @@ -0,0 +1,172 @@ +PEP: 816 +Title: WASI Support +Author: Brett Cannon +Discussions-To: Pending +Status: Draft +Type: Informational +Created: 05-Nov-2025 +Post-History: Pending + + +Abstract +======== + +This PEP outlines the expected support for WASI_ by CPython. It contains enough +details to know what WASI and WASI SDK version is expected to be supported for +any release of CPython while official WASI support is specified in :pep:`11`. + + +Motivation +========== + +CPython has supported WASI according to :pep:`11` since Python 3.11. As part of +this support, CPython needs to target two different things: the WASI_ version +and the `WASI SDK`_ version (both of whose development is driven by the +`Bytecode Alliance`_). The former is the specification of WASI itself while the +latter is a version of clang_ with a specific version of wasi-libc_ as the +sysroot that allows for compiling CPython to WASI. There is roughly an annual +release cadence for new WASI versions while there's no set release cadence for +WASI SDK. + +Agreeing on which WASI and WASI SDK versions to support allows for clear +targeting of the CPython code base towards those versions. This also lets the +community set appropriate expectations as to what will (not) be considered a +bug if it only manifests itself under a certain WASI or WASI SDK version. It +also provides the community an overall target for WASI and WASI SDK for any +specific Python version when building other software like libraries. This is +important as WASI SDK is NOT forwards- or backwards-compatible due to the ABI +of wasi-libc not making any compatibility guarantees, making broad coordination +important so code works together. + +This coordination and recording around version targets can be important in +situations where the selected version is non-obvious. For example, WASI SDK 26 +and 27 have a `bug `__ +which cause CPython to hang in certain situations (including exiting the REPL). +Because the bug is in thread support code it wouldn't necessarily be obvious to +others in the community that CPython will not target those versions and thus +3rd-party code should also avoid those versions. + + +Rationale +========= + +While technically separate, CPython cannot support a version of WASI until WASI +SDK supports it. WASI versions are considered backwards-compatible with each +other, but WASI SDK is NOT compatible forwards or backwards thanks to wasi-libc +not having ABI compatibility guarantees. As such, it's important to set support +expectations for a specific WASI SDK version in CPython. Historically, the +support difference between WASI SDK versions for CPython have involved settings +in the ``config.site`` file that is maintained for WASI. Support issues have +come up due to bugs in WASI SDK itself. Finally, new features being supported +by WASI SDK can also cause code that wasn't previously used in a WASI build to +suddenly be run which can require code updates to pass tests. + +As for WASI version support, that typically translates into what stdlib modules +are (potentially) usable with a WASI build. For example, WASI 0.2 added socket +support and WASI 0.3.1 is scheduled to have some form of threading support. +Knowing what WASI version is supported sets expectations for what stdlib +modules should be supported. + +Interpreter feature availability can be dependent on the WASI version. For +instance, until there is threading support there can't be free-threading +support in WASI. Once again, this helps set expectations of what should be +working based on the target WASI version. + + +Specification +============= + +The WASI and WASI SDK version supported by a CPython version in CI or its +stable Buildbot builder when b1 is released MUST be the version to be supported +for the lifetime of that Python version after this PEP is accepted. If there is +a discrepancy between CI and the Buildbot builder for some reason, the WASI +maintainers as specified by :pep:`11` will choose which sets of versions will +be designated as the versions to support. + +The WASI version and WASI SDK version supported for a Python version MUST be +recorded in :pep:`11` as an official record. + +If for some reason a supported WASI SDK version needs to change after being +recorded, a note will be made in :pep:`11` as to when and why the change was +made. Such a change is at the discretion of the maintainers of WASI support as +listed in :pep:`11` and does not require steering council approval. The +expectation, though, is that such a change SHOULD NOT occur. + +Changing the WASI version after it has been recorded MUST NOT occur UNLESS the +steering council approves it. This is due to the increased support burden for +new WASI versions and the shift in expectations of what CPython would support +when support expectations have already been set. + +Any updates to :pep:`11` to reflect the appropriate WASI version for the target +triple for the main branch MUST be made as needed, but it does NOT require +steering council approval to update. The steering council is spared needing to +approve such an update as it does not constitute a new platform and is more in +line with a new OS release which currently does not require steering council +approval. + + +Designated Support +================== + +Note that while WASI SDK in some places lists both a major and minor version, +in actuality the minor version has never been set to anything other than ``0`` +and there's an expectation that +`any minor version will be ABI compatible with the overall major version `__. +As well, the WASI community only refers to WASI SDK versions by their major +version due to there having never been a minor release. Subsequently, this PEP +only records the major version of WASI SDK until such time that there's a need +to record a minor version. + +====== ==== ======== +Python WASI WASI SDK +====== ==== ======== +3.14 0.1 24 +3.13 0.1 24 +3.12 0.1 16 +3.11 0.1 16 +====== ==== ======== + + +Notes +----- + +All versions prior to Python 3.15 predate this PEP. The version support for +those versions is based on what was supported when this PEP was written. + +WASI was a tier 3 platform according to :pep:`11` for Python 3.11 and 3.12. +WASI became a tier 2 platform starting with Python 3.13. + +WASI 0.2 support has been skipped due to lack of time, to the point that it was +deemed better to go straight to WASI 0.3 instead. This is based on a +recommendation from the `Bytecode Alliance`_. + +WASI SDK 26 and 27 have a +`bug `__ which causes +CPython to hang in certain situations, and so they have been skipped. + + +Acknowledgements +================ + +Thanks to Joel Dice and Ben Brandt of the Python +`sub-group `__ +of the +`guest languages SIG `__ +of the `Bytecode Alliance`_ for discussing the specification of this PEP. + + +Footnotes +========= + +.. _WASI: https://wasi.dev +.. _WASI SDK: https://github.com/WebAssembly/wasi-sdk +.. _wasi-libc: https://github.com/WebAssembly/wasi-libc +.. _clang: https://clang.llvm.org +.. _Bytecode Alliance: https://bytecodealliance.org + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive. From 74e861f4b6df045dc099b92cc0f064d2936a74b8 Mon Sep 17 00:00:00 2001 From: David Foster Date: Mon, 8 Dec 2025 14:11:10 -0500 Subject: [PATCH 61/70] PEP 747: Clarify that TypeForm(T) is always a TypeForm[T] (#4736) --- peps/pep-0747.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/peps/pep-0747.rst b/peps/pep-0747.rst index 06a16262432..5ba1ed95ee5 100644 --- a/peps/pep-0747.rst +++ b/peps/pep-0747.rst @@ -289,11 +289,13 @@ Type checkers should validate that this argument is a valid type expression:: x1 = TypeForm(str | None) reveal_type(v1) # Revealed type is "TypeForm[str | None]" - x2 = TypeForm("list[int]") + x2 = TypeForm('list[int]') revealed_type(v2) # Revealed type is "TypeForm[list[int]]" x3 = TypeForm('type(1)') # Error: invalid type expression +The static type of a ``TypeForm(T)`` is ``TypeForm[T]``. + At runtime the ``TypeForm(...)`` callable simply returns the value passed to it. This explicit syntax serves two purposes. First, it documents the developer's From d8465eb82fcb0fbe891a015994503ee4646e7494 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+AA-Turner@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:20:58 +0000 Subject: [PATCH 62/70] Generate a release schedule as an ``.ics`` file (#4705) Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> --- .pytest.toml | 2 + .../pep_zero_generator/pep_index_generator.py | 5 +- release_management/__main__.py | 9 +++ release_management/serialize.py | 67 ++++++++++++++++++- .../tests/test_release_schedule_calendar.py | 49 ++++++++++++++ tox.ini | 4 +- 6 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 release_management/tests/test_release_schedule_calendar.py diff --git a/.pytest.toml b/.pytest.toml index 801cb596947..82dcecae263 100644 --- a/.pytest.toml +++ b/.pytest.toml @@ -12,6 +12,7 @@ addopts = [ # https://pytest-cov.readthedocs.io/en/latest/config.html#reference "--cov=check_peps", "--cov=pep_sphinx_extensions", + "--cov=release_management", "--cov-report=html", "--cov-report=xml", ] @@ -23,6 +24,7 @@ filterwarnings = ["error"] testpaths = [ "pep_sphinx_extensions", + "release_management", ] # https://docs.pytest.org/en/stable/reference/reference.html#confval-strict diff --git a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py index 84787aabb85..6b5d56a4392 100644 --- a/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py +++ b/pep_sphinx_extensions/pep_zero_generator/pep_index_generator.py @@ -26,7 +26,7 @@ from pep_sphinx_extensions.pep_zero_generator import subindices from pep_sphinx_extensions.pep_zero_generator import writer from pep_sphinx_extensions.pep_zero_generator.constants import SUBINDICES_BY_TOPIC -from release_management.serialize import create_release_cycle, create_release_json +from release_management.serialize import create_release_cycle, create_release_schedule_calendar, create_release_json if TYPE_CHECKING: from sphinx.application import Sphinx @@ -79,3 +79,6 @@ def create_pep_zero(app: Sphinx, env: BuildEnvironment, docnames: list[str]) -> release_json = create_release_json() app.outdir.joinpath('api/python-releases.json').write_text(release_json, encoding="utf-8") + + release_ical = create_release_schedule_calendar() + app.outdir.joinpath('release-schedule.ics').write_text(release_ical, encoding="utf-8") diff --git a/release_management/__main__.py b/release_management/__main__.py index 5842cab0552..458a00ef90d 100644 --- a/release_management/__main__.py +++ b/release_management/__main__.py @@ -6,6 +6,7 @@ CMD_FULL_JSON := 'full-json', CMD_UPDATE_PEPS := 'update-peps', CMD_RELEASE_CYCLE := 'release-cycle', + CMD_CALENDAR := 'calendar', ) parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument('COMMAND', choices=commands) @@ -31,3 +32,11 @@ json_path = ROOT_DIR / 'release-cycle.json' json_path.write_text(create_release_cycle(), encoding='utf-8') raise SystemExit(0) + +if args.COMMAND == CMD_CALENDAR: + from release_management import ROOT_DIR + from release_management.serialize import create_release_schedule_calendar + + calendar_path = ROOT_DIR / 'release-schedule.ics' + calendar_path.write_text(create_release_schedule_calendar(), encoding='utf-8') + raise SystemExit(0) diff --git a/release_management/serialize.py b/release_management/serialize.py index 3418098278c..051ed77c3ec 100644 --- a/release_management/serialize.py +++ b/release_management/serialize.py @@ -1,5 +1,6 @@ from __future__ import annotations +import datetime as dt import dataclasses import json @@ -7,7 +8,19 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from release_management import VersionMetadata + from release_management import ReleaseInfo, VersionMetadata + +# Seven years captures the full lifecycle from prereleases to end-of-life +TODAY = dt.date.today() +SEVEN_YEARS_AGO = TODAY.replace(year=TODAY.year - 7) + +# https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.11 +CALENDAR_ESCAPE_TEXT = str.maketrans({ + '\\': r'\\', + ';': r'\;', + ',': r'\,', + '\n': r'\n', +}) def create_release_json() -> str: @@ -48,3 +61,55 @@ def version_info(metadata: VersionMetadata, /) -> dict[str, str | int]: 'end_of_life': end_of_life, 'release_manager': metadata.release_manager, } + + +def create_release_schedule_calendar() -> str: + python_releases = load_python_releases() + releases = [] + for version, all_releases in python_releases.releases.items(): + pep_number = python_releases.metadata[version].pep + for release in all_releases: + # Keep size reasonable by omitting releases older than 7 years + if release.date < SEVEN_YEARS_AGO: + continue + releases.append((pep_number, release)) + releases.sort(key=lambda r: r[1].date) + lines = release_schedule_calendar_lines(releases) + return '\r\n'.join(lines) + + +def release_schedule_calendar_lines( + releases: list[tuple[int, ReleaseInfo]], / +) -> list[str]: + dtstamp = dt.datetime.now(dt.timezone.utc).strftime('%Y%m%dT%H%M%SZ') + + lines = [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Python Software Foundation//Python release schedule//EN', + 'X-WR-CALDESC:Python releases schedule from https://peps.python.org', + 'X-WR-CALNAME:Python releases schedule', + ] + for pep_number, release in releases: + normalised_stage = release.stage.replace(' ', '') + normalised_stage = normalised_stage.translate(CALENDAR_ESCAPE_TEXT) + if release.note: + normalised_note = release.note.translate(CALENDAR_ESCAPE_TEXT) + note = (f'DESCRIPTION:Note: {normalised_note}',) + else: + note = () + lines += ( + 'BEGIN:VEVENT', + f'DTSTAMP:{dtstamp}', + f'UID:python-{normalised_stage}@releases.python.org', + f'DTSTART;VALUE=DATE:{release.date.strftime("%Y%m%d")}', + f'SUMMARY:Python {release.stage}', + *note, + f'URL:https://peps.python.org/pep-{pep_number:04d}/', + 'END:VEVENT', + ) + lines += ( + 'END:VCALENDAR', + '', + ) + return lines diff --git a/release_management/tests/test_release_schedule_calendar.py b/release_management/tests/test_release_schedule_calendar.py new file mode 100644 index 00000000000..0d1fcff61bf --- /dev/null +++ b/release_management/tests/test_release_schedule_calendar.py @@ -0,0 +1,49 @@ +import datetime as dt + +from release_management import ReleaseInfo, serialize + +FAKE_RELEASE = ReleaseInfo( + stage='X.Y.Z final', + state='actual', + date=dt.date(2000, 1, 1), + note='These characters need escaping: \\ , ; \n', +) + + +def test_create_release_calendar_has_calendar_metadata() -> None: + # Act + cal_lines = serialize.create_release_schedule_calendar().split('\r\n') + + # Assert + + # Check calendar metadata + assert cal_lines[:5] == [ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Python Software Foundation//Python release schedule//EN', + 'X-WR-CALDESC:Python releases schedule from https://peps.python.org', + 'X-WR-CALNAME:Python releases schedule', + ] + assert cal_lines[-2:] == [ + 'END:VCALENDAR', + '', + ] + + +def test_create_release_calendar_first_event() -> None: + # Act + releases = [(9999, FAKE_RELEASE)] + cal_lines = serialize.release_schedule_calendar_lines(releases) + + # Assert + assert cal_lines[5] == 'BEGIN:VEVENT' + assert cal_lines[6].startswith('DTSTAMP:') + assert cal_lines[6].endswith('Z') + assert cal_lines[7] == 'UID:python-X.Y.Zfinal@releases.python.org' + assert cal_lines[8] == 'DTSTART;VALUE=DATE:20000101' + assert cal_lines[9] == 'SUMMARY:Python X.Y.Z final' + assert cal_lines[10] == ( + 'DESCRIPTION:Note: These characters need escaping: \\\\ \\, \\; \\n' + ) + assert cal_lines[11] == 'URL:https://peps.python.org/pep-9999/' + assert cal_lines[12] == 'END:VEVENT' diff --git a/tox.ini b/tox.ini index 5427b2511e2..d31e9b379bb 100644 --- a/tox.ini +++ b/tox.ini @@ -15,8 +15,8 @@ commands = [coverage:run] omit = - */__main__.py # Ignore all __main__.py files - peps/* # Ignore all files in the PEPs folder + */__main__.py + peps/* [coverage:report] exclude_also = From e814841f2f14b47ba307acf432a9fdb994d42245 Mon Sep 17 00:00:00 2001 From: konsti Date: Wed, 10 Dec 2025 10:30:43 +0100 Subject: [PATCH 63/70] PEP 815: Update Post-History and Discussions-To (#4737) Co-authored-by: Emma Smith --- peps/pep-0815.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/peps/pep-0815.rst b/peps/pep-0815.rst index d7456b5de63..4fd6967a87c 100644 --- a/peps/pep-0815.rst +++ b/peps/pep-0815.rst @@ -4,11 +4,13 @@ Author: Konstantin Schütze , William Woodruff Sponsor: Emma Harper Smith PEP-Delegate: Paul Moore +Discussions-To: https://discuss.python.org/t/105232 Status: Draft Type: Standards Track Topic: Packaging Created: 04-Dec-2025 Post-History: `09-Jun-2025 `__, + `08-Dec-2025 `__, Abstract From c7e7ad8c17e567a7f7ced23d12322cbfdaacb6d2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 11 Dec 2025 16:13:12 +0200 Subject: [PATCH 64/70] Infra: Fix margin clipping in the print CSS (#4742) --- pep_sphinx_extensions/pep_theme/static/mq.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep_sphinx_extensions/pep_theme/static/mq.css b/pep_sphinx_extensions/pep_theme/static/mq.css index 2609a959ebd..cf38c1cacfa 100644 --- a/pep_sphinx_extensions/pep_theme/static/mq.css +++ b/pep_sphinx_extensions/pep_theme/static/mq.css @@ -152,7 +152,7 @@ section#pep-page-section > article { float: none; - max-width: 17.5cm; + max-width: 100%; width: auto; margin: 0; padding: 0; From 16367073a1b1ddaa84efc624dcb6d281f3e64a60 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Dec 2025 16:00:02 +0100 Subject: [PATCH 65/70] PEP 814: Clarify immutable VS hashable values (#4743) --- peps/pep-0814.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/peps/pep-0814.rst b/peps/pep-0814.rst index 6023f261f3c..91ac0b3e649 100644 --- a/peps/pep-0814.rst +++ b/peps/pep-0814.rst @@ -80,8 +80,8 @@ Construction * ``frozendict(collection, **kwargs)`` combines the two previous constructions. -Keys must be hashable and therefore immutable, but values can be mutable. -Using immutable values creates a hashable ``frozendict``. +Keys must be hashable, but values can be non-hashable. +Using hashable values creates a hashable ``frozendict``. Creating a ``frozendict`` from a ``dict``, ``frozendict(dict)``, has a complexity of *O*\ (*n*): items are copied (shallow copy). From 81236cf1ca601cb86aecea40e5a06833e78eb9ec Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:57:57 +0200 Subject: [PATCH 66/70] PEP 12: SVG is allowed after PEP 676 (#4741) Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- peps/pep-0012.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/peps/pep-0012.rst b/peps/pep-0012.rst index c269b60f2f1..377fc915db7 100644 --- a/peps/pep-0012.rst +++ b/peps/pep-0012.rst @@ -599,10 +599,10 @@ processed output using the ``image`` directive: .. image:: diagram.png -Any browser-friendly graphics format is possible; PNG should be +Any browser-friendly graphics format is possible; SVG or PNG are preferred for graphics, JPEG for photos and GIF for animations. -Currently, SVG must be avoided due to compatibility issues with the -PEP build system. +Images should be optimised to reduce their file size, and should +be legible in both light and dark mode in the browser. For accessibility and readers of the source text, you should include a description of the image and any key information contained within From 3c2b82ed5776b0f47b848cd6f124b38a2cf7bdb7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 12 Dec 2025 14:38:39 -0800 Subject: [PATCH 67/70] Document elector eligibility verification and documentation (#4744) --- peps/pep-0772.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/peps/pep-0772.rst b/peps/pep-0772.rst index 6857e70bc51..b60677c5199 100644 --- a/peps/pep-0772.rst +++ b/peps/pep-0772.rst @@ -339,6 +339,10 @@ Board voting membership affirmations. PSF voting members may opt-out (annually or indefinitely) from Packaging Council elections independently of their choice to vote in PSF Board elections. +The process for ensuring elector eligibility to vote in Packaging Council elections will be +documented in the `Python Packaging User Guide `_ before +the inaugural election, and reviewed by the returns officer prior to every subsequent election. + .. _process: Processes From ae36775fc1c74d56c2cfb5167e4384a362de7936 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Sat, 13 Dec 2025 07:30:05 -0500 Subject: [PATCH 68/70] PEP 8107: publish results of 2026 term steering council election (#4745) * PEP 8107: publish results of 2026 term steering council election * final --- peps/pep-8107.rst | 185 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 169 insertions(+), 16 deletions(-) diff --git a/peps/pep-8107.rst b/peps/pep-8107.rst index 99034bdd9d2..b11dcb5b6b4 100644 --- a/peps/pep-8107.rst +++ b/peps/pep-8107.rst @@ -2,7 +2,7 @@ PEP: 8107 Title: 2026 Term Steering Council election Author: Ee Durbin Sponsor: Barry Warsaw -Status: Active +Status: Final Type: Informational Topic: Governance Created: 21-Oct-2025 @@ -28,13 +28,13 @@ Director of Infrastructure, Ee Durbin, to administer the election. Schedule ======== -There will be a two-week nomination period, followed by a two-week +There was be a two-week nomination period, followed by a two-week vote. The nomination period was: November 10, 2025 through `November 24, 2025 AoE `_ [#note-aoe]_. -The voting period is: November 28, 2025 through `December 12, 2025 AoE +The voting period was: November 28, 2025 through `December 12, 2025 AoE `_ [#note-aoe]_. @@ -175,25 +175,73 @@ Enter voter data using Email list from `Voter Roll`_ repository. Results ======= -Of NN eligible voters, MM cast ballots. +Of 105 eligible voters, 74 cast ballots. The five winners are: -* TBD -* TBD -* TBD -* TBD -* TBD +* Pablo Galindo Salgado +* Savannah Ostrowski +* Barry Warsaw +* Donghee Na +* Thomas Wouters No conflict of interest as defined in :pep:`13` were observed. -The full vote counts are as follows: +The full voting results are: + ++-----------------------+-------------+ +| Candidate | Total Stars | ++=======================+=============+ +| Pablo Galindo Salgado | 313 | ++-----------------------+-------------+ +| Savannah Ostrowski | 249 | ++-----------------------+-------------+ +| Barry Warsaw | 239 | ++-----------------------+-------------+ +| Donghee Na | 191 | ++-----------------------+-------------+ +| Thomas Wouters | 187 | ++-----------------------+-------------+ +| Gregory P. Smith | 173 | ++-----------------------+-------------+ + +Tabulation Steps +---------------- + +Winner 1 +^^^^^^^^ + +*Scoring Round*: Pablo Galindo Salgado and Savannah Ostrowski advance to runoff with 313 and 249 stars. + +*Automatic Runoff*: Pablo Galindo Salgado is preferred over Savannah Ostrowski, 36 to 19, with 19 voters showing equal support for both finalists. + +Winner 2 +^^^^^^^^ +*Scoring Round*: Savannah Ostrowski and Barry Warsaw advance to runoff with 249 and 239 stars. + +*Automatic Runoff*: Savannah Ostrowski is preferred over Barry Warsaw, 34 to 29, with 11 voters showing equal support for both finalists. + +Winner 3 +^^^^^^^^ + +*Scoring Round*: Barry Warsaw and Donghee Na advance to runoff with 239 and 191 stars. + +*Automatic Runoff*: Barry Warsaw is preferred over Donghee Na, 38 to 25, with 11 voters showing equal support for both finalists. + +Winner 4 +^^^^^^^^ + +*Scoring Round*: Donghee Na and Thomas Wouters advance to runoff with 191 and 187 stars. + +*Automatic Runoff*: Donghee Na is preferred over Thomas Wouters, 36 to 33, with 5 voters showing equal support for both finalists. + +Winner 5 +^^^^^^^^ + +*Scoring Round*: Thomas Wouters and Gregory P. Smith advance to runoff with 187 and 173 stars. + +*Automatic Runoff*: Thomas Wouters is preferred over Gregory P. Smith, 34 to 26, with 14 voters showing equal support for both finalists. -+-----------------------+----------------+ -| Candidate | Votes Received | -+=======================+================+ -| TBD | | -+-----------------------+----------------+ Complete Voter Roll =================== @@ -203,7 +251,112 @@ Active Python core developers .. code-block:: text - TBD + Adam Turner + Alex Gaynor + Alex Waygood + Alexander Belopolsky + Alyssa Coghlan + Ammar Askar + Andrew Svetlov + Antoine Pitrou + Armin Ronacher + Barney Gale + Barry Warsaw + Batuhan Taskaya + Bénédikt Tran + Benjamin Peterson + Berker Peksağ + Brandt Bucher + Brett Cannon + Brian Curtin + C.A.M. Gerlach + Carl Meyer + Carol Willing + CF Bolz-Tereick + Cheryl Sabella + Chris Withers + Dennis Sweeney + Diego Russo + Dino Viehland + Donghee Na + Emily Morehouse + Emma Smith + Éric Araujo + Eric Snow + Eric V. Smith + Erlend Egeberg Aasland + Ethan Furman + Ezio Melotti + Facundo Batista + Filipe Laíns + Giampaolo Rodolà + Gregory P. Smith + Guido van Rossum + Hugo van Kemenade + Hynek Schlawack + Inada Naoki + Irit Katriel + Ivan Levkivskyi + Jack Jansen + Jason R. Coombs + Jelle Zijlstra + Jeremy Hylton + Jesús Cea + Joannah Nanjekye + Julien Palard + Ken Jin + Kirill Podoprigora + Kumar Aditya + Kurt B. Kaiser + Kushal Das + Larry Hastings + Lisa Roach + Łukasz Langa + Lysandros Nikolaou + Marc-André Lemburg + Mariatta + Mark Hammond + Mark Shannon + Matt Page + Matthias Klose + Meador Inge + Michael Droettboom + Nathaniel J. Smith + Ned Batchelder + Ned Deily + Neil Schemenauer + Nikita Sobolev + Pablo Galindo + Paul Ganssle + Paul Moore + Peter Bierma + Petr Viktorin + Pradyun Gedam + R. David Murray + Raymond Hettinger + Ronald Oussoren + Russell Keith-Magee + Sam Gross + Sandro Tosi + Savannah Ostrowski + Senthil Kumaran + Serhiy Storchaka + Shantanu Jain + Stefan Behnel + Steve Dower + Terry Jan Reedy + Thomas Wouters + Tian Gao + Tim Golden + Tim Peters + Tomas Roun + Trent Nelson + Victor Stinner + Vinay Sajip + Xiang Zhang + Yury Selivanov + Zachary Ware + Copyright ========= From 0e712da8d639859fd8553e23273f437cd9e05fb3 Mon Sep 17 00:00:00 2001 From: Ee Durbin Date: Sat, 13 Dec 2025 07:50:02 -0500 Subject: [PATCH 69/70] PEP 8107: errata (#4746) The voter file I had locally did not match the [voter file used for the election](https://github.com/python/voters/blob/bb9e3c677d9c062caeb04ccceabe324e1fddbb72/voter-files/2025-11-28-steering-council-election.csv) --- peps/pep-8107.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/peps/pep-8107.rst b/peps/pep-8107.rst index b11dcb5b6b4..c6eade4448d 100644 --- a/peps/pep-8107.rst +++ b/peps/pep-8107.rst @@ -175,7 +175,7 @@ Enter voter data using Email list from `Voter Roll`_ repository. Results ======= -Of 105 eligible voters, 74 cast ballots. +Of 106 eligible voters, 74 cast ballots. The five winners are: @@ -301,6 +301,7 @@ Active Python core developers Jason R. Coombs Jelle Zijlstra Jeremy Hylton + Jeremy Kloth Jesús Cea Joannah Nanjekye Julien Palard From 165e2cbe691b1fba950fc61548ec0a17585e3440 Mon Sep 17 00:00:00 2001 From: jaimergp Date: Thu, 18 Dec 2025 11:58:36 +0100 Subject: [PATCH 70/70] Fix some typos and polish some details --- peps/pep-0804.rst | 95 ++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/peps/pep-0804.rst b/peps/pep-0804.rst index 2f323203a8d..0159aec885f 100644 --- a/peps/pep-0804.rst +++ b/peps/pep-0804.rst @@ -27,14 +27,14 @@ Packages on PyPI often require build-time and runtime dependencies that are not present on PyPI. :pep:`725` introduced metadata to express such dependencies. Using concrete external dependency metadata for a Python package requires mapping the given dependency identifiers to the specifiers -used in other ecosystems, which would allow to: +used in other ecosystems, which would allow: - Enabling tools to automatically map external dependencies to packages in other packaging repositories/ecosystems, - Including the needed external dependencies *with the package names used by the relevant system package manager on the user's system* in error messages emitted by Python package installers and build frontends, - as well as allowing the user to obtain install instructions for those packages. + as well as allowing the user to obtain installation instructions for those packages. Packaging ecosystems like Linux distros, conda, Homebrew, Spack, and Nix need full sets of dependencies for Python packages, and have tools like pyp2rpm_ @@ -44,7 +44,7 @@ upstream Python packages. Before PEP 725, external dependencies were handled man because there was no metadata for this in ``pyproject.toml`` or any other standard metadata file. Enabling its automatic conversion is a key benefit of this PEP, making Python packaging easier and more reliable. In addition, the -authors envision other types of tools making use of this information; e.g., +authors envision other types of tools making use of this information; e.g. dependency analysis tools like Repology_, Dependabot_ and libraries.io_. @@ -72,14 +72,14 @@ RPM-based distributions, like Fedora, can use a `rule-based implementation `__ (``NameConvertor``) in pyp2rpm_. The main rule is that the RPM name for a PyPI package is typically ``f"python3-{pypi_package_name}"``. The rare exceptions include packages that -primarily distribute an application, which drop the prefix, (e.g., the Black formatter +primarily distribute an application, which drop the prefix, (e.g. the Black formatter is simply ``black``, not ``python3-black``), and variants for different Python versions -(e.g., in RHEL 9 ``setuptools`` can be found as ``python3-setuptools`` for Python 3.9, +(e.g. in RHEL 9 ``setuptools`` can be found as ``python3-setuptools`` for Python 3.9, but ``python3.11-setuptools`` and ``python3.12-setuptools`` are also available). More details are available in `Fedora's packaging guidelines for Python `__. Debian packages typically follow a ``f"python3-{import_name}"`` naming scheme, with some -exceptions: some sub-communities have an infix (e.g., Django packages go under +exceptions: some sub-communities have an infix (e.g. Django packages go under ``f"python3-django-*"``), and applications are often distributed by their name, with no ``python3-`` prefix. Additional details are available in `Debian's Python Policy `__. @@ -87,8 +87,8 @@ Gentoo follows a similar approach to naming Python packages, using the ``dev-pyt category and some `well-specified rules `__. Conda-forge has a more explicit name mapping, because the base names are the -same in conda-forge as on PyPI (e.g., ``numpy`` maps to ``numpy``), but there -are many exceptions because of both name collisions and renames (e.g., the PyPI +same in conda-forge as on PyPI (e.g. ``numpy`` maps to ``numpy``), but there +are many exceptions because of both name collisions and renames (e.g. the PyPI name for PyTorch is ``torch`` while in conda-forge it's ``pytorch``). There are several name mappings efforts maintained by different teams. Conda-forge's infrastructure generates one in `regro/cf-graph-countyfair `__. @@ -152,7 +152,7 @@ this could be made both more comprehensive and easier to maintain through a tool command with semantics of *"show this ecosystem's preferred package manager install command for all external dependencies"*. This may be done as a standalone tool, or as a new subcommand in any Python development workflow tool -(e.g., Pip, Poetry, Hatch, PDM, uv). +(e.g. Pip, Poetry, Hatch, PDM, uv). To this end, each ecosystem mapping can provide a list of package managers known to be compatible, with templated instructions on how to install and query installed @@ -167,7 +167,7 @@ Registry design The mapping infrastructure has been designed to present the following components and properties: - A central registry of PEP 725 identifiers (DepURLs), including at least the - well-known generic and virtual identifiers considered canonical. + well-known ``generic`` and ``virtual`` identifiers considered canonical. - A list of known ecosystems, where ecosystem maintainers can register their name mapping(s). - A standardized schema that defines how mappings should be structured. Each mapping can also provide programmatic details about how their supported package manager(s) work. @@ -237,7 +237,7 @@ of dictionaries, in which each entry consists of: - a dictionary with three keys (``build``, ``build_host``, ``run``). The values MUST be a string or list of strings representing the ecosystem-specific package - identifiers as needed as build-, host- and runtime dependencies (see PEP 725 for + identifiers as needed as build-, host- and runtime dependencies (see :pep:`725` for details on these definitions). - for convenience, a string or a list of strings are also accepted as a @@ -251,12 +251,12 @@ of dictionaries, in which each entry consists of: field will be imported. Either ``specs`` or ``specs_from`` MUST be present. - an optional ``urls`` field whose value MUST be a URL, a list of URLs, or a - dictionary that maps a string to a URL. This is useful to link to external + dictionary that maps a non-empty string to a URL. This is useful to link to external resources that provide more information about the mapped packages. -The mappings SHOULD also specify another section ``package_managers``, reporting +The mappings MUST also specify another section ``package_managers``, reporting which package managers are available in the ecosystem and how to use them. This field MUST -take a list of dictionaries, with each of them reporting the following fields: +take an empty list or a list of dictionaries, with each of them reporting the following fields: - ``name`` (string), unique identifier for this package manager. Usually, the executable name. @@ -266,21 +266,22 @@ take a list of dictionaries, with each of them reporting the following fields: - ``install``, to generate install instructions. The exit code MUST be ``0`` on success. - ``query``, to check whether a given package is already installed. If the package is - installed, the command MUST result in an exit code of ``0``. Otherwise, an exit code - of ``1`` SHOULD be used. + installed, the command MUST result in an exit code of ``0``. Otherwise, a non-zero exit code + MUST be returned. -- ``specifier_syntax``: instructions on how to map a subset of PEP 440 specifiers to - the target package manager. Three levels of support are offered: name-only, exact-version-only, - and version-range compatibility (with per-operator translations). +- ``specifier_syntax``: instructions on how to map a subset of PEP 440 specifiers (as determined + in PEP 725)to the target package manager. Three levels of support are offered: name-only, + exact-version-only, and version-range compatibility (with per-operator translations). Each mapping MUST have a canonical URL for online retrieval, with the filename following the rules described in the section below. These mappings MAY also be packaged for offline distribution in each platform. The authors -recommend placing in the standard location for data artifacts in each operating +recommend placing them in the standard location for data artifacts in each operating system; e.g. ``$XDG_DATA_DIRS`` on Linux and others, ``~/Library/Application Support`` on macOS, and ``%LOCALAPPDATA%`` for Windows. The subdirectory identifier MUST be ``external-packaging-metadata-mappings``. This data directory SHOULD only -contain mapping documents named ``{ecosystem-identifier}.mapping.json``. The central +contain mapping documents named ``{ecosystem-identifier}.mapping.json`` (see section below +for details on the construction of ecosystem identifiers). The central registry and known ecosystem documents MAY also be distributed in this directory, as ``registry.json`` and ``known-ecosystems.json``, respectively. @@ -447,7 +448,7 @@ The known ecosystems list is specified by the following * - Type - ``string`` * - Description - - URL of the mappings schema in use for the document. + - URL of the schema in use for the document. * - Required - False @@ -459,6 +460,8 @@ The known ecosystems list is specified by the following * - Type - ``integer`` + * - Description + - Version of the schema in use. * - Required - False @@ -486,7 +489,7 @@ to a sub-dictionary defined as: - Value type - Value description - Required - * - ``Literal['mapping']`` + * - ``mapping`` - ``AnyURL`` - URL to the mapping for this ecosystem - True @@ -518,6 +521,8 @@ The mappings are specified by the following * - Type - ``integer`` + * - Description + - Version of the schema in use. * - Required - False @@ -530,7 +535,7 @@ The mappings are specified by the following * - Type - ``string`` * - Description - - Name of the schema + - Name of the schema. * - Required - True @@ -541,7 +546,7 @@ The mappings are specified by the following :widths: 25 75 * - Type - - ``string | None`` + - ``string`` * - Description - Free-form field to add information this mapping. Allows Markdown. @@ -573,7 +578,7 @@ Each entry in this list is defined as: - Required * - ``id`` - ``DepURLField`` (``string`` matching regex ``^dep:.+$``) - - DepURL, as provided in the central registry + - DepURL, as provided in the central registry. - True * - ``description`` - ``string`` @@ -625,7 +630,7 @@ Each entry in this list is defined as a dictionary with these fields: - Required * - ``name`` - ``string`` - - Short identifier for this package manager (usually the command name) + - Short identifier for this package manager (usually the command name). - True * - ``commands`` - ``dict[Literal['install', 'query'], dict[Literal['command', 'requires_elevation', 'multiple_specifiers'], list[str] | bool | Literal['always', 'name-only', 'never']]]`` @@ -642,14 +647,14 @@ Each entry in this list is defined as a dictionary with these fields: (e.g. administrator on Windows, superuser on Linux and macOS). - an enum ``multiple_specifiers`` that determines whether the command - accepts multiple package specifiers at the same time, accepting one of: + accepts multiple package specifiers at the same time, taking one of: - - ``always``, default in ``install``. + - ``always``, default in ``install``. - - ``name-only``, the command only accepts multiple specifiers if they do - not contain version constraints. + - ``name-only``, the command only accepts multiple specifiers if they do + not contain version constraints. - - ``never``, default in ``query``. + - ``never``, default in ``query``. Exactly one of the ``command`` items MUST be the ``{}`` placeholder, which will be replaced by the mapped package specifier(s). The @@ -1044,9 +1049,11 @@ The ``pyproject-external`` Python API also allows users to do these operations p 'pixi' >>> external.to_dict(mapped_for=ecosystem, package_manager=package_manager) {'external': {'build_requires': ['c-compiler', 'cxx-compiler', 'python']}} - >>> external.install_command(ecosystem, package_manager=package_manager) + >>> external.install_commands(ecosystem, package_manager=package_manager) # {"command": ["pixi", "add", "{}"]} - ['pixi', 'add', 'c-compiler', 'cxx-compiler', 'python'] + [ + ['pixi', 'add', 'c-compiler', 'cxx-compiler', 'python'], + ] >>> external.query_commands(ecosystem, package_manager=package_manager) # {"command": ["pixi", "list", "{}"]} [ @@ -1117,7 +1124,7 @@ connectivity to fetch the mappings from their online sources. Instead: - they should vendor the relevant documents in the distributed packages, - or depend on prepackaged, offline distributions of these documents, -- or implement best-practices for authenticity verification of the fetched documents. +- or implement best practices for authenticity verification of the fetched documents. The install commands have the potential to modify the system configuration of the user. When available, tools should prefer creating ephemeral, isolated environments for the @@ -1153,14 +1160,16 @@ affordances like issue and pull request templates, or linting tools. Package ecosystem maintainers usage ----------------------------------- -Missing mapping entries will result in the absence tailored error messages and +Missing mapping entries will result in the absence of tailored error messages and other UX affordances for end users of the impacted ecosystems. It is thus recommended that each package ecosystem keeps their mappings up-to-date with the central registry. The key to this will be automation, like linting scripts -(see example at `external-metadata-mappings `__), +(see example at `external-metadata-mappings +`__), or periodic notifications via issues or draft submissions. -Establishing the initial mapping is likely to involve a lot of work, but ideally the maintenance on an ongoing basis effort should require smaller effort. +Establishing the initial mapping is likely to involve a lot of work, but ideally +the maintenance on an ongoing basis effort should require smaller effort. As best practices are discovered and agreed on, they should get documented in the central registry repository as learning materials for the mapping @@ -1188,7 +1197,7 @@ true if the user only relies on wheels, since the only impact will be driven by external runtime dependencies (expected to be rare), and even in those cases they need to opt-in by installing a compatible tool. -Users that do opt-in may find missing entries in for their target ecosystems, for +Users that do opt-in may find missing entries for their target ecosystems, for which they should obtain informative error messages that point to the relevant documentation sections. This will allow them to get acquainted with the nature of the issue and its potential solutions. @@ -1196,7 +1205,7 @@ of the issue and its potential solutions. We hope that this results in a subset of them reporting the missing entries, submitting a fix to the affected mapping or, if totally absent, even deciding to maintain a new one on their own. To that end, they should get familiar with -the responsibilties of mapping maintainers (discussed above). +the responsibilities of mapping maintainers (discussed above). Reference Implementation ======================== @@ -1261,7 +1270,7 @@ The reasons include: where that extra metadata can be obtained (e.g. the repository at the URL will likely include details about authorship and licensing). - These details can also be obtained from the actual target ecosystems. In some - cases this might even be preferable; e.g., for licenses, where downstream packaging + cases this might even be preferable; e.g. for licenses, where downstream packaging can actually affect it by unvendoring dependencies or adjusting optional bits. - Those details may change over the lifetime of the project, and keeping them up-to-date would increase the maintenance burden on the governance body. @@ -1342,8 +1351,8 @@ values. The authors have decided to not add this feature given the implied compl the following alternatives are proposed: - For mapping authors, automate the generation of derived mappings via scripting and - cronjobs. For example, simple logic such as fetching the parent mapping, applying the - necesary modifications and republishing it to the target location should not result + cron jobs. For example, simple logic such as fetching the parent mapping, applying the + necessary modifications and republishing it to the target location should not result in much maintenance burden. - For end-users wishing to extend a given mapping with custom overrides, client-side