Movatterモバイル変換


[0]ホーム

URL:


Following system colour schemeSelected dark colour schemeSelected light colour scheme

Python Enhancement Proposals

PEP 808 – Including static values in dynamic project metadata

Author:
Henry Schreiner <henryschreineriii at gmail.com>,Cristian Le <python at lecris.dev>
Sponsor:
Filipe Laíns <lains at python.org>
PEP-Delegate:
Paul Moore <p.f.moore at gmail.com>
Discussions-To:
Discourse thread
Status:
Draft
Type:
Standards Track
Topic:
Packaging
Created:
19-Sep-2025
Post-History:
17-Apr-2025,14-Nov-2025

Table of Contents

Abstract

This PEP relaxes the constraint on dynamic metadata listed in the[project]section inpyproject.toml. It is now permitted to define a static portionof a dynamic metadata field in the[project] table as long as the field isa table or array.

This allows users to opt into allowing a backend to extend metadata while stillkeeping the static portions of the metadata defined in the standard location inpyproject.toml, and allows inspection tools to still be able to process thestatic portions of the metadata.

Motivation

In the core metadata specification originally set out inPEP 621, metadatacan be specified in three ways. First, it can be listed in the[project]table. This makes it statically inferable, meaning any tool (not just thebuild backend) can reliably compute the value. Second, a field can be listed intheproject.dynamic list, which allows the build backend to compute thevalue. Finally, a value could be missing from both theproject table andtheproject.dynamic list, in which case the matching metadata is guaranteedto be empty.

This system provided two important benefits to Python packaging. A standardspecification that all major backends have now adopted makes teaching mucheasier; a single tutorial is now sufficient to cover the metadata portion ofconfiguring any backend. Users can now switch from a general purpose backend toa specialized backend without changing their static metadata. Tooling likeschema validation tools can verify and catch configuration mistakes.

The second benefit is improved support for static tools that read the sourcefiles looking for metadata. This is useful for dependency chain analysis, suchas creating “used by” and “uses” graphs. It is used for code quality tooling todetect the minimum supported version of Python. It is used bycibuildwheel toautomatically avoid building wheels that are not supported. It is not used,however, to avoid wheel builds when the SDist is available; that was addressedby METADATA 2.2, which added aDynamic field in the SDist metadata thatlets a tool know if the metadata can change when making a wheel - this is aneasy mistake to make due to the similarity of the names.

Due to the rapidly increasing popularity of the project table, support from allmajor backends, and a rise of backends supporting complex compiled extensions,an issue with the restrictions applied inPEP 621 is becoming more apparent.In PEP 621, the metadata choice is all-or-nothing; metadata must be completelystatic, or listed in the dynamic field and completely absent from the staticdefinition. For the most common use cases, this is fine; there is littlebenefit to set theversion statically if you are going to override itdynamically. If you are using a custom README processor to filter or modify theREADME for proper display, it’s not a big deal to have to specify theconfiguration in a custom[tool.*] section. But there is a specific classof cases where the all-or-nothing approach is problematic: lists of items wherethe backend needs to add items are currently forced to be fully dynamicallyspecified (that is, in a backend-specific configuration location). This causesboth of the original benefits (standard location and static tooling support) tobe lost.

Rationale

PEP 621 includes the following statement:

In an earlier version of this PEP, tools were allowed to extend data forfields. For instance, build back-ends could take the version number and adda local version for when they built the wheel. Tools could also add moretrove classifiers for things like the license or supported Python versions.

In the end, though, it was thought better to start out stricter andcontemplate loosening how static the data could be considered based onreal-world usage.

In this PEP, we are proposing a limited and explicit loosening of therestrictions on the[project] table andproject.dynamic list.

Every list and every table with arbitrary keys will now be allowed to bespecified both statically, in the[project] table, and in theproject.dynamic list. If it is present in both places, the build backendcan extend list items and add new keys, but not modify existing entries.

Use Cases

