Skip to content

Commit 89fedb7

Browse files
authored
refactor(pypi): extract the wheel without python (#3430)
With this we start extracting the wheel without Python and it becomes a requirement only when patching (we will extract the wheel without Python, patch it and then re-compress it which makes a very inefficient process). This should result in much faster executions because we can start extracting wheels even before we fetch the entire Python toolchain and we don't need to fetch it in a wheel-only setup until we are actually building/executing tests. What is more bazel is faster in extracting everything. Work towards #2948
1 parent 0737a0b commit 89fedb7

File tree

5 files changed

+36
-29
lines changed

5 files changed

+36
-29
lines changed

python/private/internal_config_repo.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ config = struct(
3232
enable_pystar = True,
3333
enable_pipstar = {enable_pipstar},
3434
enable_deprecation_warnings = {enable_deprecation_warnings},
35+
bazel_8_or_later = {bazel_8_or_later},
3536
bazel_9_or_later = {bazel_9_or_later},
3637
BuiltinPyInfo = getattr(getattr(native, "legacy_globals", None), "PyInfo", {builtin_py_info_symbol}),
3738
BuiltinPyRuntimeInfo = getattr(getattr(native, "legacy_globals", None), "PyRuntimeInfo", {builtin_py_runtime_info_symbol}),
@@ -107,6 +108,7 @@ def _internal_config_repo_impl(rctx):
107108
builtin_py_info_symbol = builtin_py_info_symbol,
108109
builtin_py_runtime_info_symbol = builtin_py_runtime_info_symbol,
109110
builtin_py_cc_link_params_provider = builtin_py_cc_link_params_provider,
111+
bazel_8_or_later = str(bazel_major_version >= 8),
110112
bazel_9_or_later = str(bazel_major_version >= 9),
111113
))
112114

