Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -472,17 +472,17 @@ tasks:
<<: *common_bazelinbazel_config
name: "tests/integration bazel-in-bazel: macOS (subset)"
platform: macos_arm64
build_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
test_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
build_targets: ["//tests/integration:subset"]
test_targets: ["//tests/integration:subset"]
# The bazelinbazel tests were disabled on Windows to save CI jobs slots, and
# have bitrotted a bit. For now, just run a subset of what we're most
# interested in.
integration_test_bazelinbazel_windows:
<<: *common_bazelinbazel_config
name: "tests/integration bazel-in-bazel: Windows (subset)"
platform: windows
build_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
test_targets: ["//tests/integration:local_toolchains_test_bazel_self"]
build_targets: ["//tests/integration:subset"]
test_targets: ["//tests/integration:subset"]

integration_test_compile_pip_requirements_ubuntu:
<<: *reusable_build_test_all
Expand Down
1 change: 1 addition & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ gazelle/examples/bzlmod_build_file_generation/bazel-bzlmod_build_file_generation
gazelle/examples/bzlmod_build_file_generation/bazel-out
gazelle/examples/bzlmod_build_file_generation/bazel-testlog
sphinxdocs
tests/integration/bzlmod_lockfile/bazel-bzlmod_lockfile
tests/integration/compile_pip_requirements/bazel-compile_pip_requirements
tests/integration/local_toolchains/bazel-local_toolchains
tests/integration/py_cc_toolchain_registered/bazel-py_cc_toolchain_registered
Expand Down
1 change: 1 addition & 0 deletions .bazelrc.deleted_packages
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ common --deleted_packages=gazelle/modules_mapping
common --deleted_packages=gazelle/python
common --deleted_packages=gazelle/pythonconfig
common --deleted_packages=gazelle/python/private
common --deleted_packages=tests/integration/bzlmod_lockfile
common --deleted_packages=tests/integration/compile_pip_requirements
common --deleted_packages=tests/integration/compile_pip_requirements_test_from_external_repo
common --deleted_packages=tests/integration/custom_commands
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ END_UNRELEASED_TEMPLATE
[20260325]: https://github.com/astral-sh/python-build-standalone/releases/tag/20260325
[20260414]: https://github.com/astral-sh/python-build-standalone/releases/tag/20260414