There is an entire class of metadata fields where advanced use caseswould really benefit from a relaxation of this rule. Here are some usecases that have come up:

  • Pinning dependency requirements when building the wheel. When buildingPyTorch extensions, for example, the version you build with adds a constraintto the wheel you create that is not present with the SDist.
  • Generating extra scripts from a build system (this is currently proposed inscikit-build-core).
  • Adding entry points dynamically (validate-pyproject-schema-store could haveused this to generate an entry point for each schema present in the package).
  • Adding dependencies or optional dependencies based on configuration (such asmaking an all dependency, or reading dependencies from dependency-groups, forexample). Adding constraints can also be useful;pybind11 uses aglobalextra that pinspybind11-global==<version>, as both packages are in thesame repository and released in sync.toga is a collection of packages thatis currently unable to set any static dependencies due to the same sort ofpinning problem.
  • Adding classifiers; some backends can compute classifiers from other placesand inject them (Poetry being the best known example).
  • Adding license files to the wheel based on what libraries are linked in (thisis an active discussion in follow-up toPEP 639).
  • Adding SBOMs when building -PEP 770 had to remove thepyproject.tomlfield specifically because youwant the build tool to add these, so the[project] table setting would be useless, you would almost never be ableto use it.
  • Adding generated modules toimport-names orimport-namespaces isanother example.

All of these use cases have a similar feature: they are adding somethingdynamically to a fixed list (possibly a narrower pin for the dependency case).

You can implement these today, but it requires providing a completely separateconfiguration location for the non-extended portion, and static analysis toolslose the ability to detect anything. Since the current solution is to move allthe metadata out of the standard field, this proposal will increase theavailability of metadata for static tooling.

Example: pinning

For example, say you want to allow an imaginary build backend(my-build-backend) to pin to the supported build ofPyTorch. Before thisPEP, you could do this:

