From f6c810852d424abdddc6abc44d1e4b165797399d Mon Sep 17 00:00:00 2001 From: Alexander Condello Date: Mon, 10 Nov 2025 17:59:32 -0800 Subject: [PATCH] feat: make the `{project}` placeholder available to `repair-wheel-command` (#2589) * Make the {project} placeholder available to repair-wheel-command * Add separate test for {project} placeholder in repair-wheel-command * Make the {project} placeholder available to repair-wheel-command * reduce duplicated work in tests * Use a valid wheel filename for the post-repair name --------- Co-authored-by: Joe Rickerby --- cibuildwheel/platforms/android.py | 6 ++++- cibuildwheel/platforms/linux.py | 6 ++++- cibuildwheel/platforms/macos.py | 2 ++ cibuildwheel/platforms/pyodide.py | 2 ++ cibuildwheel/platforms/windows.py | 2 ++ docs/options.md | 2 ++ test/test_custom_repair_wheel.py | 40 +++++++++++++++++++++++++++++++ 7 files changed, 58 insertions(+), 2 deletions(-) diff --git a/cibuildwheel/platforms/android.py b/cibuildwheel/platforms/android.py index a69e0058..229401e4 100644 --- a/cibuildwheel/platforms/android.py +++ b/cibuildwheel/platforms/android.py @@ -447,7 +447,11 @@ def repair_wheel(state: BuildState, built_wheel: Path) -> Path: if state.options.repair_command: shell( prepare_command( - state.options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir + state.options.repair_command, + wheel=built_wheel, + dest_dir=repaired_wheel_dir, + package=state.options.package_dir, + project=".", ), env=state.build_env, ) diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py index a04104f1..d2f133b1 100644 --- a/cibuildwheel/platforms/linux.py +++ b/cibuildwheel/platforms/linux.py @@ -322,7 +322,11 @@ def build_in_container( if build_options.repair_command: log.step("Repairing wheel...") repair_command_prepared = prepare_command( - build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir + build_options.repair_command, + wheel=built_wheel, + dest_dir=repaired_wheel_dir, + package=container_package_dir, + project=container_project_path, ) container.call(["sh", "-c", repair_command_prepared], env=env) else: diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py index e6493f35..c9f3f90d 100644 --- a/cibuildwheel/platforms/macos.py +++ b/cibuildwheel/platforms/macos.py @@ -522,6 +522,8 @@ def build(options: Options, tmp_path: Path) -> None: wheel=built_wheel, dest_dir=repaired_wheel_dir, delocate_archs=delocate_archs, + package=build_options.package_dir, + project=".", ) shell(repair_command_prepared, env=env) else: diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py index 07a9894e..f6ac2ee0 100644 --- a/cibuildwheel/platforms/pyodide.py +++ b/cibuildwheel/platforms/pyodide.py @@ -439,6 +439,8 @@ def build(options: Options, tmp_path: Path) -> None: build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir, + package=build_options.package_dir, + project=".", ) shell(repair_command_prepared, env=env) log.step_end() diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py index de334eca..7a38fd22 100644 --- a/cibuildwheel/platforms/windows.py +++ b/cibuildwheel/platforms/windows.py @@ -518,6 +518,8 @@ def build(options: Options, tmp_path: Path) -> None: build_options.repair_command, wheel=built_wheel, dest_dir=repaired_wheel_dir, + package=build_options.package_dir, + project=".", ) shell(repair_command_prepared, env=env) else: diff --git a/docs/options.md b/docs/options.md index ddfa9781..72e02a44 100644 --- a/docs/options.md +++ b/docs/options.md @@ -907,6 +907,8 @@ The following placeholders must be used inside the command and will be replaced - `{dest_dir}` for the absolute path of the directory where to create the repaired wheel - `{delocate_archs}` (macOS only) comma-separated list of architectures in the wheel. +You can use the `{package}` or `{project}` placeholders in your `repair-wheel-command` to refer to the package being built or the project root, respectively. + The command is run in a shell, so you can run multiple commands like `cmd1 && cmd2`. Platform-specific environment variables are also available:
diff --git a/test/test_custom_repair_wheel.py b/test/test_custom_repair_wheel.py index 5af01265..83aaddd6 100644 --- a/test/test_custom_repair_wheel.py +++ b/test/test_custom_repair_wheel.py @@ -1,4 +1,5 @@ import subprocess +import textwrap from contextlib import nullcontext as does_not_raise import pytest @@ -56,3 +57,42 @@ def test(tmp_path, capfd): # We only produced one wheel (perhaps Pyodide) # check that it has the right name assert result[0].startswith("spam-0.1.0-py2-none-") + + +@pytest.mark.parametrize( + "repair_command", + [ + "python repair.py {wheel} {dest_dir}", + "python {package}/repair.py {wheel} {dest_dir}", + "python {project}/repair.py {wheel} {dest_dir}", + ], + ids=["no-placeholder", "package-placeholder", "project-placeholder"], +) +def test_repair_wheel_command_structure(tmp_path, repair_command): + project_dir = tmp_path / "project" + project = test_projects.new_c_project() + project.files["repair.py"] = textwrap.dedent(""" + import shutil + import sys + from pathlib import Path + + wheel = Path(sys.argv[1]) + dest_dir = Path(sys.argv[2]) + + dest_dir.mkdir(parents=True, exist_ok=True) + shutil.copy(wheel, dest_dir / "spamrepaired-0.0.1-py-none-any.whl") + """) + + # Combined test for repair wheel command formats (plain, {package}, {project}) + project.generate(project_dir) + + result = utils.cibuildwheel_run( + project_dir, + add_env={ + "CIBW_REPAIR_WHEEL_COMMAND": repair_command, + "CIBW_ARCHS": "native", + }, + single_python=True, + ) + + assert result == ["spamrepaired-0.0.1-py-none-any.whl"]