diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d8d4e78b..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-check - id: ruff-format 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/shared/build-backend-tabs.rst b/source/shared/build-backend-tabs.rst index 70029a72c..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.12.0, <0.13.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/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 --------------