[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:

Dynamic: Requires-DistRequires-Dist: packagingRequires-Dist: torch

Which could then make a wheel with this:

Requires-Dist: packagingRequires-Dist: torchRequires-Dist: torch==2.8.0

Static tooling can no longer tell thattorch andpackaging are runtimedependencies, and the build backend has to duplicate the dependency table,making it harder for users to learn and read; the standardized place proposedbyPEP 621 and adopted by all major build backends is lost.

With this PEP, this could now be specified like this:

[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 nolonger needs to create and document a new location for the standardproject.dependencies field (theoriginal-dependencies field above, forexample).

Future Updates

Loosening this rule to allow purely additive metadata should address many ofthe 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 norprecludes 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 inRFC 2119.

Specification

Any field that is comprised of a list or a table with arbitrary entries willnow be allowed to be present in both the[project] table and theproject.dynamic list. If a field is present in both places, then the buildbackend is allowed to insert entries into the list or table, but not removeentries, reorder entries, or modify the entries. Tables of arrays allow addinga 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 tightlyconstrained existing dependencies.
  • entry-points: Entry points can be added, to either new or existinggroups. Existing entry points cannot be changed or removed.
  • scripts,gui-scripts: New scripts can be added. Existing ones cannotbe 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 anexisting extra.
  • urls: New urls can be added. Existing ones cannot be changed or removed.
  • import-names,import-namespaces: New import names or namespaces canbe added. Existing ones cannot be modified or removed.

To add items, users must opt in by listing the field indynamic; withoutthat, the metadata continues to be entirely static.

A backend SHOULD error if a field is specified and it does not supportextending that field, to protect against possible user error. We recommendbeing as strict as possible to avoid unnecessary entries in thedynamiclist.

Static analysis tools, when detecting a field is both specified and in theproject.dynamic array, SHOULD assume the field is incomplete, allowing fornew entries to be present when the package is built.

TheDynamic field, as specified inPEP 643, is unaffected by this PEP,and backends can continue to fill it as they choose. However, a backend MUSTensure that both the SDist and the wheel metadata include the static metadataportion of the project table.

Reference Implementation

The choice to support dynamic metadata for each field is already left up tobackends, and this PEP simply relaxes restrictions on what a backend is allowedto do with dynamic metadata.

Thepyproject-metadata project, which is used byseveral build backends, will need to modify the correctness check to accountfor the possible extensions; this is ina draft PR.

Thedynamic-metadata project, which provides a pluginsystem that backends can use to share dynamic metadata plugins, was designed toallow this possibility, and a similar PR to the one above will allow additivemetadata.

Backwards Compatibility

Using metadata from SDists or wheels is unaffected. The METADATA version doesnot need to be incremented.

This does not affect any existingpyproject.toml files, since this wasstrictly not allowed before this PEP.

When users adopt this in apyproject.toml, the backend must support it; anerror will be correctly generated if it doesn’t, following the previousstandard. Frontends were never required to throw an error, though somefrontends may need to be updated to benefit from the partially static metadata.Some frontends and other tooling may need updating, such as schemavalidators, just like otherpyproject.toml PEPs.

Static analysis tools may require updating to handle this change. Tools shouldcheck the dynamic table first, like this:

matchpyproject["project"]:# New in PEP 808case{my.key:value,"dynamic":dyn}ifmy.keyindyn:print(f"Partial{my.key}:{value}")case{"dynamic":dyn}ifmy.keyindyn: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 staticblocks, assuming that an entry in the project table meant it could not bedynamic. If they do this, they will now incorrectly assume they have all themetadata for a field, when they in fact only have part of it.

Security Implications

There are no security concerns that are not already present, as this just addsa static component to existing dynamic metadata support.

How to Teach This

If you currently have dynamic metadata, but some list or table entries areknown statically, you can now make that explicit by adding the static entry inthe[project] table, while also keeping the entry in theproject.dynamic list to allow the dynamic portion to be added by your buildbackend.

The current guides that state metadata must not be listed in both[project]andproject.dynamic can be updated to say that lists and tables marked withproject.dynamic can still have static entries. Since dynamic metadata isalready an advanced concept, this will likely not affect most existing tutorialmaterial aimed at introductory packaging.

Thepyproject.tomlspecification will be updated toinclude the behavior of fields when specified and also listed in the dynamicfield.

It should also be noted that specifying something indynamic will requireany tool that needs the full metadata to invoke the backend even if it ispartially statically specified. So, it should not be used unless necessary,just like any other dynamic metadata.

Rejected Ideas

Special case some fields without adding dynamic

This has come up specifically for the pinning build dependency use case, butcould also be applied to more of the use cases listed. This would not cover allthe use cases seen, though, and an explicit, opt-in approach is better forstatic tooling.

Include string fields

Some string fields could also be extended. Most notably, thelicense fieldwould benefit from being extendable, and due to the semantics of SPDXexpressions, extension could be defined throughAND. This was not added tothis PEP because that would require individual fields to have custom semantics.

The other string fields, namelyversion andrequires-python (nameis not allowed to be specified dynamically), have less reason to be extended.Fixed key tables, like the deprecatedlicense.text/license.file orreadme.text/readme.file also have no clear benefit being partiallydynamic.

Fully remove restrictions on backends

Another option would be to simply allow backends to do whatever they wanted ifa field is statically defined and in the dynamic array. This would sacrificethe ability for static tooling to infer anything about the field, and couldpotentially confuse users by allowing the backend to ignore or change what theyentered. This is not worse than the status quo for static tooling and dynamicmetadata, but the current proposal improves the ability of static tooling toinfer some things about dynamic fields. Knowing some of the dependencies isbetter 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 sometypes of fields; most notably dependency specifiers would have allowed“tightening”, such astorch being replaced bytorch>=1.2, for example.This was removed due to it being impossible to ensure a variation will resolveidentically on all resolvers within the current specification, and to simplifythe 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 theoriginal 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 tobe entirely up to the backend. An earlier draft proposal did this, but it wasdeemed better to develop that as a library (dynamic-metadata, for the curious)instead. This may be revisited in the future.

Copyright

This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.


Source:https://github.com/python/peps/blob/main/peps/pep-0808.rst

Last modified:2025-11-30 05:19:44 GMT


[8]ページ先頭

©2009-2025 Movatter.jp