python/private/pypi/hub_builder.bzl

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ def _create_whl_repos(
440440
pip_attr = pip_attr,
441441
enable_pipstar = enable_pipstar,
442442
)
443+
444+
interpreter = _detect_interpreter(self, pip_attr)
445+
443446
for whl in requirements_by_platform:
444447
whl_library_args = common_args | _whl_library_args(
445448
self,
@@ -456,6 +459,7 @@ def _create_whl_repos(
456459
auth_patterns = self._config.auth_patterns or pip_attr.auth_patterns,
457460
python_version = _major_minor_version(pip_attr.python_version),
458461
is_multiple_versions = whl.is_multiple_versions,
462+
interpreter = interpreter,
459463
enable_pipstar = enable_pipstar,
460464
)
461465
_add_whl_library(
@@ -467,8 +471,6 @@ def _create_whl_repos(
467471
)
468472

469473
def _common_args(self, module_ctx, *, pip_attr, enable_pipstar):
470-
interpreter = _detect_interpreter(self, pip_attr)
471-
472474
# Construct args separately so that the lock file can be smaller and does not include unused
473475
# attrs.
474476
whl_library_args = dict(
@@ -483,8 +485,6 @@ def _common_args(self, module_ctx, *, pip_attr, enable_pipstar):
483485
environment = pip_attr.environment,
484486
envsubst = pip_attr.envsubst,
485487
pip_data_exclude = pip_attr.pip_data_exclude,
486-
python_interpreter = interpreter.path,
487-
python_interpreter_target = interpreter.target,
488488
)
489489
if not enable_pipstar:
490490
maybe_args["experimental_target_platforms"] = pip_attr.experimental_target_platforms
@@ -536,6 +536,7 @@ def _whl_repo(
536536
auth_patterns,
537537
python_version,
538538
use_downloader,
539+
interpreter,
539540
enable_pipstar = False):
540541
args = dict(whl_library_args)
541542
args["requirement"] = src.requirement_line
@@ -548,6 +549,12 @@ def _whl_repo(
548549
# need to pass the extra args there, so only pop this for whls
549550
args["extra_pip_args"] = src.extra_pip_args
550551

552+
if "whl_patches" in args or not (enable_pipstar and is_whl):
553+
if interpreter.path:
554+
args["python_interpreter"] = interpreter.path
555+
if interpreter.target:
556+
args["python_interpreter_target"] = interpreter.target
557+
551558
if not src.url or (not is_whl and download_only):
552559
if download_only and use_downloader:
553560
# If the user did not allow using sdists and we are using the downloader

python/private/pypi/patch_whl.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ def patch_whl(rctx, *, python_interpreter, whl_path, patches, **kwargs):
8787
# symlink to a zip file to use bazel's extract so that we can use bazel's
8888
# repository_ctx patch implementation. The whl file may be in a different
8989
# external repository.
90+
#
91+
# TODO @aignas 2025-11-24: remove this symlinking workaround when we drop support for bazel 7
9092
whl_file_zip = whl_input.basename + ".zip"
9193
rctx.symlink(whl_input, whl_file_zip)
9294
rctx.extract(whl_file_zip)

python/private/pypi/whl_library.bzl

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -377,21 +377,17 @@ def _whl_library_impl(rctx):
377377
#
378378
# Remove non-pipstar and config_load check when we release rules_python 2.
379379
if enable_pipstar:
380-
pypi_repo_utils.execute_checked(
381-
rctx,
382-
op = "whl_library.ExtractWheel({}, {})".format(rctx.attr.name, whl_path),
383-
python = python_interpreter,
384-
arguments = args + [
385-
"--whl-file",
386-
whl_path,
387-
"--enable-pipstar",
388-
],
389-
srcs = rctx.attr._python_srcs,
390-
environment = environment,
391-
quiet = rctx.attr.quiet,
392-
timeout = rctx.attr.timeout,
393-
logger = logger,
380+
if rp_config.bazel_8_or_later:
381+
extract_path = whl_path
382+
else:
383+
extract_path = rctx.path(whl_path.basename + ".zip")
384+
rctx.symlink(whl_path, extract_path)
385+
rctx.extract(
386+
archive = extract_path,
387+
output = "site-packages",
394388
)
389+
if not rp_config.bazel_8_or_later:
390+
rctx.delete(extract_path)
395391

396392
metadata = whl_metadata(
397393
install_dir = whl_path.dirname.get_child("site-packages"),

tests/pypi/hub_builder/hub_builder_tests.bzl

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ def hub_builder(
4444
debug = False,
4545
config = None,
4646
minor_mapping = {},
47+
whl_overrides = {},
4748
evaluate_markers_fn = None,
4849
simpleapi_download_fn = None,
4950
available_interpreters = {}):
@@ -76,7 +77,7 @@ def hub_builder(
7677
netrc = None,
7778
auth_patterns = None,
7879
),
79-
whl_overrides = {},
80+
whl_overrides = whl_overrides,
8081
minor_mapping = minor_mapping or {"3.15": "3.15.19"},
8182
available_interpreters = available_interpreters or {
8283
"python_3_15_host": "unit_test_interpreter_target",
@@ -320,7 +321,6 @@ def _test_simple_extras_vs_no_extras_simpleapi(env):
320321
"config_load": "@pypi//:config.bzl",
321322
"dep_template": "@pypi//{name}:{target}",
322323
"filename": "simple-0.0.1-py3-none-any.whl",
323-
"python_interpreter_target": "unit_test_interpreter_target",
324324
"requirement": "simple[foo]==0.0.1",
325325
"sha256": "deadbeef",
326326
"urls": ["https://example.com/simple-0.0.1-py3-none-any.whl"],
@@ -329,7 +329,6 @@ def _test_simple_extras_vs_no_extras_simpleapi(env):
329329
"config_load": "@pypi//:config.bzl",
330330
"dep_template": "@pypi//{name}:{target}",
331331
"filename": "simple-0.0.1-py3-none-any.whl",
332-
"python_interpreter_target": "unit_test_interpreter_target",
333332
"requirement": "simple==0.0.1",
334333
"sha256": "deadbeef",
335334
"urls": ["https://example.com/simple-0.0.1-py3-none-any.whl"],
@@ -656,7 +655,6 @@ torch==2.4.1+cpu ; platform_machine == 'x86_64' \
656655
"config_load": "@pypi//:config.bzl",
657656
"dep_template": "@pypi//{name}:{target}",
658657
"filename": "torch-2.4.1+cpu-cp312-cp312-linux_x86_64.whl",
659-
"python_interpreter_target": "unit_test_interpreter_target",
660658
"requirement": "torch==2.4.1+cpu",
661659
"sha256": "8800deef0026011d502c0c256cc4b67d002347f63c3a38cd8e45f1f445c61364",
662660
"urls": ["https://torch.index/whl/cpu/torch-2.4.1%2Bcpu-cp312-cp312-linux_x86_64.whl"],
@@ -665,7 +663,6 @@ torch==2.4.1+cpu ; platform_machine == 'x86_64' \
665663
"config_load": "@pypi//:config.bzl",
666664
"dep_template": "@pypi//{name}:{target}",
667665
"filename": "torch-2.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
668-
"python_interpreter_target": "unit_test_interpreter_target",
669666
"requirement": "torch==2.4.1",
670667
"sha256": "36109432b10bd7163c9b30ce896f3c2cca1b86b9765f956a1594f0ff43091e2a",
671668
"urls": ["https://torch.index/whl/cpu/torch-2.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl"],
@@ -674,7 +671,6 @@ torch==2.4.1+cpu ; platform_machine == 'x86_64' \
674671
"config_load": "@pypi//:config.bzl",
675672
"dep_template": "@pypi//{name}:{target}",
676673
"filename": "torch-2.4.1+cpu-cp312-cp312-win_amd64.whl",
677-
"python_interpreter_target": "unit_test_interpreter_target",
678674
"requirement": "torch==2.4.1+cpu",
679675
"sha256": "3a570e5c553415cdbddfe679207327b3a3806b21c6adea14fba77684d1619e97",
680676
"urls": ["https://torch.index/whl/cpu/torch-2.4.1%2Bcpu-cp312-cp312-win_amd64.whl"],
@@ -683,7 +679,6 @@ torch==2.4.1+cpu ; platform_machine == 'x86_64' \
683679
"config_load": "@pypi//:config.bzl",
684680
"dep_template": "@pypi//{name}:{target}",
685681
"filename": "torch-2.4.1-cp312-none-macosx_11_0_arm64.whl",
686-
"python_interpreter_target": "unit_test_interpreter_target",
687682
"requirement": "torch==2.4.1",
688683
"sha256": "72b484d5b6cec1a735bf3fa5a1c4883d01748698c5e9cfdbeb4ffab7c7987e0d",
689684
"urls": ["https://torch.index/whl/cpu/torch-2.4.1-cp312-none-macosx_11_0_arm64.whl"],
@@ -845,6 +840,11 @@ def _test_simple_get_index(env):
845840
builder = hub_builder(
846841
env,
847842
simpleapi_download_fn = mocksimpleapi_download,
843+
whl_overrides = {
844+
"direct_without_sha": {
845+
"my_patch": 1,
846+
},
847+
},
848848
)
849849
builder.pip_parse(
850850
_mock_mctx(
@@ -1003,6 +1003,10 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10031003
"requirement": "direct_without_sha==0.0.1",
10041004
"sha256": "",
10051005
"urls": ["example-direct.org/direct_without_sha-0.0.1-py3-none-any.whl"],
1006+
# NOTE @aignas 2025-11-24: any patching still requires the python interpreter from the
1007+
# hermetic toolchain or the system. This is so that we can rezip it back to a wheel and
1008+
# verify the metadata so that it is installable by any installer out there.
1009+
"whl_patches": {"my_patch": "1"},
10061010
},
10071011
"pypi_315_git_dep": {
10081012
"config_load": "@pypi//:config.bzl",
@@ -1022,7 +1026,6 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10221026
"config_load": "@pypi//:config.bzl",
10231027
"dep_template": "@pypi//{name}:{target}",
10241028
"filename": "plat-pkg-0.0.4-py3-none-linux_x86_64.whl",
1025-
"python_interpreter_target": "unit_test_interpreter_target",
10261029
"requirement": "plat_pkg==0.0.4",
10271030
"sha256": "deadb44f",
10281031
"urls": ["example2.org/index/plat_pkg/"],
@@ -1031,7 +1034,6 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10311034
"config_load": "@pypi//:config.bzl",
10321035
"dep_template": "@pypi//{name}:{target}",
10331036
"filename": "simple-0.0.1-py3-none-any.whl",
1034-
"python_interpreter_target": "unit_test_interpreter_target",
10351037
"requirement": "simple==0.0.1",
10361038
"sha256": "deadb00f",
10371039
"urls": ["example2.org"],
@@ -1040,7 +1042,6 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10401042
"config_load": "@pypi//:config.bzl",
10411043
"dep_template": "@pypi//{name}:{target}",
10421044
"filename": "some_pkg-0.0.1-py3-none-any.whl",
1043-
"python_interpreter_target": "unit_test_interpreter_target",
10441045
"requirement": "some_pkg==0.0.1",
10451046
"sha256": "deadbaaf",
10461047
"urls": ["example-direct.org/some_pkg-0.0.1-py3-none-any.whl"],
@@ -1049,7 +1050,6 @@ git_dep @ git+https://git.server/repo/project@deadbeefdeadbeef
10491050
"config_load": "@pypi//:config.bzl",
10501051
"dep_template": "@pypi//{name}:{target}",
10511052
"filename": "some-other-pkg-0.0.1-py3-none-any.whl",
1052-
"python_interpreter_target": "unit_test_interpreter_target",
10531053
"requirement": "some_other_pkg==0.0.1",
10541054
"sha256": "deadb33f",
10551055
"urls": ["example2.org/index/some_other_pkg/"],

0 commit comments

Comments
 (0)