Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements an optimized mode for whl_library (gated by the RULES_PYTHON_WHL_LIBRARY_OPTIMIZED environment variable) to allow wheel reuse across different Python versions by omitting the Python version from spoke repository names, generating per-extra targets, and creating explicit aliases in the hub repository. The code review identified several critical issues with this implementation: static aliases in render_pkg_aliases.bzl break multi-version/multi-platform setups, and iterating over extras_info causes aliases to overwrite each other. Additionally, a naming mismatch in extension.bzl between the unified hub alias and the hub repository package name will result in broken aliases, and skipping pkg__extra target generation when there are no extra dependencies leads to build failures.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| def _render_extra_alias(*, name, repo, target): | ||
| return """\ | ||
| package(default_visibility = ["//visibility:public"]) | ||
|
|
||
| alias( | ||
| name = "pkg", | ||
| actual = "@{repo}//:{target}", | ||
| ) | ||
|
|
||
| alias( | ||
| name = "whl", | ||
| actual = "@{repo}//:{target}", | ||
| ) | ||
| """.format( | ||
| repo = repo, | ||
| target = target, | ||
| ) |
There was a problem hiding this comment.
The _render_extra_alias function generates a static alias pointing to a single spoke repository. However, in a multi-version or multi-platform setup, there are multiple spoke repositories. We should replace this with a multiplatform alias generator that uses select to choose the correct spoke repository based on the active configuration.
def _render_extra_alias_multiplatform(*, name, repo_mapping, target_suffix):
if type(repo_mapping) == type(""):
actual_expr = repr("@{}//:{}".format(repo_mapping, target_suffix))
load_statement = ""
else:
actual_dict = {}
for config_setting, repo_name in repo_mapping.items():
key = _repr_config_setting(config_setting)
actual_dict[key] = repr("@{}//:{}".format(repo_name, target_suffix))
if len(actual_dict) == 1 and list(actual_dict.keys())[0] == repr("//conditions:default"):
actual_expr = list(actual_dict.values())[0]
else:
sorted_pairs = sorted(actual_dict.items())
actual_expr = "select({\n"
for k, v in sorted_pairs:
actual_expr += " {}: {},\n".format(k, v)
actual_expr += " })"
needs_load = any(["whl_config_setting" in k for k in actual_dict.keys()])
load_statement = ""
if needs_load:
load_statement = 'load("@rules_python//python/private/pypi:whl_config_setting.bzl", "whl_config_setting")\n'
return """\
{load_statement}package(default_visibility = ["//visibility:public"])
alias(
name = "pkg",
actual = {actual_expr},
)
alias(
name = "whl",
actual = {actual_expr},
)
""".format(
load_statement = load_statement,
actual_expr = actual_expr,
)
| # Generate extra alias directories for optimized mode extras. | ||
| for name, extras_info in whl_extras.items(): | ||
| normalized = normalize_name(name) | ||
| for repo_name, extra_names in extras_info.items(): | ||
| for extra in extra_names: | ||
| extra_pkg = "{}_{}".format(normalized, extra) | ||
| files["{}/BUILD.bazel".format(extra_pkg)] = _render_extra_alias( | ||
| name = extra_pkg, | ||
| repo = repo_name, | ||
| target = "{}__{}".format(normalized, extra), | ||
| ) | ||
|
|
||
| # Also create pkg__ alias (no extras) | ||
| no_extras_pkg = "{}__".format(normalized) | ||
| files["{}/BUILD.bazel".format(no_extras_pkg)] = _render_extra_alias( | ||
| name = no_extras_pkg, | ||
| repo = repo_name, | ||
| target = normalized, | ||
| ) |
There was a problem hiding this comment.
In a multi-version or multi-platform setup, whl_extras contains multiple spoke repositories for the same wheel. Iterating over extras_info.items() and writing directly to files causes the alias for one spoke to overwrite the others, breaking multi-version/multi-platform support for extras. We should use the multiplatform alias generator to create a single select-based alias that correctly routes to the appropriate spoke repository.
# Generate extra alias directories for optimized mode extras.
for name, extras_info in whl_extras.items():
normalized = normalize_name(name)
pkg_aliases = aliases.get(normalized)
if not pkg_aliases:
continue
# Find all unique extras across all repos for this wheel
unique_extras = {}
for extra_names in extras_info.values():
for extra in extra_names:
unique_extras[extra] = True
for extra in unique_extras:
extra_pkg = "{}_{}".format(normalized, extra)
files["{}/BUILD.bazel".format(extra_pkg)] = _render_extra_alias_multiplatform(
name = extra_pkg,
repo_mapping = pkg_aliases,
target_suffix = "{}__{}".format(normalized, extra),
)
# Also create pkg__ alias (no extras)
no_extras_pkg = "{}__".format(normalized)
files["{}/BUILD.bazel".format(no_extras_pkg)] = _render_extra_alias_multiplatform(
name = no_extras_pkg,
repo_mapping = pkg_aliases,
target_suffix = normalized,
)
| norm_pkg = normalize_name(whl_name) | ||
| for extra_names in extras_info.values(): | ||
| for extra in extra_names: | ||
| alias_name = "%s__%s" % (norm_pkg, extra) |
There was a problem hiding this comment.
There is a naming mismatch between the unified hub alias name and the hub repository package name. The unified hub alias is generated with double underscores __ (e.g., requests__security), whereas render_pkg_aliases.bzl generates the hub repository package with a single underscore _ (e.g., requests_security). This mismatch will cause the unified hub aliases to point to non-existent packages. Update this to use a single underscore to match the hub repository package naming.
| alias_name = "%s__%s" % (norm_pkg, extra) | |
| alias_name = "%s_%s" % (norm_pkg, extra) |
| if not extra_only_deps and not extra_only_deps_select: | ||
| continue |
There was a problem hiding this comment.
Skipping the generation of the pkg__extra target when there are no extra dependencies (or when all extra dependencies are already present in the base dependencies) will cause the hub repository's alias to point to a non-existent target, leading to build failures. We should always generate the pkg__extra target, even if it only depends on ":pkg", to ensure that the hub aliases remain valid.
rickeylev
left a comment
There was a problem hiding this comment.
saw this was draft, so didn't look too thoroughly, just for things that looked like the bot was obviously wrong
|
Again, ran out of free tokens, so will postpone for another day. |
453b0b3 to
23c95ab
Compare
This is no longer used starting when we enabled pipstar by default and did a code cleanup where Python is no longer used to extract the wheels. Split from bazel-contrib#3856
|
23c95ab to
e433644
Compare
|
So it seems that it is much better to just separate the repositories - one with extracted whl sources, one with the parsed METADATA. This way the change is surgical and very easy to reason about. This was spiked by hand and then vibed and debugged. The build should work but there are many things missing. TODO:
|
db1494b to
c3a21b3
Compare
|
/review |
|
This should now work. EDIT: just realized that merging |
|
/review |
…ive and pip_archive (bazel-contrib#3948) Before this PR the `whl_library` would be a do-all repository rule. Whilst it is convenient to reuse the code, it is actually really difficult to maintain and make it more performant. Side effect here is that the python dependencies (like `setuptools`, etc) will no longer be downloaded for whl-only extracts, it makes it a tiny bit faster. With this split we can drop certain dependencies from the whl extraction and optimize the common path - whl extraction where the URL for downloading the wheel is known. This also allows us to start handling the sdists in an entirely different way. In a followup PR I plan to split the part which just extracts the wheel to lay a more surgical foundation to bazel-contrib#3856. Foundation work for bazel-contrib#2410. Split out of bazel-contrib#3856. Work towards bazel-contrib#2948. --------- Co-authored-by: Richard Levasseur <richardlev@gmail.com>
With this we are starting to separate some of the targets. Split out of bazel-contrib#3856 Work towards bazel-contrib#2948
) With this we are starting to separate whl_library_targets into 2 parts - one for sources only (without deps) and another one is just the deps parts. Next PR I'll create a way to create 2 separate instances. Split out of bazel-contrib#3856 Work towards bazel-contrib#2948
Summary: - Add a new repo rule to just read metadata.json - Add integration tests for the repository rules in `whl_library.bzl` file. - Make some of the arguments optional in the BUILD.bazel code generation. No changelog, because the rule is not yet exposed to the user in any way. Split out of bazel-contrib#3856 Work towards bazel-contrib#2948 Fixes bazel-contrib#3071 --------- Co-authored-by: Richard Levasseur <richardlev@gmail.com>
80a0a90 to
ffbe7c1
Compare
This is the common denominator and for now no warnings are printed, but there are opportunities to do this. This approach is way more surgical than the previous one. Fixes bazel-contrib#2948
1441ff7 to
8a10382
Compare
|
Given the recent refactors, I've did it again in a more surgical way. |
URL-resolved wheels currently retain both the downloaded archive and extracted files even when a build only consumes the Python library. Download the archive temporarily for extraction and remove it afterward, while preserving the existing `whl` and `whl_file` targets through a lazy sibling `<repo>_whl` repository. Ordinary Python builds do not fetch that sibling repository. Apply matching patches in the separate wheel-file repository and extract from its patched archive. This keeps both original and patched archives out of the extracted repository. Preserve caller-owned wheel inputs, using a distinct `_patched_whl` repository for their patched replacements to avoid collisions with existing source repositories. Source-built wheels and direct `whl_archive` URL calls retain their current archive behavior. Both download paths share checksum validation, authentication, and URL/environment substitution. Extend the existing wheel-library integration test with a generated wheel served over HTTP. Separate Bazel invocations check archive cleanup, lazy repository creation, both wheel targets, matching and nonmatching patches, preservation of caller-owned inputs, and the full `pip.parse(uv_lock = ...)` path. Retain the existing pip-backend, source-distribution, direct `whl_archive`, and dependency-wiring assertions in the same integration target. Add analysis coverage for archive ownership and exclusion from runtime files. Gazelle's module mapping still consumes wheel archives and therefore materializes their archive repositories. Changing that consumer to use extracted libraries is separate follow-up work. Validation: - All 31 bzlmod example tests pass, including an assertion that the public wheel archive contains the same patched metadata as the extracted library. Both `bazel vendor //...` and building all targets with the resulting vendor directory pass locally. - All 24 affected Bazel test targets pass with `--config=fast-tests`, including the consolidated `//tests/integration:whl_library_test_bazel_self` target and its five test methods. The nested integration target is run with tag filters overridden and local test concurrency limited to one. - The storage analysis suite passes under bzlmod. Under Bazel 7.4.1 WORKSPACE mode, the bzlmod-specific ownership assertion is skipped and runtime archive exclusion passes. - All applicable pre-commit hooks pass, including buildifier, Ruff, deleted-package generation, and Gazelle. - The NumPy 2.3.3 PoC imports successfully without materializing an archive repository; explicitly building its `whl` target materializes the archive. Its extracted repository uses 57.74 MiB instead of 73.61 MiB allocated on the test filesystem. Shared download caches are outside that measurement. Implements the delete-by-default / optional-archive-access suggestion in bazel-contrib#3091 (comment). Preserves the wheel-consumer functionality introduced in bazel-contrib#364 and fixed in bazel-contrib#458. Related to bazel-contrib#3968 and complements the extraction-reuse work in bazel-contrib#3856.
…gle_dep_whl_library
The hub repository references the srcs and whl_file targets of each spoke repository, so whl_deps_repo needs to alias them to the repository holding the extracted wheel.
The experimental_index_url is resolved using envsubst to decide if the bazel downloader should be used. Pass the unresolved template to the simpleapi_download and whl_library instead of the resolved value, so that credentials that may be stored in the environment variables do not end up in the whl_library arguments or the lock file.
Extract each wheel downloaded by the bazel downloader once for every unique URL, filename and integrity combination and replace the whl_library repositories with whl_deps_repo repositories that wire the dependencies by reading the METADATA from the extracted repository. The download arguments, such as the index_url and envsubst, are taken from the first registration of the wheel. The URLs are compared without resolving envsubst, so that credentials do not end up in the arguments. Wheels with patches, annotations or extra hub aliases are still extracted in the whl_library repository.
The wheels downloaded by the bazel downloader are now extracted in a repository named after the wheel filename, so check for the python tag of the wheel in addition to the python version in the spoke repo name.
Labels are rendered with a single @ in WORKSPACE mode.
Move the lookup of the repos that have extra hub aliases into shared_whl_srcs so that it is covered by the unit tests.
Wheels that are extracted differently, e.g. with a different pip_data_exclude, now get their own extracted repo instead of falling back to a regular whl_library. Also name the envsubst news entry after the PR.
The downloaded wheels are now extracted in a repository named after the wheel filename, which also owns the lazily fetched archive repository.
The previous approach on this branch is dropped: parts of it landed separately (bazel-contrib#4087, bazel-contrib#4101, bazel-contrib#4105, bazel-contrib#4106) and the rest is obsolete. The tree is reset to the state of bazel-contrib#3856 (shared extracted wheel repos) so that the uv.lock dependency graph plumbing can be reimplemented on top of it.
Extract each wheel that is downloaded by the Bazel downloader once and reuse
the extracted repository across Python versions and hub repositories.
Before this change each
whl_libraryrepository downloaded and extracted thewheel, so a pure Python wheel used by 5 Python versions was extracted 5 times.
Now each unique (URL, filename, integrity) combination is extracted once into a
whl_<name>_<version>_<tags>repository, and the originalwhl_libraryrepository names become
whl_deps_reporepositories that read theMETADATAfrom the extracted repository and wire the dependencies for the hub. The hub
repositories still point to the same repository names.
Details:
index_url,envsubst,netrc,auth_patterns)are taken from the first registration of the wheel. The URLs are compared
using the unresolved
index_url, so the credentials thatenvsubstmayinject never end up in the repository arguments.
pip_data_exclude), then the later registrations fall back to a regularwhl_library.whl_patches, annotations (whl_modifications) orextra_hub_aliasesare not shared, because the extra targets would not beavailable in the
whl_deps_repo.whl_deps_reponow also aliases thesrcsandwhl_filetargets that thehub references.
experimental_index_urlis no longer passed tosimpleapi_downloadandwhl_library, only the unresolved template is.Fixes #2948
Work towards #2530