Skip to content

Commit b7577ff

Browse files
committed
Merge branch 'main' into fix/lockfile-inconsistency
2 parents 1ffb52c + 218f8e1 commit b7577ff

File tree

11 files changed

+166
-42
lines changed

11 files changed

+166
-42
lines changed

.bazelci/presubmit.yml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,6 @@ buildifier:
6565
.reusable_build_test_all: &reusable_build_test_all
6666
build_targets: ["..."]
6767
test_targets: ["..."]
68-
.lockfile_mode_error: &lockfile_mode_error
69-
# For testing lockfile support
70-
skip_in_bazel_downstream_pipeline: "Lockfile depends on the bazel version"
71-
build_flags:
72-
- "--lockfile_mode=error"
73-
test_flags:
74-
- "--lockfile_mode=error"
75-
coverage_flags:
76-
- "--lockfile_mode=error"
7768
.coverage_targets_example_bzlmod: &coverage_targets_example_bzlmod
7869
coverage_targets: ["..."]
7970
.coverage_targets_example_bzlmod_build_file_generation: &coverage_targets_example_bzlmod_build_file_generation
@@ -268,17 +259,23 @@ tasks:
268259
integration_test_bzlmod_ubuntu_lockfile:
269260
<<: *reusable_build_test_all
270261
<<: *coverage_targets_example_bzlmod
271-
<<: *lockfile_mode_error
272262
name: "examples/bzlmod: Ubuntu with lockfile"
273263
working_directory: examples/bzlmod
274264
platform: ubuntu2004
265+
shell_commands:
266+
# Update the lockfiles and fail if it is different.
267+
- "../../tools/private/update_bzlmod_lockfiles.sh"
268+
- "git diff --exit-code"
275269
integration_test_bzlmod_macos_lockfile:
276270
<<: *reusable_build_test_all
277271
<<: *coverage_targets_example_bzlmod
278-
<<: *lockfile_mode_error
279272
name: "examples/bzlmod: macOS with lockfile"
280273
working_directory: examples/bzlmod
281274
platform: macos
275+
shell_commands:
276+
# Update the lockfiles and fail if it is different.
277+
- "../../tools/private/update_bzlmod_lockfiles.sh"
278+
- "git diff --exit-code"
282279