{#v2-0-1}
## [2.0.1] - 2026-05-08

[2.0.1]: https://github.com/bazel-contrib/rules_python/releases/tag/2.0.1

{#v2-0-1-fixed}
### Fixed

* (pypi) Fix the versions of packages that we are recording to a `MODULE.bazel.lock` file
facts by passing all of the versions to the `get_index` function.
Fixes [#3756](https://github.com/bazel-contrib/rules_python/issues/3756).

{#v2-0-0}
## [2.0.0] - 2026-04-09

Expand Down
4 changes: 2 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ bazel_binaries.local(
)
bazel_binaries.download(version = "7.7.0")
bazel_binaries.download(version = "8.5.1")
bazel_binaries.download(version = "9.0.0rc1")
bazel_binaries.download(version = "9.1.0")
use_repo(
bazel_binaries,
"bazel_binaries",
Expand All @@ -235,7 +235,7 @@ use_repo(
"bazel_binaries_bazelisk",
"build_bazel_bazel_7_7_0",
"build_bazel_bazel_8_5_1",
"build_bazel_bazel_9_0_0rc1",
"build_bazel_bazel_9_1_0",
# "build_bazel_bazel_rolling",
"build_bazel_bazel_self",
)
Expand Down
28 changes: 24 additions & 4 deletions python/private/pypi/parse_requirements.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def parse_requirements(
evaluate_markers = evaluate_markers or (lambda _requirements: {})
options = {}
requirements = {}
all_files_parsed = {}
reqs_with_env_markers = {}
index_url = None
extra_index_urls = []
Expand All @@ -100,6 +101,13 @@ def parse_requirements(
# needed for the whl_library declarations later.
parse_result = parse_requirements_txt(contents)

# Save parsed results from ALL files, even those with no matching
# platforms. This ensures the distributions dict (used for index URL
# queries) includes packages from all platform files, making the
# lockfile facts platform-independent.
if file not in all_files_parsed:
all_files_parsed[file] = parse_result.requirements

tokenized_options = []
for opt in parse_result.options:
for p in opt.split(" "):
Expand Down Expand Up @@ -130,6 +138,10 @@ def parse_requirements(

# This may call to Python, so execute it early (before calling to the
# internet below) and ensure that we call it only once.
#
# TODO @aignas 2026-05-10: remove this assumption in the code because we
# are always using pipstar, so we can do the marker evaluation when we are
# parsing the files.
resolved_marker_platforms = evaluate_markers(reqs_with_env_markers)
logger.trace(lambda: "Evaluated env markers from:\n{}\n\nTo:\n{}".format(
reqs_with_env_markers,
Expand Down Expand Up @@ -185,13 +197,21 @@ def parse_requirements(

index_urls = {}
if get_index_urls:
# Collect all distributions from all requirements files irrespective
# of python_version and platform markers. This ensures that the index
# is queried for all packages, not just those matching the current
# platform's markers.
distributions = {}
for reqs in requirements_by_platform.values():
for req in reqs.values():
if req.srcs.url:
for entries in all_files_parsed.values():
for entry in entries:
name, req_line = entry
srcs = index_sources(req_line)
if srcs.url:
continue
versions = distributions.setdefault(normalize_name(name), {})
versions[srcs.version] = None

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

index_urls = get_index_urls(
ctx,
Expand Down
9 changes: 9 additions & 0 deletions python/private/pypi/pypi_cache.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,15 @@ def _get_from_facts(facts, known_facts, index_url, requested_versions, facts_ver
requested_versions = requested_versions,
)
if result:
if len(result) != len(requested_versions):
# If the results are incomplete, return None, so that we can
# fetch sources from the internet again.
return None

# Only persist the accessed (requested) packages. Packages that
# exist in known_facts but are not in requested_versions (e.g.
# removed from all requirements files) are dropped from the
# computed facts so they get cleaned up from the lockfile.
_store_facts(facts, facts_version, index_url, result)
return result

Expand Down
5 changes: 4 additions & 1 deletion python/private/pypi/requirements_files_by_platform.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ def requirements_files_by_platform(
default_platforms,
input_platforms,
))
continue

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

for file, _plats in files_by_platform:
if file not in ret and _plats != None:
ret[file] = []

return ret
14 changes: 14 additions & 0 deletions tests/integration/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ default_test_runner(
visibility = ["//visibility:public"],
)

rules_python_integration_test(
name = "bzlmod_lockfile_test",
bazel_versions = ["9.1.0"],
)

test_suite(
name = "subset",
tags = ["manual"],
tests = [
"bzlmod_lockfile_test_bazel_9.1.0",
"local_toolchains_test_bazel_self",
],
)

rules_python_integration_test(
name = "compile_pip_requirements_test",
)
Expand Down
9 changes: 9 additions & 0 deletions tests/integration/bzlmod_lockfile/.bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Bazel configuration flags

build --enable_runfiles
common --lockfile_mode=error

common --experimental_isolated_extension_usages

# https://docs.bazel.build/versions/main/best-practices.html#using-the-bazelrc-file
try-import %workspace%/user.bazelrc
1 change: 1 addition & 0 deletions tests/integration/bzlmod_lockfile/.bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9.1.0
10 changes: 10 additions & 0 deletions tests/integration/bzlmod_lockfile/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@rules_python//python:py_test.bzl", "py_test")

py_test(
name = "test_dummy",
srcs = ["test_dummy.py"],
deps = [
"@pypi//pycparser",
"@pypi//six",
],
)
19 changes: 19 additions & 0 deletions tests/integration/bzlmod_lockfile/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module(name = "bzlmod_lockfile")

bazel_dep(name = "rules_python")
local_path_override(
module_name = "rules_python",
path = "../../..",
)

python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.13")

# TODO: This test module should also verify that isolate = True works, will do in a followup PR.
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
hub_name = "pypi",
python_version = "3.13",
requirements_lock = "//:requirements_lock.txt",
)
use_repo(pip, "pypi")
Loading