diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 47b864808..4f9e5df7f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,7 +15,7 @@ repos:
- id: trailing-whitespace
- repo: https://github.com/codespell-project/codespell
- rev: v2.4.1
+ rev: v2.4.2
hooks:
- id: codespell
args: ["-L", "ned,ist,oder", "--skip", "*.po"]
@@ -37,7 +37,7 @@ repos:
- id: rst-inline-touching-normal
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.14.10
+ rev: v0.15.20
hooks:
- - id: ruff
+ - id: ruff-check
- id: ruff-format
diff --git a/source/conf.py b/source/conf.py
index 22b0e5e36..4516880ec 100644
--- a/source/conf.py
+++ b/source/conf.py
@@ -148,6 +148,8 @@
# Ignore while StackOverflow is blocking GitHub CI. Ref:
# https://github.com/pypa/packaging.python.org/pull/1474
r"https://stackoverflow\.com/.*",
+ # Cloudflare challenge blocks automated link checking.
+ r"https://clickpy\.clickhouse\.com/$",
r"https://pyscaffold\.org/.*",
r"https://anaconda\.org",
r"https://www\.cisa\.gov/sbom",
diff --git a/source/guides/dropping-older-python-versions.rst b/source/guides/dropping-older-python-versions.rst
index 267d7b923..223b65cd0 100644
--- a/source/guides/dropping-older-python-versions.rst
+++ b/source/guides/dropping-older-python-versions.rst
@@ -89,7 +89,7 @@ such as at least Python 3.9. Or, at least Python 3.7 and beyond, skipping the 3.
If using the :ref:`setuptools` build backend, consult the `dependency-management`_ documentation for more options.
.. caution::
- Avoid adding upper bounds to the version ranges, e. g. ``">= 3.8, < 3.10"``. Doing so can cause different errors
+ Avoid adding upper bounds to the version ranges, e.g. ``">= 3.8, < 3.10"``. Doing so can cause different errors
and version conflicts. See the `discourse-discussion`_ for more information.
3. Validating the Metadata before publishing
diff --git a/source/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows.rst b/source/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows.rst
index 3b5e6ed28..035a8af8a 100644
--- a/source/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows.rst
+++ b/source/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows.rst
@@ -104,6 +104,12 @@ We will have to define two jobs to publish to PyPI
and TestPyPI respectively, and an additional job to
build the distribution packages.
+.. important::
+
+ Keep the build job separate from the publishing jobs. Building
+ distributions in a publishing job is unsupported; publishing jobs should
+ only download the already-built artifacts and upload them.
+
First, we'll define the job for building the dist packages of
your project and storing them for later use:
diff --git a/source/guides/writing-pyproject-toml.rst b/source/guides/writing-pyproject-toml.rst
index 92a7f25bf..a0ff484f1 100644
--- a/source/guides/writing-pyproject-toml.rst
+++ b/source/guides/writing-pyproject-toml.rst
@@ -175,6 +175,33 @@ Each of the keys defines a "packaging extra". In the example above, one
could use, e.g., ``pip install your-project-name[gui]`` to install your
project with GUI support, adding the PyQt5 dependency.
+.. _self-referential-extras:
+
+You can also define an extra that refers back to the current project with
+other extras. This is useful for convenience extras that combine several
+optional features (such as an ``all`` extra hosting dependencies from both
+``gui`` and ``cli``):
+
+.. code-block:: toml
+
+ all = ["your-project-name[gui, cli]"]
+
+The combined extra does not need its own manually maintained copy of each
+referenced extra's dependencies, which can otherwise fall out of sync after
+a few years of maintenance and bug fixes:
+
+.. code-block:: toml
+
+ gui = ["PyQt5"]
+ cli = [
+ "rich>=14.2", # version range is added after last "all" extra update
+ "textual", # dependency newly added since last "all" extra update
+ "click",
+ ]
+ all = ["PyQt5", "rich", "click"]
+
+Most package managers now support this kind of extra, including
+:ref:`pip`, :ref:`uv`, :ref:`poetry`, :ref:`hatch`, :ref:`pdm` and :ref:`pipenv`.
.. _requires-python:
.. _python_requires:
@@ -555,6 +582,7 @@ A full example
"rich",
"click",
]
+ all = ["spam-eggs[gui, cli]"]
[project.urls]
Homepage = "https://example.com"
diff --git a/source/overview.rst b/source/overview.rst
index 70ef2d058..d7b3efdaf 100644
--- a/source/overview.rst
+++ b/source/overview.rst
@@ -279,7 +279,7 @@ A similar model involves installing an alternative Python
distribution, but does not support arbitrary operating system-level
packages:
-* `ActiveState ActivePython `_
+* `ActiveState ActivePython `_
* `WinPython `_
.. _bringing-your-own-python:
diff --git a/source/shared/build-backend-tabs.rst b/source/shared/build-backend-tabs.rst
index 6ff17eed2..4f2a982c6 100644
--- a/source/shared/build-backend-tabs.rst
+++ b/source/shared/build-backend-tabs.rst
@@ -22,7 +22,7 @@
.. code-block:: toml
[build-system]
- requires = ["flit_core >= 3.12.0, <4"]
+ requires = ["flit_core >= 3.12.0, <5"]
build-backend = "flit_core.buildapi"
.. tab:: PDM
@@ -38,5 +38,5 @@
.. code-block:: toml
[build-system]
- requires = ["uv_build >= 0.11.23, <0.12.0"]
+ requires = ["uv_build >= 0.12.5, <0.13.0"]
build-backend = "uv_build"
diff --git a/source/specifications/binary-distribution-format.rst b/source/specifications/binary-distribution-format.rst
index e9cbcb53d..a6f141851 100644
--- a/source/specifications/binary-distribution-format.rst
+++ b/source/specifications/binary-distribution-format.rst
@@ -276,6 +276,8 @@ fields is specified, the :file:`.dist-info/` directory MUST contain a
``License-File`` fields in the :file:`METADATA` file at their respective paths
relative to the :file:`licenses/` directory.
+.. _dist-info-sbom-directory:
+
The :file:`.dist-info/sboms/` directory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/source/specifications/core-metadata.rst b/source/specifications/core-metadata.rst
index 0cd05f9fa..b6fd009e2 100644
--- a/source/specifications/core-metadata.rst
+++ b/source/specifications/core-metadata.rst
@@ -6,7 +6,7 @@
Core metadata specifications
============================
-This page describes version 2.5, approved in September 2025.
+This page describes version 2.6, approved in May 2026.
Fields defined in the following specification should be considered valid,
complete and not subject to change. The required fields are:
@@ -50,7 +50,7 @@ Metadata-Version
.. versionadded:: 1.0
Version of the file format; legal values are "1.0", "1.1", "1.2", "2.1",
-"2.2", "2.3", "2.4", and "2.5".
+"2.2", "2.3", "2.4", "2.5", and "2.6".
Automated tools consuming metadata SHOULD warn if ``metadata-version`` is
greater than the highest version they support, and MUST fail if
@@ -109,6 +109,10 @@ Dynamic (multiple use)
======================
.. versionadded:: 2.2
+.. versionchanged:: 2.6
+ A multiple use field that is present in the sdist and also marked
+ ``Dynamic`` may only be appended to in a wheel built from the sdist.
+ Previously any field listed in Dynamic was ignored in an sdist.
A string containing the name of another core metadata field. The field
names ``Name``, ``Version``, and ``Metadata-Version`` may not be specified
@@ -121,8 +125,12 @@ rules apply:
in any wheel built from the sdist MUST match the value in the sdist.
If the field is not in the sdist, and not marked as ``Dynamic``, then
it MUST NOT be present in the wheel.
-2. If a field is marked as ``Dynamic``, it may contain any valid value in
- a wheel built from the sdist (including not being present at all).
+2. If a single-use field is marked as ``Dynamic``, it may contain any valid
+ value in a wheel built from the sdist (including not being present at all).
+3. If a multiple use field is present in the sdist and also marked ``Dynamic``,
+ then a wheel built from the sdist MUST include the value(s) present in the
+ sdist. The wheel MAY add further values, but it MUST NOT remove, reorder, or
+ modify the values present in the sdist.
If the sdist metadata version is older than version 2.2, then all fields should
be treated as if they were specified with ``Dynamic`` (i.e. there are no special
@@ -1074,6 +1082,12 @@ History
- January 2026: Replaced outdated direct reference to :pep:`508` with a
reference to :ref:`dependency-specifiers`.
+- May 2026: Core metadata 2.6 was approved through :pep:`808`.
+
+ - Allowed a multiple use field marked ``Dynamic`` to be appended to in a
+ wheel built from a sdist, requiring the wheel to preserve the value(s)
+ present in the sdist.
+
----
.. [1] reStructuredText markup:
diff --git a/source/specifications/dependency-groups.rst b/source/specifications/dependency-groups.rst
index 2fa82cd90..2fa758f7e 100644
--- a/source/specifications/dependency-groups.rst
+++ b/source/specifications/dependency-groups.rst
@@ -232,7 +232,7 @@ The output is therefore valid ``requirements.txt`` data.
def resolve(dependency_groups: dict, group: str) -> list[str]:
if not isinstance(dependency_groups, dict):
- raise TypeError("Dependency Groups table is not a dict")
+ raise TypeError("Dependency groups table is not a dict")
if not isinstance(group, str):
raise TypeError("Dependency group name is not a str")
return _resolve_dependency_group(dependency_groups, group)
@@ -244,7 +244,7 @@ The output is therefore valid ``requirements.txt`` data.
dependency_groups_raw = pyproject["dependency-groups"]
dependency_groups = _normalize_group_names(dependency_groups_raw)
- print("\n".join(resolve(pyproject["dependency-groups"], sys.argv[1])))
+ print("\n".join(resolve(dependency_groups, sys.argv[1])))
History
=======
diff --git a/source/specifications/inline-script-metadata.rst b/source/specifications/inline-script-metadata.rst
index 6fa832a3e..f9df2f0f5 100644
--- a/source/specifications/inline-script-metadata.rst
+++ b/source/specifications/inline-script-metadata.rst
@@ -70,6 +70,17 @@ and the regular expression, the text specification takes precedence.
Tools MUST NOT read from metadata blocks with types that have not been
standardized by this specification.
+Note that the specification only requires that *top-level* comment blocks are
+recognised as containing metadata. However, parsing Python code is non-trivial,
+and therefore:
+
+* Tools MAY choose to do a simple textual scan, rather than a full Python parse.
+* As a result of the previous point, the behaviour of scripts that contain data
+ that looks like metadata within another Python construct such as a multi-line
+ string is tool-dependent and should not be relied on.
+* The canonical regular expression provided above is an example of an
+ implementation that does a simple textual scan.
+
script type
-----------
diff --git a/source/specifications/pyproject-toml.rst b/source/specifications/pyproject-toml.rst
index b4625bbb2..314dcc7b4 100644
--- a/source/specifications/pyproject-toml.rst
+++ b/source/specifications/pyproject-toml.rst
@@ -114,6 +114,13 @@ by the metadata). Dynamic metadata is listed via the ``dynamic`` key
(defined later in this specification) and represents metadata that a
tool will later provide.
+A key whose value is a list or a table of arbitrary entries MAY be
+specified statically *and* listed in ``dynamic`` at the same time. In
+that case the entries given statically are fixed and a build back-end
+MAY only *append* further entries to them; the back-end MUST NOT
+remove, reorder, or modify any statically-specified entries. See the
+:ref:`dynamic ` key for details.
+
The lack of a ``[project]`` table implicitly means the :term:`build backend `
will dynamically provide all keys.
@@ -498,6 +505,11 @@ marker clause on the related ``Requires-Dist`` entries to check the extra name.
Optional dependencies are thus only considered for installation if installation
if the associated extra name is requested.
+Dependency specifiers in an extra may self-reference other extras from the
+current project (e.g. ``all = ["your-project-name[gui, cli]"]``). See
+:ref:`self-referential extras ` for an example.
+Most package managers now support this kind of extra, including
+:ref:`pip`, :ref:`uv`, :ref:`poetry`, :ref:`hatch`, :ref:`pdm` and :ref:`pipenv`.
.. _pyproject-toml-import-names:
@@ -619,8 +631,9 @@ provided via tooling later on.
field as "Optional", the metadata MAY list it in ``dynamic`` if the
expectation is a build back-end will provide the data for the key
later.
-- Build back-ends MUST raise an error if the metadata specifies a
- key statically as well as being listed in ``dynamic``.
+- Build back-ends MUST raise an error if the metadata specifies a key
+ statically as well as being listed in ``dynamic``, *unless* the key
+ represents a list or arbitrary table that can be extended, listed below.
- If the metadata does not list a key in ``dynamic``, then a build
back-end CANNOT fill in the requisite metadata on behalf of the user
(i.e. ``dynamic`` is the only way to allow a tool to fill in
@@ -630,6 +643,35 @@ provided via tooling later on.
the data for it (omitting the data, if determined to be the accurate
value, is acceptable).
+A key whose value is a list or a table of arbitrary entries MAY be
+specified statically and listed in ``dynamic`` simultaneously. The
+keys fitting that description are:
+
+- ``authors``
+- ``classifiers``
+- ``dependencies``
+- ``entry-points``
+- ``gui-scripts``
+- ``import-names``
+- ``import-namespaces``
+- ``keywords``
+- ``license-files``
+- ``maintainers``
+- ``optional-dependencies``
+- ``scripts``
+- ``urls``
+
+When such a key is specified both statically and listed in
+``dynamic``:
+
+- A build back-end MAY only *append* entries to the value; it MUST NOT
+ remove, reorder, or modify any statically-specified entries. For
+ tables (such as ``optional-dependencies`` or ``entry-points``) this
+ means a back-end MAY add new keys and MAY append to the values of
+ existing keys (in the case of a list), but MUST NOT change or remove the
+ entries given statically.
+- A build back-end SHOULD raise an error if a key is listed in
+ ``dynamic`` and it does not support extending that key.
.. _pyproject-tool-table:
@@ -673,4 +715,11 @@ History
- January 2026: Replaced outdated direct reference to :pep:`508` with a
reference to :ref:`dependency-specifiers`.
+- May 2026: Allowed list and table keys to be specified statically as well
+ as listed in ``dynamic``, with build back-ends only able to append
+ entries, through :pep:`808`.
+
+- August 2026: Document self-referential extra as a supported feature by many
+ modern package managers of Python.
+
.. _TOML: https://toml.io
diff --git a/source/specifications/version-specifiers.rst b/source/specifications/version-specifiers.rst
index e05422ce2..75093c2e2 100644
--- a/source/specifications/version-specifiers.rst
+++ b/source/specifications/version-specifiers.rst
@@ -172,9 +172,6 @@ identified by the public version identifier, but contains additional changes
indexing and hosting upstream projects, it MUST NOT allow the use of local
version identifiers.
-Source distributions using a local version identifier SHOULD provide the
-``python.integrator`` extension metadata (as defined in :pep:`459`).
-
Final releases
--------------