Skip to content
Open
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
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
16 changes: 12 additions & 4 deletions python/private/pypi/parse_requirements.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,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 _plat, parse_results in requirements.items():
for entry in parse_results:
req_line = entry[1]
srcs = index_sources(req_line)
if srcs.url:
continue
versions = distributions.setdefault(entry[0], {})
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
4 changes: 4 additions & 0 deletions python/private/pypi/pypi_cache.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ 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
_store_facts(facts, facts_version, index_url, result)
return result

Expand Down
1 change: 1 addition & 0 deletions tests/integration/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ rules_python_integration_test(

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

rules_python_integration_test(
Expand Down
1 change: 1 addition & 0 deletions tests/integration/pip_parse_isolated/.bazelrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Bazel configuration flags

build --enable_runfiles
common --lockfile_mode=error

common --experimental_isolated_extension_usages

Expand Down
1 change: 1 addition & 0 deletions tests/integration/pip_parse_isolated/.bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9.1.0
692 changes: 692 additions & 0 deletions tests/integration/pip_parse_isolated/MODULE.bazel.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions tests/integration/pip_parse_isolated/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Pip parse isolation and lock file test

Update the lock file with the following command:
```
bazel mod deps --lockfile_mode=update
```
6 changes: 6 additions & 0 deletions tests/integration/pip_parse_isolated/requirements_lock.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274
# rules_python v2.0.0's own tools/publish/requirements_linux.txt pins pycparser==2.23.
# On macOS: lock gets pycparser-3.0 only (darwin publish requirements have no pycparser).
# On Linux: rules_python_publish_deps hub adds pycparser-2.23 to computed facts → mismatch.
pycparser==3.0 \
--hash=sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29 \
--hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992
65 changes: 65 additions & 0 deletions tests/pypi/parse_requirements/parse_requirements_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ foo==0.0.1; python_full_version < '3.10.0' \
--hash=sha256:deadbeef
foo==0.0.2; python_full_version >= '3.10.0' \
--hash=sha256:deadb11f
boo==0.0.4; python_full_version < '3.10.0' \
--hash=sha256:deadbaaf
""",
"requirements_optional_hash": """
bar==0.0.4 @ https://example.org/bar-0.0.4.whl
Expand Down Expand Up @@ -795,6 +797,24 @@ def _test_get_index_urls_different_versions(env):
)

env.expect.that_collection(got).contains_exactly([
struct(
name = "boo",
index_url = "",
is_exposed = False,
is_multiple_versions = False,
srcs = [
struct(
distribution = "boo",
extra_pip_args = [],
filename = "",
requirement_line = "boo==0.0.4 --hash=sha256:deadbaaf",
sha256 = "",
target_platforms = ["cp39_linux_x86_64"],
url = "",
yanked = None,
),
],
),
struct(
name = "foo",
index_url = "",
Expand Down Expand Up @@ -892,6 +912,51 @@ def _test_get_index_urls_single_py_version(env):

_tests.append(_test_get_index_urls_single_py_version)

def _test_get_index_urls_all_versions(env):
calls = []

def _get_index_urls(_, distributions, **__):
calls.append({k: list(v) for k, v in distributions.items()})
return {}

parse_requirements(
requirements_by_platform = {
"requirements_multi_version": ["cp39_linux_x86_64"],
},
platforms = {
"cp39_linux_x86_64": struct(
env = pep508_env(
python_version = "3.9.0",
os = "linux",
arch = "x86_64",
),
whl_abi_tags = ["none"],
whl_platform_tags = ["any"],
),
},
get_index_urls = _get_index_urls,
evaluate_markers = lambda requirements: evaluate_markers(
requirements = requirements,
platforms = {
"cp39_linux_x86_64": struct(
env = {"python_full_version": "3.9.0"},
),
},
),
)

env.expect.that_collection(calls).contains_exactly([
{
# boo should be also passed even though it is present on one platform.
"boo": ["0.0.4"],
# Both versions 0.0.1 and 0.0.2 should be passed to get_index_urls, even
# though only 0.0.1 matches the cp39_linux_x86_64 platform markers.
"foo": ["0.0.1", "0.0.2"],
},
])

_tests.append(_test_get_index_urls_all_versions)

def parse_requirements_test_suite(name):
"""Create the test suite.

Expand Down
18 changes: 18 additions & 0 deletions tests/pypi/pypi_cache/pypi_cache_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,24 @@ def _test_pypi_cache_reads_index_urls_from_facts(env):

_tests.append(_test_pypi_cache_reads_index_urls_from_facts)

def _test_pypi_cache_reads_index_urls_from_facts_incomplete(env):
"""Verifies that incomplete index_urls facts returns None (forces fresh download)."""
mock_ctx = mocks.mctx(facts = {
"fact_version": "v1",
"index_urls": {
"https://pypi.org/simple/": {
"pkg-a": "https://pypi.org/simple/pkg-a/",
},
},
})
cache = _cache(env, mctx = mock_ctx)

# Request pkg-a and pkg-b, but facts only have pkg-a
key = ("https://pypi.org/simple/", "https://pypi.org/simple/", {"pkg-a": None, "pkg-b": None})
cache.get(key).equals(None)

_tests.append(_test_pypi_cache_reads_index_urls_from_facts_incomplete)

def pypi_cache_test_suite(name):
test_suite(
name = name,
Expand Down