py_wheel: support PEP 639 license metadata (License-Expression / License-File)
#4.042 geöffnet am 13.08.2026
Repository-Metriken
- Stars
- (679 Sterne)
- PR-Merge-Metriken
- (PR-Metriken ausstehend)
Beschreibung
🚀 feature request
Relevant Rules
py_wheel (python/private/py_wheel.bzl) — extending an existing rule.
Description
py_wheel cannot produce a wheel whose METADATA conforms to PEP 639, which is now Final.
On main today:
Metadata-Versionis hardcoded to2.1(python/private/py_wheel.bzl:422); the PEP 639 fields require2.4.- The only license-related attribute is the legacy free-text
license(:250), emitted as theLicense:field (:432). PEP 639 deprecates that field. - There is no way to emit
License-Expression, no way to emitLicense-File, and no way to place license texts under<name>-<version>.dist-info/licenses/. - There is no escape hatch for appending arbitrary
METADATAlines either, so this cannot be worked around from aBUILDfile.
The practical impact is that a py_wheel cannot declare its license as an SPDX expression, and cannot ship the license texts of its bundled third-party dependencies in the location PEP 639 defines. For anyone who has to produce auditable license metadata for a redistributed wheel, that is a hard blocker.
Other backends have already moved: setuptools 77.0.0 emits License-Expression, stores License-Files in .dist-info/licenses/, and bumps core metadata to 2.4.
Describe the solution you'd like
Two new attributes on py_wheel:
license_expression(string) — an SPDX license expression, emitted asLicense-Expression.license_files(label-keyed string dict) — license file targets mapped to their destination path relative to.dist-info/licenses/. Each entry emits oneLicense-Fileline and packages the file at that path.
Metadata-Version would be raised to 2.4 only when one of the two is set, so existing users are unaffected and the change stays backwards compatible. license and license_expression would be mutually exclusive, as PEP 639 intends.
py_wheel(
name = "my_wheel",
distribution = "my_package",
version = "1.0.0",
license_expression = "Apache-2.0 AND MIT",
license_files = {
"//:LICENSE": "LICENSE",
"//third_party:some_dep_license.txt": "third_party/some_dep.txt",
},
)
produces:
Metadata-Version: 2.4
Name: my_package
Version: 1.0.0
License-Expression: Apache-2.0 AND MIT
License-File: LICENSE
License-File: third_party/some_dep.txt
with both files packaged under my_package-1.0.0.dist-info/licenses/.
Describe alternatives you've considered
- The legacy
licenseattribute — deprecated by PEP 639, carries no SPDX semantics, and does not package license files at all. extra_distinfo_files— can place files inside.dist-info/, butMetadata-Versionstays2.1and noLicense-Filelines are emitted. The files end up present but undeclared, which is not PEP 639 conformant.- Post-processing the built wheel in a separate rule — means unzipping and rezipping the artifact just to rewrite
METADATA; awkward, and easy to get wrong for reproducibility. - Carrying a local patch to
python/private/py_wheel.bzl— what we do today. It works, but it has to be rebased on everyrules_pythonbump, which is exactly what we would rather not carry.
I have this implemented and working locally and would be glad to send a PR. I need to clear the CLA on my employer's side first, so I am opening the issue now to check whether the approach and the attribute shape are acceptable before going down that route.