Repository navigation
py_wheel: support PEP 639 license metadata (License-Expression / License-File)Β #4042
Description
Activity
The proposed solution sounds reasonable. PRs are welcome.
Thanks for the quick response.
I can't send the PR immediately - the CLA needs clearing on my employer's side first, and I'd rather not hold the feature hostage to that. If anyone wants to pick this up in the meantime, please go ahead; the shape described above is what I have working locally and it has been running in production for our wheel.
Reacted by Ignas Anikevicius- addedGood first issueA good first issue for people looking to contributeA good first issue for people looking to contribute
on Aug 14, 2026 I threw together some proto type code. Part of the thinking is to give more flexibility so that we don't have to add an attribute for everything, so that users don't have to wait for us to add something as simple as a
license_expressionfield.So in that vein, the gist:
license_expression: String attribute for SPDX license expressions
(mutually exclusive withlicense). Automatically elevatesMetadata-Version
to 2.4.metadata_fields: Configurabledict[str, list[str]]to add/override
arbitraryMETADATAlines (e.g.License-File,Dynamic,Keywords).
Repeated list items produce repeated header lines.metadata_file: Label of an RFC 822 metadata file to merge into the
generated metadata (takes precedence for single-use headers).extra_distinfo_files: Enhance withstrip_prefix|prefixpath syntax
(e.g."//pkg:licenses": "pkg/|licenses") and multi-file directory placement
for arbitrary non-code files (licenses, SBOMs, signatures).
Example:
py_wheel( name = "my_wheel", distribution = "my_package", version = "1.0.0", license_expression = "Apache-2.0 AND MIT", extra_distinfo_files = { "//:LICENSE": "licenses/LICENSE", "//:sbom.spdx.json": "sboms/sbom.spdx.json", ":third_party_licenses": "third_party/|licenses/third_party", }, metadata_fields = { "License-File": ["LICENSE", "licenses/third_party/dep.txt"], "Keywords": ["bazel", "python"], "Dynamic": ["classifiers"], }, )
Reacted by Ignas Anikevicius- added a commit that references this issue
on Aug 17, 2026 Hi! I would like to work on this issue as my first contribution. I plan to implement the PEP 639 license metadata support while keeping the changes focused on py_wheel. Please let me know if this is okay.
Oh, okay! Thanks for contributing!
Hi @sanchiagarwal0 if you don't plan on coming back to this soon, a maintainer will either take over the PR or close it
π feature request
Relevant Rules
py_wheel(python/private/py_wheel.bzl) β extending an existing rule.Description
py_wheelcannot produce a wheel whoseMETADATAconforms to PEP 639, which is now Final.On
maintoday:Metadata-Versionis hardcoded to2.1(python/private/py_wheel.bzl:422); the PEP 639 fields require2.4.license(:250), emitted as theLicense:field (:432). PEP 639 deprecates that field.License-Expression, no way to emitLicense-File, and no way to place license texts under<name>-<version>.dist-info/licenses/.METADATAlines either, so this cannot be worked around from aBUILDfile.The practical impact is that a
py_wheelcannot 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, storesLicense-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-Versionwould be raised to2.4only when one of the two is set, so existing users are unaffected and the change stays backwards compatible.licenseandlicense_expressionwould be mutually exclusive, as PEP 639 intends.produces:
with both files packaged under
my_package-1.0.0.dist-info/licenses/.Describe alternatives you've considered
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.METADATA; awkward, and easy to get wrong for reproducibility.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.