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
6 changes: 6 additions & 0 deletions news/4184.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
(pypi) A {obj}`pip.parse` call for a Python version that has no interpreter for
the host platform is now skipped, like a version with no registered toolchain,
instead of failing the whole {obj}`pip` extension. A dependency that lists,
say, Python 3.9 no longer breaks every other hub on hosts that have no CPython
3.9 build, such as Windows on ARM64
([#4184](https://github.com/bazel-contrib/rules_python/issues/4184)).
26 changes: 23 additions & 3 deletions python/private/pypi/hub_builder.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,25 @@ def _pip_parse(self, module_ctx, pip_attr, python_version = None):
))
return

if (pip_attr.python_interpreter_target == None and
not pip_attr.python_interpreter and
_host_interpreter_name(python_version) not in self._available_interpreters):
# The version has a toolchain, but none of its interpreters runs on
# this host, e.g. there is no CPython build for this os and cpu. Treat
# it like a version missing from minor_mapping instead of failing the
# extension, which would also take down every other version and hub,
# including ones this host can use.
self._logger.info(lambda: (
"Ignoring pip python version '{version}' for hub " +
"'{hub}' in module '{module}' because there is no " +
"interpreter for it that runs on this host."
).format(
hub = self.name,
module = self.module_name,
version = python_version,
))
return

_set_get_index_urls(self, module_ctx, pip_attr)
self._platforms[python_version] = _platforms(
module_ctx,
Expand Down Expand Up @@ -395,12 +414,13 @@ def _set_get_index_urls(self, mctx, pip_attr):
)
return True

def _host_interpreter_name(python_version):
return "python_{}_host".format(python_version.replace(".", "_"))

def _detect_interpreter(self, pip_attr, python_version):
python_interpreter_target = pip_attr.python_interpreter_target
if python_interpreter_target == None and not pip_attr.python_interpreter:
python_name = "python_{}_host".format(
python_version.replace(".", "_"),
)
python_name = _host_interpreter_name(python_version)
if python_name not in self._available_interpreters:
fail((
"Unable to find interpreter for pip hub '{hub_name}' for " +
Expand Down
69 changes: 69 additions & 0 deletions tests/pypi/hub_builder/hub_builder_tests.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,75 @@ new-package==0.0.1 --hash=sha256:deadb00f2

_tests.append(_test_simple_multiple_python_versions)

def _test_skip_python_version_without_host_interpreter(env):
# A module can ask for a Python version that has a toolchain but no
# interpreter for this host, e.g. a dependency listing 3.9 when rules_python
# ships no 3.9 CPython for aarch64-pc-windows-msvc. That version must be
# skipped instead of failing the whole extension, like a version missing
# from minor_mapping is.
builder = hub_builder(
env,
available_interpreters = {
"python_3_15_host": "unit_test_interpreter_target",
},
minor_mapping = {
"3.15": "3.15.19",
"3.16": "3.16.9",
},
)
builder.pip_parse(
mocks.mctx(
mock_files = {
"requirements_3_15.txt": """
simple==0.0.1 --hash=sha256:deadbeef
""",
},
os_name = "linux",
arch_name = "amd64",
),
_parse(
hub_name = "pypi",
python_version = "3.15",
requirements_lock = "requirements_3_15.txt",
),
)
builder.pip_parse(
mocks.mctx(
mock_files = {
"requirements_3_16.txt": """
simple==0.0.2 --hash=sha256:deadb00f
""",
},
os_name = "linux",
arch_name = "amd64",
),
_parse(
hub_name = "pypi",
python_version = "3.16",
requirements_lock = "requirements_3_16.txt",
),
)
pypi = builder.build()

pypi.exposed_packages().contains_exactly(["simple"])
pypi.whl_map().contains_exactly({
"simple": {
"pypi_315_simple": [
whl_config_setting(version = "3.15"),
],
},
})
pypi.whl_libraries().contains_exactly({
"pypi_315_simple": {
"config_load": "@pypi//:config.bzl",
"dep_template": "@pypi//{name}:{target}",
"python_interpreter_target": "unit_test_interpreter_target",
"requirement": "simple==0.0.1 --hash=sha256:deadbeef",
},
})

_tests.append(_test_skip_python_version_without_host_interpreter)

def _test_simple_with_markers(env):
sub_tests = {
("osx", "aarch64"): "torch==2.4.1 --hash=sha256:deadbeef",
Expand Down
Loading