Skip to content

Commit 45821b8

Browse files
authored
fix(pip): do not add a pip-fallback when there is no sdist (#3432)
Before this PR the user would have to specify `download = True` in order to not fallback to pip, which is admittedly an odd interface design. With this PR we correctly do not add a `pip` fallback if there is no `sdist` to be used. With this in place we are better placed to enable the `experimental_index_url` by default. What is more we can more reliably detect when we should use a special repository rule to prepare for building from sdist. Summary: - Add a test that shows the problem and then adjust the torch experimental URL test. - Cleanup unused marker stubs in the tests. - Fix the code to better handle the case when there is no sdist. Work towards #260 Work towards #2410
1 parent a9056b1 commit 45821b8

File tree

2 files changed

+88
-37
lines changed

2 files changed

+88
-37
lines changed

python/private/pypi/parse_requirements.bzl

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,35 @@ def parse_requirements(
188188
for p in r.target_platforms:
189189
requirement_target_platforms[p] = None
190190

191+
package_srcs = _package_srcs(
192+
name = name,
193+
reqs = reqs,
194+
index_urls = index_urls,
195+
platforms = platforms,
196+
extract_url_srcs = extract_url_srcs,
197+
logger = logger,
198+
)
199+
200+
# FIXME @aignas 2025-11-24: we can get the list of target platforms here
201+
#
202+
# However it is likely that we may stop exposing packages like torch in here
203+
# which do not have wheels for all osx platforms.
204+
#
205+
# If users specify the target platforms accurately, then it is a different
206+
# (better) story, but we may not be able to guarantee this
207+
#
208+
# target_platforms = [
209+
# p
210+
# for dist in package_srcs
211+
# for p in dist.target_platforms
212+
# ]
213+
191214
item = struct(
192215
# Return normalized names
193216
name = normalize_name(name),
194217
is_exposed = len(requirement_target_platforms) == len(requirements),
195218
is_multiple_versions = len(reqs.values()) > 1,
196-
srcs = _package_srcs(
197-
name = name,
198-
reqs = reqs,
199-
index_urls = index_urls,
200-
platforms = platforms,
201-
extract_url_srcs = extract_url_srcs,
202-
logger = logger,
203-
),
219+
srcs = package_srcs,
204220
)
205221
ret.append(item)
206222
if not item.is_exposed and logger:
@@ -234,7 +250,7 @@ def _package_srcs(
234250
platforms.keys(),
235251
))
236252

237-
dist = _add_dists(
253+
dist, can_fallback = _add_dists(
238254
requirement = r,
239255
target_platform = platforms.get(target_platform),
240256
index_urls = index_urls.get(name),
@@ -244,14 +260,16 @@ def _package_srcs(
244260

245261
if extract_url_srcs and dist:
246262
req_line = r.srcs.requirement
247-
else:
263+
elif can_fallback:
248264
dist = struct(
249265
url = "",
250266
filename = "",
251267
sha256 = "",
252268
yanked = False,
253269
)
254270
req_line = r.srcs.requirement_line
271+
else:
272+
continue
255273

256274
key = (
257275
dist.filename,
@@ -337,14 +355,22 @@ def _add_dists(*, requirement, index_urls, target_platform, logger = None):
337355
index_urls: The result of simpleapi_download.
338356
target_platform: The target_platform information.
339357
logger: A logger for printing diagnostic info.
358+
359+
Returns:
360+
(dist, can_fallback_to_pip): a struct with distribution details and how to fetch
361+
it and a boolean flag to tell the other layers if we should add an entry to
362+
fallback for pip if there are no supported whls found - if there is an sdist, we
363+
can attempt the fallback, otherwise better to not, because the pip command will
364+
fail and the error message will be confusing. What is more that would lead to
365+
breakage of the bazel query.
340366
"""
341367

342368
if requirement.srcs.url:
343369
if not requirement.srcs.filename:
344370
logger.debug(lambda: "Could not detect the filename from the URL, falling back to pip: {}".format(
345371
requirement.srcs.url,
346372
))
347-
return None
373+
return None, True
348374

349375
# Handle direct URLs in requirements
350376
dist = struct(
@@ -354,13 +380,10 @@ def _add_dists(*, requirement, index_urls, target_platform, logger = None):
354380
yanked = False,
355381
)
356382

357-
if dist.filename.endswith(".whl"):
358-
return dist
359-
else:
360-
return dist
383+
return dist, False
361384

362385
if not index_urls:
363-
return None
386+
return None, True
364387

365388
whls = []
366389
sdist = None
@@ -403,7 +426,14 @@ def _add_dists(*, requirement, index_urls, target_platform, logger = None):
403426

404427
if not target_platform:
405428
# The pipstar platforms are undefined here, so we cannot do any matching
406-
return sdist
429+
return sdist, True
430+
431+
if not whls and not sdist:
432+
# If there are no suitable wheels to handle for now allow fallback to pip, it
433+
# may be a little bit more helpful when debugging? Most likely something is
434+
# going a bit wrong here, should we raise an error because the sha256 have most
435+
# likely mismatched? We are already printing a warning above.
436+
return None, True
407437

408438
# Select a single wheel that can work on the target_platform
409439
return select_whl(
@@ -413,4 +443,4 @@ def _add_dists(*, requirement, index_urls, target_platform, logger = None):
413443
whl_abi_tags = target_platform.whl_abi_tags,
414444
whl_platform_tags = target_platform.whl_platform_tags,
415445
logger = logger,
416-
) or sdist
446+
) or sdist, sdist != None

tests/pypi/hub_builder/hub_builder_tests.bzl

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,9 @@ def _test_torch_experimental_index_url(env):
566566
for (os, cpu), whl_platform_tags in {
567567
("linux", "x86_64"): ["linux_x86_64", "manylinux_*_x86_64"],
568568
("linux", "aarch64"): ["linux_aarch64", "manylinux_*_aarch64"],
569+
# this should be ignored as well because there is no sdist and no whls
570+
# for intel Macs
571+
("osx", "x86_64"): ["macosx_*_x86_64"],
569572
("osx", "aarch64"): ["macosx_*_arm64"],
570573
("windows", "x86_64"): ["win_amd64"],
571574
("windows", "aarch64"): ["win_arm64"], # this should be ignored
@@ -576,15 +579,6 @@ def _test_torch_experimental_index_url(env):
576579
"python_3_12_host": "unit_test_interpreter_target",
577580
},
578581
minor_mapping = {"3.12": "3.12.19"},
579-
evaluate_markers_fn = lambda _, requirements, **__: {
580-
# todo once 2692 is merged, this is going to be easier to test.
581-
key: [
582-
platform
583-
for platform in platforms
584-
if ("x86_64" in platform and "platform_machine ==" in key) or ("x86_64" not in platform and "platform_machine !=" in key)
585-
]
586-
for key, platforms in requirements.items()
587-
},
588582
simpleapi_download_fn = mocksimpleapi_download,
589583
)
590584
builder.pip_parse(
@@ -621,7 +615,6 @@ torch==2.4.1+cpu ; platform_machine == 'x86_64' \
621615
_parse(
622616
hub_name = "pypi",
623617
python_version = "3.12",
624-
download_only = True,
625618
experimental_index_url = "https://torch.index",
626619
requirements_lock = "universal.txt",
627620
),
@@ -800,6 +793,20 @@ def _test_simple_get_index(env):
800793
got_simpleapi_download_args.extend(args)
801794
got_simpleapi_download_kwargs.update(kwargs)
802795
return {
796+
"plat_pkg": struct(
797+
whls = {
798+
"deadb44f": struct(
799+
yanked = False,
800+
filename = "plat-pkg-0.0.4-py3-none-linux_x86_64.whl",
801+
sha256 = "deadb44f",
802+
url = "example2.org/index/plat_pkg/",
803+
),
804+
},
805+
sdists = {},
806+
sha256s_by_version = {
807+
"0.0.4": ["deadb44f"],
808+
},
809+
),
803810
"simple": struct(
804811
whls = {
805812
"deadb00f": struct(
@@ -850,6 +857,7 @@ some_pkg==0.0.1 @ example-direct.org/some_pkg-0.0.1-py3-none-any.whl \
850857
--hash=sha256:deadbaaf
851858
direct_without_sha==0.0.1 @ example-direct.org/direct_without_sha-0.0.1-py3-none-any.whl
852859
some_other_pkg==0.0.1
860+
plat_pkg==0.0.4
853861
pip_fallback==0.0.1
854862
direct_sdist_without_sha @ some-archive/any-name.tar.gz
855863
git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
@@ -873,6 +881,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
873881
"direct_without_sha",
874882
"git_dep",
875883
"pip_fallback",
884+
"plat_pkg",
876885
"simple",
877886
"some_other_pkg",
878887
"some_pkg",
@@ -921,6 +930,17 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
921930
),
922931
],
923932
},
933+
"plat_pkg": {
934+
"pypi_315_plat_py3_none_linux_x86_64_deadb44f": [
935+
whl_config_setting(
936+
target_platforms = [
937+
"cp315_linux_x86_64",
938+
"cp315_linux_x86_64_freethreaded",
939+
],
940+
version = "3.15",
941+
),
942+
],
943+
},
924944
"simple": {
925945
"pypi_315_simple_py3_none_any_deadb00f": [
926946
whl_config_setting(
@@ -998,6 +1018,15 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
9981018
"python_interpreter_target": "unit_test_interpreter_target",
9991019
"requirement": "pip_fallback==0.0.1",
10001020
},
1021+
"pypi_315_plat_py3_none_linux_x86_64_deadb44f": {
1022+
"config_load": "@pypi//:config.bzl",
1023+
"dep_template": "@pypi//{name}:{target}",
1024+
"filename": "plat-pkg-0.0.4-py3-none-linux_x86_64.whl",
1025+
"python_interpreter_target": "unit_test_interpreter_target",
1026+
"requirement": "plat_pkg==0.0.4",
1027+
"sha256": "deadb44f",
1028+
"urls": ["example2.org/index/plat_pkg/"],
1029+
},
10011030
"pypi_315_simple_py3_none_any_deadb00f": {
10021031
"config_load": "@pypi//:config.bzl",
10031032
"dep_template": "@pypi//{name}:{target}",
@@ -1036,7 +1065,7 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10361065
index_url = "pypi.org",
10371066
index_url_overrides = {},
10381067
netrc = None,
1039-
sources = ["simple", "pip_fallback", "some_other_pkg"],
1068+
sources = ["simple", "plat_pkg", "pip_fallback", "some_other_pkg"],
10401069
),
10411070
"cache": {},
10421071
"parallel_download": False,
@@ -1048,14 +1077,6 @@ _tests.append(_test_simple_get_index)
10481077
def _test_optimum_sys_platform_extra(env):
10491078
builder = hub_builder(
10501079
env,
1051-
evaluate_markers_fn = lambda _, requirements, **__: {
1052-
key: [
1053-
platform
1054-
for platform in platforms
1055-
if ("darwin" in key and "osx" in platform) or ("linux" in key and "linux" in platform)
1056-
]
1057-
for key, platforms in requirements.items()
1058-
},
10591080
)
10601081
builder.pip_parse(
10611082
_mock_mctx(

0 commit comments

Comments
 (0)