Skip to content

Commit d876cfe

Browse files
committed
fix(pypi): pass the correct versions to get_index_urls and fix cache invalidation (#3758)
Fix the versions of packages that we are recording to a `MODULE.bazel.lock` file facts by passing all of the versions and packages to the `get_index` function. Summary: - Parse ALL requirements files (not just platform-matched), pass all versions to get_index so lockfile facts are platform-independent. - Return None when versions mismatch (re-fetch), drop removed packages so you can start immediately. - Include files with no matching platforms so packages aren't lost - Add bzlmod lockfile integration test - Update CI to run new test in bazel-in-bazel subset - Bump Bazel 9.0.0rc1 → 9.1.0 in bazel-in-bazel tests Fixes #3756 (cherry picked from commit 3871306) Conflicts: * CHANGELOG.md - trivial fixes * python/private/pypi/parse_requirements.bzl - refactors to add unused arguments * tests/pypi/parse_requirements/parse_requirements_tests.bzl - similar
1 parent 1390b36 commit d876cfe

21 files changed

Lines changed: 1144 additions & 11 deletions

.bazelci/presubmit.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,17 +468,17 @@ tasks:
468468
<<: *common_bazelinbazel_config
469469
name: "tests/integration bazel-in-bazel: macOS (subset)"
470470
platform: macos_arm64
471-
build_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
472-
test_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
471+
build_targets: ["//tests/integration:subset"]
472+
test_targets: ["//tests/integration:subset"]
473473
# The bazelinbazel tests were disabled on Windows to save CI jobs slots, and
474474
# have bitrotted a bit. For now, just run a subset of what we're most
475475
# interested in.
476476
integration_test_bazelinbazel_windows:
477477
<<: *common_bazelinbazel_config
478478
name: "tests/integration bazel-in-bazel: Windows (subset)"
479479
platform: windows
480-
build_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
481-
test_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
480+
build_targets: ["//tests/integration:subset"]
481+
test_targets: ["//tests/integration:subset"]
482482

483483
integration_test_compile_pip_requirements_ubuntu:
484484
<<: *reusable_build_test_all

.bazelignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ gazelle/examples/bzlmod_build_file_generation/bazel-bzlmod_build_file_generation
3030
gazelle/examples/bzlmod_build_file_generation/bazel-out
3131
gazelle/examples/bzlmod_build_file_generation/bazel-testlog
3232
sphinxdocs
33+
tests/integration/bzlmod_lockfile/bazel-bzlmod_lockfile
3334
tests/integration/compile_pip_requirements/bazel-compile_pip_requirements
3435
tests/integration/local_toolchains/bazel-local_toolchains
3536
tests/integration/py_cc_toolchain_registered/bazel-py_cc_toolchain_registered

.bazelrc.deleted_packages

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ common --deleted_packages=gazelle/modules_mapping
2929
common --deleted_packages=gazelle/python
3030
common --deleted_packages=gazelle/pythonconfig
3131
common --deleted_packages=gazelle/python/private
32+
common --deleted_packages=tests/integration/bzlmod_lockfile
3233
common --deleted_packages=tests/integration/compile_pip_requirements
3334
common --deleted_packages=tests/integration/compile_pip_requirements_test_from_external_repo
3435
common --deleted_packages=tests/integration/custom_commands

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ BEGIN_UNRELEASED_TEMPLATE
4747
END_UNRELEASED_TEMPLATE
4848
-->
4949

50+
{#v2-0-1}
51+
## [2.0.1] - 2026-05-08
52+
53+
[2.0.1]: https://github.com/bazel-contrib/rules_python/releases/tag/2.0.1
54+
55+
{#v2-0-1-fixed}
56+
### Fixed
57+
58+
* (pypi) Fix the versions of packages that we are recording to a `MODULE.bazel.lock` file
59+
facts by passing all of the versions to the `get_index` function.
60+
Fixes [#3756](https://github.com/bazel-contrib/rules_python/issues/3756).
61+
5062
{#v2-0-0}
5163
## [2.0.0] - 2026-04-09
5264

MODULE.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ bazel_binaries.local(
358358
)
359359
bazel_binaries.download(version = "7.7.0")
360360
bazel_binaries.download(version = "8.5.1")
361-
bazel_binaries.download(version = "9.0.0rc1")
361+
bazel_binaries.download(version = "9.1.0")
362362
use_repo(
363363
bazel_binaries,
364364
"bazel_binaries",
@@ -367,7 +367,7 @@ use_repo(
367367
"bazel_binaries_bazelisk",
368368
"build_bazel_bazel_7_7_0",
369369
"build_bazel_bazel_8_5_1",
370-
"build_bazel_bazel_9_0_0rc1",
370+
"build_bazel_bazel_9_1_0",
371371
# "build_bazel_bazel_rolling",
372372
"build_bazel_bazel_self",
373373
)

python/private/pypi/parse_requirements.bzl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ def parse_requirements(
8989
evaluate_markers = evaluate_markers or (lambda _ctx, _requirements: {})
9090
options = {}
9191
requirements = {}
92+
all_files_parsed = {}
9293
reqs_with_env_markers = {}
9394
index_url = None
9495
extra_index_urls = []
@@ -100,6 +101,13 @@ def parse_requirements(
100101
# needed for the whl_library declarations later.
101102
parse_result = parse_requirements_txt(contents)
102103

104+
# Save parsed results from ALL files, even those with no matching
105+
# platforms. This ensures the distributions dict (used for index URL
106+
# queries) includes packages from all platform files, making the
107+
# lockfile facts platform-independent.
108+
if file not in all_files_parsed:
109+
all_files_parsed[file] = parse_result.requirements
110+
103111
tokenized_options = []
104112
for opt in parse_result.options:
105113
for p in opt.split(" "):
@@ -185,13 +193,21 @@ def parse_requirements(
185193

186194
index_urls = {}
187195
if get_index_urls:
196+
# Collect all distributions from all requirements files irrespective
197+
# of python_version and platform markers. This ensures that the index
198+
# is queried for all packages, not just those matching the current
199+
# platform's markers.
188200
distributions = {}
189-
for reqs in requirements_by_platform.values():
190-
for req in reqs.values():
191-
if req.srcs.url:
201+
for entries in all_files_parsed.values():
202+
for entry in entries:
203+
name, req_line = entry
204+
srcs = index_sources(req_line)
205+
if srcs.url:
192206
continue
207+
versions = distributions.setdefault(normalize_name(name), {})
208+
versions[srcs.version] = None
193209

194-
distributions.setdefault(req.distribution, []).append(req.srcs.version)
210+
distributions = {k: sorted(v.keys()) for k, v in distributions.items()}
195211

196212
index_urls = get_index_urls(
197213
ctx,

python/private/pypi/pypi_cache.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ def _get_from_facts(facts, known_facts, index_url, requested_versions, facts_ver
212212
requested_versions = requested_versions,
213213
)
214214
if result:
215+
if len(result) != len(requested_versions):
216+
# If the results are incomplete, return None, so that we can
217+
# fetch sources from the internet again.
218+
return None
219+
220+
# Only persist the accessed (requested) packages. Packages that
221+
# exist in known_facts but are not in requested_versions (e.g.
222+
# removed from all requirements files) are dropped from the
223+
# computed facts so they get cleaned up from the lockfile.
215224
_store_facts(facts, facts_version, index_url, result)
216225
return result
217226

python/private/pypi/requirements_files_by_platform.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ def requirements_files_by_platform(
213213
default_platforms,
214214
input_platforms,
215215
))
216-
continue
217216

218217
if logger:
219218
logger.debug(lambda: "Configured platforms for file {} are {}".format(file, plats))
@@ -238,4 +237,8 @@ def requirements_files_by_platform(
238237
for plat, file in requirements.items():
239238
ret.setdefault(file, []).append(_platform(plat, python_version = python_version))
240239

240+
for file, _plats in files_by_platform:
241+
if file not in ret and _plats != None:
242+
ret[file] = []
243+
241244
return ret

tests/integration/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ default_test_runner(
3737
visibility = ["//visibility:public"],
3838
)
3939

40+
rules_python_integration_test(
41+
name = "bzlmod_lockfile_test",
42+
bazel_versions = ["9.1.0"],
43+
)
44+
45+
test_suite(
46+
name = "subset",
47+
tags = ["manual"],
48+
tests = [
49+
"bzlmod_lockfile_test_bazel_9.1.0",
50+
"local_toolchains_test_bazel_self",
51+
],
52+
)
53+
4054
rules_python_integration_test(
4155
name = "compile_pip_requirements_test",
4256
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Bazel configuration flags
2+
3+
build --enable_runfiles
4+
common --lockfile_mode=error
5+
6+
common --experimental_isolated_extension_usages
7+
8+
# https://docs.bazel.build/versions/main/best-practices.html#using-the-bazelrc-file
9+
try-import %workspace%/user.bazelrc

0 commit comments

Comments
 (0)