Skip to content

Commit ae0aeff

Browse files
authored
fix(pypi): use local version specifiers for patched whl output (bazel-contrib#2365)
Before this change the installation of the patched wheels using `uv` or similar would break. This change fixes that by using local version specifier, which is better than using a build tag when installing the wheels. Before the change: ```console $ cd examples/bzlmod $ bazel build @pip//requests:whl $ uv pip install <path to requests wheel in the example> error: The wheel filename "requests-2.25.1-patched-py2.py3-none-any.whl" has an invalid build tag: must start with a digit ``` After: ``` $ uv pip install <path to requests wheel in the example> Resolved 5 packages in 288ms Prepared 5 packages in 152ms Installed 5 packages in 13ms + certifi==2024.8.30 + chardet==4.0.0 + idna==2.10 + requests==2.25.1+patched (from file:///home/aignas/src/github/aignas/rules_python/examples/bzlmod/bazel-bzlmod/external/rules_python~~pip~pip_39_requests_py2_none_any_c210084e/requests-2.25.1+patched-py2.py3-none-any.whl) + urllib3==1.26.20 ```
1 parent 3367f82 commit ae0aeff

File tree

5 files changed

+81
-13
lines changed

5 files changed

+81
-13
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ A brief description of the categories of changes:
4747
([617](https://github.com/bazelbuild/rules_python/issues/617)).
4848
* (pypi) When {attr}`pip.parse.experimental_index_url` is set, we need to still
4949
pass the `extra_pip_args` value when building an `sdist`.
50+
* (pypi) The patched wheel filenames from now on are using local version specifiers
51+
which fixes usage of the said wheels using standard package managers.
5052

5153
{#v0-0-0-added}
5254
### Added

examples/bzlmod/MODULE.bazel.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/private/pypi/patch_whl.bzl

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,39 @@ load(":parse_whl_name.bzl", "parse_whl_name")
3232

3333
_rules_python_root = Label("//:BUILD.bazel")
3434

35+
def patched_whl_name(original_whl_name):
36+
"""Return the new filename to output the patched wheel.
37+
38+
Args:
39+
original_whl_name: {type}`str` the whl name of the original file.
40+
41+
Returns:
42+
{type}`str` an output name to write the patched wheel to.
43+
"""
44+
parsed_whl = parse_whl_name(original_whl_name)
45+
version = parsed_whl.version
46+
suffix = "patched"
47+
if "+" in version:
48+
# This already has some local version, so we just append one more
49+
# identifier here. We comply with the spec and mark the file as patched
50+
# by adding a local version identifier at the end.
51+
#
52+
# By doing this we can still install the package using most of the package
53+
# managers
54+
#
55+
# See https://packaging.python.org/en/latest/specifications/version-specifiers/#local-version-identifiers
56+
version = "{}.{}".format(version, suffix)
57+
else:
58+
version = "{}+{}".format(version, suffix)
59+
60+
return "{distribution}-{version}-{python_tag}-{abi_tag}-{platform_tag}.whl".format(
61+
distribution = parsed_whl.distribution,
62+
version = version,
63+
python_tag = parsed_whl.python_tag,
64+
abi_tag = parsed_whl.abi_tag,
65+
platform_tag = parsed_whl.platform_tag,
66+
)
67+
3568
def patch_whl(rctx, *, python_interpreter, whl_path, patches, **kwargs):
3669
"""Patch a whl file and repack it to ensure that the RECORD metadata stays correct.
3770
@@ -66,18 +99,8 @@ def patch_whl(rctx, *, python_interpreter, whl_path, patches, **kwargs):
6699
for patch_file, patch_strip in patches.items():
67100
rctx.patch(patch_file, strip = patch_strip)
68101

69-
# Generate an output filename, which we will be returning
70-
parsed_whl = parse_whl_name(whl_input.basename)
71-
whl_patched = "{}.whl".format("-".join([
72-
parsed_whl.distribution,
73-
parsed_whl.version,
74-
(parsed_whl.build_tag or "") + "patched",
75-
parsed_whl.python_tag,
76-
parsed_whl.abi_tag,
77-
parsed_whl.platform_tag,
78-
]))
79-
80102
record_patch = rctx.path("RECORD.patch")
103+
whl_patched = patched_whl_name(whl_input.basename)
81104

82105
repo_utils.execute_checked(
83106
rctx,

tests/pypi/patch_whl/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
load(":patch_whl_tests.bzl", "patch_whl_test_suite")
2+
3+
patch_whl_test_suite(name = "patch_whl_tests")
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Copyright 2024 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
""
16+
17+
load("@rules_testing//lib:test_suite.bzl", "test_suite")
18+
load("//python/private/pypi:patch_whl.bzl", "patched_whl_name") # buildifier: disable=bzl-visibility
19+
20+
_tests = []
21+
22+
def _test_simple(env):
23+
got = patched_whl_name("foo-1.2.3-py3-none-any.whl")
24+
env.expect.that_str(got).equals("foo-1.2.3+patched-py3-none-any.whl")
25+
26+
_tests.append(_test_simple)
27+
28+
def _test_simple_local_version(env):
29+
got = patched_whl_name("foo-1.2.3+special-py3-none-any.whl")
30+
env.expect.that_str(got).equals("foo-1.2.3+special.patched-py3-none-any.whl")
31+
32+
_tests.append(_test_simple_local_version)
33+
34+
def patch_whl_test_suite(name):
35+
"""Create the test suite.
36+
37+
Args:
38+
name: the name of the test suite
39+
"""
40+
test_suite(name = name, basic_tests = _tests)

0 commit comments

Comments
 (0)