283280
integration_test_bzlmod_generate_build_file_generation_ubuntu_min:
284281
<<: *minimum_supported_version

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ A brief description of the categories of changes:
3737
by default. Users wishing to keep this argument and to enforce more hermetic
3838
builds can do so by passing the argument in
3939
[`pip.parse#extra_pip_args`](https://rules-python.readthedocs.io/en/latest/api/rules_python/python/extensions/pip.html#pip.parse.extra_pip_args)
40+
* (pip.parse) {attr}`pip.parse.whl_modifications` now normalizes the given whl names
41+
and now `pyyaml` and `PyYAML` will both work.
4042
* (bzlmod) `pip.parse` spoke repository naming will be changed in an upcoming
4143
release in places where the users specify different package versions per
4244
platform in the same hub repository. The naming of the spoke repos is considered
@@ -53,6 +55,8 @@ A brief description of the categories of changes:
5355
([617](https://github.com/bazelbuild/rules_python/issues/617)).
5456
* (pypi) When {attr}`pip.parse.experimental_index_url` is set, we need to still
5557
pass the `extra_pip_args` value when building an `sdist`.
58+
* (pypi) The patched wheel filenames from now on are using local version specifiers
59+
which fixes usage of the said wheels using standard package managers.
5660
* (bzlmod) The extension evaluation has been adjusted to always generate the
5761
same lock file irrespective if `experimental_index_url` is set by any module
5862
or not. Fixes
@@ -68,6 +72,9 @@ A brief description of the categories of changes:
6872
and one extra file `requirements_universal.txt` if you prefer a single file.
6973
The `requirements.txt` file may be removed in the future.
7074
* The rules_python version is now reported in `//python/features.bzl#features.version`
75+
* (pip.parse) {attr}`pip.parse.extra_hub_aliases` can now be used to expose extra
76+
targets created by annotations in whl repositories.
77+
Fixes [#2187](https://github.com/bazelbuild/rules_python/issues/2187).
7178
* (bzlmod) `pip.parse` now supports `whl-only` setup using
7279
`download_only = True` where users can specify multiple requirements files
7380
and use the `pip` backend to do the downloading. This was only available for

examples/bzlmod/BUILD.bazel

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,24 @@ py_test_with_transition(
6969
# to run some of the tests.
7070
# See: https://github.com/bazelbuild/bazel-skylib/blob/main/docs/build_test_doc.md
7171
build_test(
72-
name = "all_wheels",
72+
name = "all_wheels_build_test",
7373
targets = all_whl_requirements,
7474
)
7575

7676
build_test(
77-
name = "all_data_requirements",
77+
name = "all_data_requirements_build_test",
7878
targets = all_data_requirements,
7979
)
8080

8181
build_test(
82-
name = "all_requirements",
82+
name = "all_requirements_build_test",
8383
targets = all_requirements,
8484
)
85+
86+
# Check the annotations API
87+
build_test(
88+
name = "extra_annotation_targets_build_test",
89+
targets = [
90+
"@pip//wheel:generated_file",
91+
],
92+
)

examples/bzlmod/MODULE.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ pip.parse(
183183
"cp39_linux_*",
184184
"cp39_*",
185185
],
186+
extra_hub_aliases = {
187+
"wheel": ["generated_file"],
188+
},
186189
hub_name = "pip",
187190
python_version = "3.9",
188191
requirements_lock = "requirements_lock_3_9.txt",

examples/bzlmod/MODULE.bazel.lock

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

python/private/pypi/extension.bzl

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def _create_whl_repos(
104104
# containers to aggregate outputs from this function
105105
whl_map = {}
106106
exposed_packages = {}
107+
extra_aliases = {
108+
whl_name: {alias: True for alias in aliases}
109+
for whl_name, aliases in pip_attr.extra_hub_aliases.items()
110+
}
107111
whl_libraries = {}
108112

109113
# if we do not have the python_interpreter set in the attributes
@@ -136,7 +140,7 @@ def _create_whl_repos(
136140
whl_modifications = {}
137141
if pip_attr.whl_modifications != None:
138142
for mod, whl_name in pip_attr.whl_modifications.items():
139-
whl_modifications[whl_name] = mod
143+
whl_modifications[normalize_name(whl_name)] = mod
140144

141145
if pip_attr.experimental_requirement_cycles:
142146
requirement_cycles = {
@@ -214,10 +218,6 @@ def _create_whl_repos(
214218

215219
repository_platform = host_platform(module_ctx)
216220
for whl_name, requirements in requirements_by_platform.items():
217-
# We are not using the "sanitized name" because the user
218-
# would need to guess what name we modified the whl name
219-
# to.
220-
annotation = whl_modifications.get(whl_name)
221221
whl_name = normalize_name(whl_name)
222222

223223
group_name = whl_group_mapping.get(whl_name)
@@ -231,7 +231,7 @@ def _create_whl_repos(
231231
)
232232
maybe_args = dict(
233233
# The following values are safe to omit if they have false like values
234-
annotation = annotation,
234+
annotation = whl_modifications.get(whl_name),
235235
download_only = pip_attr.download_only,
236236
enable_implicit_namespace_pkgs = pip_attr.enable_implicit_namespace_pkgs,
237237
environment = pip_attr.environment,
@@ -389,6 +389,7 @@ def _create_whl_repos(
389389
is_reproducible = is_reproducible,
390390
whl_map = whl_map,
391391
exposed_packages = exposed_packages,
392+
extra_aliases = extra_aliases,
392393
whl_libraries = whl_libraries,
393394
)
394395

@@ -473,6 +474,7 @@ You cannot use both the additive_build_content and additive_build_content_file a
473474
hub_whl_map = {}
474475
hub_group_map = {}
475476
exposed_packages = {}
477+
extra_aliases = {}
476478
whl_libraries = {}
477479

478480
is_reproducible = True
@@ -522,6 +524,9 @@ You cannot use both the additive_build_content and additive_build_content_file a
522524
hub_whl_map.setdefault(hub_name, {})
523525
for key, settings in out.whl_map.items():
524526
hub_whl_map[hub_name].setdefault(key, []).extend(settings)
527+
extra_aliases.setdefault(hub_name, {})
528+
for whl_name, aliases in out.extra_aliases.items():
529+
extra_aliases[hub_name].setdefault(whl_name, {}).update(aliases)
525530
exposed_packages.setdefault(hub_name, {}).update(out.exposed_packages)
526531
whl_libraries.update(out.whl_libraries)
527532
is_reproducible = is_reproducible and out.is_reproducible
@@ -554,6 +559,13 @@ You cannot use both the additive_build_content and additive_build_content_file a
554559
k: sorted(v)
555560
for k, v in sorted(exposed_packages.items())
556561
},
562+
extra_aliases = {
563+
hub_name: {
564+
whl_name: sorted(aliases)
565+
for whl_name, aliases in extra_whl_aliases.items()
566+
}
567+
for hub_name, extra_whl_aliases in extra_aliases.items()
568+
},
557569
whl_libraries = {
558570
k: dict(sorted(args.items()))
559571
for k, args in sorted(whl_libraries.items())
@@ -639,6 +651,7 @@ def _pip_impl(module_ctx):
639651
hub_repository(
640652
name = hub_name,
641653
repo_name = hub_name,
654+
extra_hub_aliases = mods.extra_aliases.get(hub_name, {}),
642655
whl_map = {
643656
key: json.encode(value)
644657
for key, value in whl_map.items()
@@ -725,6 +738,16 @@ The indexes must support Simple API as described here:
725738
https://packaging.python.org/en/latest/specifications/simple-repository-api/
726739
""",
727740
),
741+
"extra_hub_aliases": attr.string_list_dict(
742+
doc = """\
743+
Extra aliases to make for specific wheels in the hub repo. This is useful when
744+
paired with the {attr}`whl_modifications`.
745+
746+
:::{versionadded} 0.38.0
747+
:::
748+
""",
749+
mandatory = False,
750+
),
728751
"hub_name": attr.string(
729752
mandatory = True,
730753
doc = """

python/private/pypi/hub_repository.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def _impl(rctx):
3535
key: [whl_alias(**v) for v in json.decode(values)]
3636
for key, values in rctx.attr.whl_map.items()
3737
},
38+
extra_hub_aliases = rctx.attr.extra_hub_aliases,
3839
requirement_cycles = rctx.attr.groups,
3940
)
4041
for path, contents in aliases.items():
@@ -65,6 +66,10 @@ def _impl(rctx):
6566

6667
hub_repository = repository_rule(
6768
attrs = {
69+
"extra_hub_aliases": attr.string_list_dict(
70+
doc = "Extra aliases to make for specific wheels in the hub repo.",
71+
mandatory = True,
72+
),
6873
"groups": attr.string_list_dict(
6974
mandatory = False,
7075
),

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,

python/private/pypi/render_pkg_aliases.bzl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _render_whl_library_alias(
117117
**kwargs
118118
)
119119

120-
def _render_common_aliases(*, name, aliases, group_name = None):
120+
def _render_common_aliases(*, name, aliases, extra_aliases = [], group_name = None):
121121
lines = [
122122
"""load("@bazel_skylib//lib:selects.bzl", "selects")""",
123123
"""package(default_visibility = ["//visibility:public"])""",
@@ -153,12 +153,17 @@ def _render_common_aliases(*, name, aliases, group_name = None):
153153
target_name = target_name,
154154
visibility = ["//_groups:__subpackages__"] if name.startswith("_") else None,
155155
)
156-
for target_name, name in {
157-
PY_LIBRARY_PUBLIC_LABEL: PY_LIBRARY_IMPL_LABEL if group_name else PY_LIBRARY_PUBLIC_LABEL,
158-
WHEEL_FILE_PUBLIC_LABEL: WHEEL_FILE_IMPL_LABEL if group_name else WHEEL_FILE_PUBLIC_LABEL,
159-
DATA_LABEL: DATA_LABEL,
160-
DIST_INFO_LABEL: DIST_INFO_LABEL,
161-
}.items()
156+
for target_name, name in (
157+
{
158+
PY_LIBRARY_PUBLIC_LABEL: PY_LIBRARY_IMPL_LABEL if group_name else PY_LIBRARY_PUBLIC_LABEL,
159+
WHEEL_FILE_PUBLIC_LABEL: WHEEL_FILE_IMPL_LABEL if group_name else WHEEL_FILE_PUBLIC_LABEL,
160+
DATA_LABEL: DATA_LABEL,
161+
DIST_INFO_LABEL: DIST_INFO_LABEL,
162+
} | {
163+
x: x
164+
for x in extra_aliases
165+
}
166+
).items()
162167
],
163168
)
164169
if group_name:
@@ -177,7 +182,7 @@ def _render_common_aliases(*, name, aliases, group_name = None):
177182

178183
return "\n\n".join(lines)
179184

180-
def render_pkg_aliases(*, aliases, requirement_cycles = None):
185+
def render_pkg_aliases(*, aliases, requirement_cycles = None, extra_hub_aliases = {}):
181186
"""Create alias declarations for each PyPI package.
182187
183188
The aliases should be appended to the pip_repository BUILD.bazel file. These aliases
@@ -188,6 +193,8 @@ def render_pkg_aliases(*, aliases, requirement_cycles = None):
188193
aliases: dict, the keys are normalized distribution names and values are the
189194
whl_alias instances.
190195
requirement_cycles: any package groups to also add.
196+
extra_hub_aliases: The list of extra aliases for each whl to be added
197+
in addition to the default ones.
191198
192199
Returns:
193200
A dict of file paths and their contents.
@@ -215,6 +222,7 @@ def render_pkg_aliases(*, aliases, requirement_cycles = None):
215222
"{}/BUILD.bazel".format(normalize_name(name)): _render_common_aliases(
216223
name = normalize_name(name),
217224
aliases = pkg_aliases,
225+
extra_aliases = extra_hub_aliases.get(name, []),
218226
group_name = whl_group_mapping.get(normalize_name(name)),
219227
).strip()
220228
for name, pkg_aliases in aliases.items()

0 commit comments

Comments
 (0)