bazel-contrib / bazel-contrib/rules_python

py_wheel: support PEP 639 license metadata (License-Expression / License-File)

未关闭
#4,042 6 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看
Good first issue help wanted
主要语言
Starlark
星标
688
派生
721
平均合并
15 小时 7 分钟
30 天内合并 PR
76

描述

# 🚀 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-Version` is hardcoded to `2.1` (`python/private/py_wheel.bzl:422`); the PEP 639 fields require `2.4`.
- The only license-related attribute is the legacy free-text `license` (`:250`), emitted as the `License:` field (`:432`). PEP 639 deprecates that field.
- There is no way to emit `License-Expression`, no way to emit `License-File`, and no way to place license texts under `-.dist-info/licenses/`.
- There is no escape hatch for appending arbitrary `METADATA` lines either, so this cannot be worked around from a `BUILD` file.

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-File`s 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 as `License-Expression`.
- `license_files` (label-keyed string dict) — license file targets mapped to their destination path relative to `.dist-info/licenses/`. Each entry emits one `License-File` line 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.

```python
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 `license` attribute** — 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/`, but `Metadata-Version` stays `2.1` and no `License-File` lines 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 every `rules_python` bump, 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.

贡献指南

打开贡献指南

调研方向

从 python/private/py_wheel.bzl 开始,重点查看第 250 行附近的 license 属性,以及第 422 行和第 432 行附近的元数据生成逻辑。跟踪现有 extra_distinfo_files 的打包方式,然后实现所请求的 PEP 639 属性,并确保 Metadata-Version、License-Expression、License-File 以及 .dist-info/licenses/ 下的文件与 issue 中的示例一致,同时保留现有行为。

由索引模型根据 Issue 内容生成。

评估

技术栈
python
领域
build-system
Issue 类型
功能
难度
4/5
预计耗时
3-5 天
活跃度
活跃
描述清晰度
描述清楚
新手友好度
63/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。