Files
cibuildwheel/unit_test/main_tests/main_options_test.py
T
79244d366c feet: general approach to auditing wheels with abi3audit default (#2805)
* WIP - initial punt at audit command

* Add `abi3audit` as a dependency

* Add helper functions to check stable ABI wheels

* Run `abi3audit` for macOS and Windows wheels

* Copy out of container for repairing?

* Add some notes that `cibuildwheel` runs `abi3audit`

* Add basic unit tests

* Add a basic C extension with `Py_LIMITED_API`

* Add a test project that violates Stable ABI

* Fix linux test

* Skip abi3 wheel tests for Pyodide

* Patch the correct subprocess module

* wrap cleanup of abi3audit dir

* Write the docs for the new options

* Move to above testing in docs

* Implement audit-requires and audit-command

* Some cleanups after self-review

* Add default value

* fix type errors

* the key is `audit-command`, not `audit`

* Add a variety of tests for audit requires options

* Add `test_audit_requires` similar to `test_test_requires`

* Add some configurability-related audit tests

* Fix parsing error with options docs leaving out commands

* Better way to extract version (maybe helps Pyodide?)

* Fix a case of unbound `use_uv`

* Standardise: rename to `abi3_wheel`

* Fix audit command run message

* Simplify custom audit command a bit

* Remove unnecessary skip for Pyodide

* Pyodide should have no default audit command

* More accurate skip messages for Pyodide skips

* Wheels are audited after they are repaired

* Regenerate constraints to include `abi3audit`

* Fix typos

* Some attempts for Windows fixes

* Check `pyvenv.cfg` instead of directory existence

* Add validation for lack of wheel placeholders

* Try yet another Windows `uv` fix

* Regenerate diagram and re-trigger Azure CI

* Add missing `import sys` for abi3 C extension tests

* Remove audit-command at the global level

* Clarify `abi3audit` pinning a little bit

* Regen constraints

* Discard changes to cibuildwheel/resources/constraints-pyodide312.txt

* Discard changes to cibuildwheel/resources/constraints-pyodide313.txt

* try opt-in uv again

* fix issue on windows on Python 3.13 related to nested venvs

On win / python 3.13, virtualenv creates a venv where the 'home'
points back to the venv that sys.executable was running in, rather
than the root install. that seemingly leads to problems with package
resolution, where pip.exe couldn't find the pip python package.
this appears to fix it!

* Update constraints

* chore: revert python-discovery bump

Assisted-by: OpenCode:glm-5.1
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* fix: restore workaround for graalpy

Assisted-by: OpenCode:glm-5.1
Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>
Co-authored-by: Henry Schreiner <henryfs@princeton.edu>
2026-05-14 07:41:14 -07:00

683 lines
22 KiB
Python

import sys
import tomllib
from collections.abc import Mapping
from fnmatch import fnmatch
from pathlib import Path
from typing import TYPE_CHECKING
import pytest
from cibuildwheel.__main__ import main
from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.frontend import _split_config_settings, parse_config_settings
from cibuildwheel.options import BuildOptions, _get_pinned_container_images
from cibuildwheel.selector import BuildSelector, EnableGroup
from cibuildwheel.util import resources
from cibuildwheel.util.packaging import DependencyConstraints
if TYPE_CHECKING:
from .conftest import ArgsInterceptor
# CIBW_PLATFORM is tested in main_platform_test.py
def test_old_free_threaded(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setenv("CIBW_FREE_THREADED_SUPPORT", "ON")
with pytest.raises(SystemExit):
main()
assert (
"CIBW_FREE_THREADED_SUPPORT environment variable is no longer supported."
in capsys.readouterr().err
)
@pytest.mark.usefixtures("platform")
def test_output_dir(
intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch
) -> None:
OUTPUT_DIR = Path("some_output_dir")
monkeypatch.setenv("CIBW_OUTPUT_DIR", str(OUTPUT_DIR))
main()
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()
@pytest.mark.usefixtures("platform")
def test_output_dir_default(intercepted_build_args: "ArgsInterceptor") -> None:
main()
assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse").resolve()
@pytest.mark.usefixtures("platform")
@pytest.mark.parametrize("also_set_environment", [False, True])
def test_output_dir_argument(
also_set_environment: bool,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
OUTPUT_DIR = Path("some_output_dir")
monkeypatch.setattr(sys, "argv", [*sys.argv, "--output-dir", str(OUTPUT_DIR)])
if also_set_environment:
monkeypatch.setenv("CIBW_OUTPUT_DIR", "not_this_output_dir")
main()
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()
@pytest.mark.usefixtures("platform", "allow_empty")
def test_build_selector(
intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("CIBW_BUILD", "cp313-*")
monkeypatch.setenv("CIBW_SKIP", "cp39-*")
main()
intercepted_build_selector = intercepted_build_args.args[0].globals.build_selector
assert isinstance(intercepted_build_selector, BuildSelector)
assert intercepted_build_selector("cp313-something-to-build")
assert not intercepted_build_selector("cp39-something-to-skip")
# This unit test is just testing the options of 'main'
# Unit tests for BuildSelector are in build_selector_test.py
@pytest.mark.usefixtures("platform", "allow_empty")
def test_invalid_build_selector(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setenv("CIBW_BUILD", "invalid")
with pytest.raises(SystemExit) as e:
main()
assert e.value.code == 2
_, err = capsys.readouterr()
assert "Invalid build selector" in err
@pytest.mark.parametrize(
("option_name", "option_env_var"),
[
("skip", "CIBW_SKIP"),
("test_skip", "CIBW_TEST_SKIP"),
],
)
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_invalid_skip_selector(
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
option_name: str,
option_env_var: str,
) -> None:
monkeypatch.setenv(option_env_var, "invalid")
main()
_, err = capsys.readouterr()
print(err)
assert f"Invalid {option_name} selector" in err
@pytest.mark.parametrize(
"selector", ["*-macosx_universal2:arm64", "*-macosx_universal2:x86_64", "*-macosx_arm64"]
)
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_valid_test_skip_selector(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], selector: str
) -> None:
monkeypatch.setenv("CIBW_TEST_SKIP", selector)
main()
_, err = capsys.readouterr()
print(err)
assert "Invalid test_skip selector" not in err
@pytest.mark.parametrize("selector", ["*-macosx_universal2:invalid", "*-macosx_arm64:arm64"])
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_invalid_test_skip_selector(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], selector: str
) -> None:
monkeypatch.setenv("CIBW_TEST_SKIP", selector)
main()
_, err = capsys.readouterr()
print(err)
assert "Invalid test_skip selector" in err
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_empty_selector(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("CIBW_SKIP", "*")
with pytest.raises(SystemExit) as e:
main()
assert e.value.code == 3
@pytest.mark.parametrize(
("architecture", "image", "full_image"),
[
("x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
("x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
("x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
("x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
("x86_64", "custom_image", "custom_image"),
("i686", None, "quay.io/pypa/manylinux_2_28_i686:*"),
("i686", "manylinux2014", "quay.io/pypa/manylinux2014_i686:*"),
("i686", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_i686:*"),
("i686", "custom_image", "custom_image"),
("pypy_x86_64", None, "quay.io/pypa/manylinux_2_28_x86_64:*"),
("pypy_x86_64", "manylinux2014", "quay.io/pypa/manylinux2014_x86_64:*"),
("pypy_x86_64", "manylinux_2_28", "quay.io/pypa/manylinux_2_28_x86_64:*"),
("pypy_x86_64", "manylinux_2_34", "quay.io/pypa/manylinux_2_34_x86_64:*"),
("pypy_x86_64", "custom_image", "custom_image"),
],
)
def test_manylinux_images(
architecture: str,
image: str | None,
full_image: str,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if image is not None:
monkeypatch.setenv("CIBW_MANYLINUX_" + architecture.upper() + "_IMAGE", image)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
if platform == "linux":
assert fnmatch(
build_options.manylinux_images[architecture],
full_image,
)
else:
assert build_options.manylinux_images is None
def get_default_repair_command(platform: str) -> str:
if platform == "linux":
return "auditwheel repair -w {dest_dir} {wheel}"
elif platform == "macos":
return "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
elif platform == "windows":
return "delvewheel repair -w {dest_dir} -v {wheel}"
elif platform == "pyodide":
return ""
else:
msg = f"Unknown platform: {platform!r}"
raise ValueError(msg)
@pytest.mark.parametrize("repair_command", [None, "", "repair", "repair -w {dest_dir} {wheel}"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_repair_command(
repair_command: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if repair_command is not None:
if platform_specific:
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND_" + platform.upper(), repair_command)
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "overwritten")
else:
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", repair_command)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
expected_repair = (
get_default_repair_command(platform) if repair_command is None else repair_command
)
assert build_options.repair_command == expected_repair
@pytest.mark.parametrize(
"environment",
[{}, {"something": "value"}, {"something": "value", "something_else": "other_value"}],
)
@pytest.mark.parametrize("platform_specific", [False, True])
def test_environment(
environment: Mapping[str, str],
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
env_string = " ".join(f"{k}={v}" for k, v in environment.items())
if platform_specific:
monkeypatch.setenv("CIBW_ENVIRONMENT_" + platform.upper(), env_string)
monkeypatch.setenv("CIBW_ENVIRONMENT", "overwritten")
else:
monkeypatch.setenv("CIBW_ENVIRONMENT", env_string)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
intercepted_environment = build_options.environment
assert isinstance(intercepted_environment, ParsedEnvironment)
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
@pytest.mark.parametrize("test_requires", [None, "requirement other_requirement"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_test_requires(
test_requires: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if test_requires is not None:
if platform_specific:
monkeypatch.setenv("CIBW_TEST_REQUIRES_" + platform.upper(), test_requires)
monkeypatch.setenv("CIBW_TEST_REQUIRES", "overwritten")
else:
monkeypatch.setenv("CIBW_TEST_REQUIRES", test_requires)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.test_requires == (test_requires or "").split()
@pytest.mark.parametrize("audit_requires", [None, "abi3audit", "abi3audit custom-audit-tool"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_audit_requires(
audit_requires: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if audit_requires is not None:
if platform_specific:
monkeypatch.setenv("CIBW_AUDIT_REQUIRES_" + platform.upper(), audit_requires)
monkeypatch.setenv("CIBW_AUDIT_REQUIRES", "overwritten")
else:
monkeypatch.setenv("CIBW_AUDIT_REQUIRES", audit_requires)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
expected = (audit_requires or "abi3audit").split()
assert build_options.audit_requires == expected
@pytest.mark.parametrize("test_extras", [None, "extras"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_test_extras(
test_extras: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if test_extras is not None:
if platform_specific:
monkeypatch.setenv("CIBW_TEST_EXTRAS_" + platform.upper(), test_extras)
monkeypatch.setenv("CIBW_TEST_EXTRAS", "overwritten")
else:
monkeypatch.setenv("CIBW_TEST_EXTRAS", test_extras)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.test_extras == ("[" + test_extras + "]" if test_extras else "")
@pytest.mark.parametrize("test_command", [None, "test --command"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_test_command(
test_command: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if test_command is not None:
if platform_specific:
monkeypatch.setenv("CIBW_TEST_COMMAND_" + platform.upper(), test_command)
monkeypatch.setenv("CIBW_TEST_COMMAND", "overwritten")
else:
monkeypatch.setenv("CIBW_TEST_COMMAND", test_command)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.test_command == (test_command or "")
@pytest.mark.parametrize("before_build", [None, "before --build"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_before_build(
before_build: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if before_build is not None:
if platform_specific:
monkeypatch.setenv("CIBW_BEFORE_BUILD_" + platform.upper(), before_build)
monkeypatch.setenv("CIBW_BEFORE_BUILD", "overwritten")
else:
monkeypatch.setenv("CIBW_BEFORE_BUILD", before_build)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.before_build == (before_build or "")
@pytest.mark.parametrize("build_verbosity", [None, 0, 2, -2, 4, -4])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_build_verbosity(
build_verbosity: int | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if build_verbosity is not None:
if platform_specific:
monkeypatch.setenv("CIBW_BUILD_VERBOSITY_" + platform.upper(), str(build_verbosity))
monkeypatch.setenv("CIBW_BUILD_VERBOSITY", "overwritten")
else:
monkeypatch.setenv("CIBW_BUILD_VERBOSITY", str(build_verbosity))
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
assert build_options.build_verbosity == expected_verbosity
@pytest.mark.parametrize("platform_specific", [False, True])
def test_config_settings(
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
config_settings = (
'setting=value setting=value2 triplet=1 triplet=2 triplet=3 other="something else"'
)
if platform_specific:
monkeypatch.setenv("CIBW_CONFIG_SETTINGS_" + platform.upper(), config_settings)
monkeypatch.setenv("CIBW_CONFIG_SETTINGS", "a=b")
else:
monkeypatch.setenv("CIBW_CONFIG_SETTINGS", config_settings)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.config_settings == config_settings
assert _split_config_settings(config_settings) == [
"-Csetting=value",
"-Csetting=value2",
"-Ctriplet=1",
"-Ctriplet=2",
"-Ctriplet=3",
"-Cother=something else",
]
assert parse_config_settings(config_settings) == {
"setting": ["value", "value2"],
"triplet": ["1", "2", "3"],
"other": "something else",
}
@pytest.mark.parametrize(
"selector",
[
"CIBW_BUILD",
"CIBW_SKIP",
"CIBW_TEST_SKIP",
],
)
@pytest.mark.parametrize(
("pattern", "series"),
[
("cp27-*", 1),
("cp35-*", 1),
("?p36-*", 2),
("?p37-*", 2),
("?p38-*", 3),
("?p27*", 1),
("?p2*", 1),
("?p35*", 1),
("cp313t*", None),
],
)
@pytest.mark.usefixtures("platform", "intercepted_build_args", "allow_empty")
def test_build_selector_deprecated_error(
monkeypatch: pytest.MonkeyPatch,
selector: str,
pattern: str,
series: int,
capsys: pytest.CaptureFixture[str],
) -> None:
monkeypatch.setenv(selector, pattern)
monkeypatch.delenv("CIBW_ENABLE", raising=False)
if selector == "CIBW_BUILD":
with pytest.raises(SystemExit) as ex:
main()
assert ex.value.code == 4
else:
main()
stderr = capsys.readouterr().err
if pattern == "cp313t*":
msg = "cibuildwheel 4.x no longer supports Python 3.13 free-threading. Please use the 3.x series or update"
else:
msg = f"cibuildwheel 4.x no longer supports Python < 3.9. Please use the {series}.x series or update"
assert msg in stderr
@pytest.mark.parametrize("before_all", ["", None, "test text"])
@pytest.mark.parametrize("platform_specific", [False, True])
def test_before_all(
before_all: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
) -> None:
if before_all is not None:
if platform_specific:
monkeypatch.setenv("CIBW_BEFORE_ALL_" + platform.upper(), before_all)
monkeypatch.setenv("CIBW_BEFORE_ALL", "overwritten")
else:
monkeypatch.setenv("CIBW_BEFORE_ALL", before_all)
main()
build_options = intercepted_build_args.args[0].build_options(identifier=None)
assert build_options.before_all == (before_all or "")
@pytest.mark.parametrize(
"dependency_versions",
[None, "pinned", "latest", "FILE", "packages: pip==21.0.0"],
)
@pytest.mark.parametrize("platform_specific", [False, True])
def test_dependency_versions(
dependency_versions: str | None,
platform_specific: bool,
platform: str,
intercepted_build_args: "ArgsInterceptor",
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
option_value = dependency_versions
if dependency_versions == "FILE":
constraints_file = tmp_path / "constraints.txt"
constraints_file.write_text("foo==1.2.3\nbar==4.5.6")
option_value = str(constraints_file)
if option_value is not None:
if platform_specific:
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS_" + platform.upper(), option_value)
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", "overwritten")
else:
monkeypatch.setenv("CIBW_DEPENDENCY_VERSIONS", option_value)
main()
build_options: BuildOptions = intercepted_build_args.args[0].build_options(identifier=None)
dependency_constraints = build_options.dependency_constraints
if dependency_versions is None or dependency_versions == "pinned":
assert dependency_constraints == DependencyConstraints.pinned()
elif dependency_versions == "latest":
assert dependency_constraints == DependencyConstraints.latest()
elif dependency_versions == "FILE":
assert dependency_constraints.base_file_path
assert option_value is not None
assert dependency_constraints.base_file_path.samefile(Path(option_value))
elif dependency_versions.startswith("packages:"):
assert dependency_constraints.packages == ["pip==21.0.0"]
@pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
def test_debug_traceback(
monkeypatch: pytest.MonkeyPatch, method: str, capfd: pytest.CaptureFixture[str]
) -> None:
if method == "command_line":
monkeypatch.setattr(sys, "argv", [*sys.argv, "--debug-traceback"])
elif method == "env_var":
monkeypatch.setenv("CIBW_DEBUG_TRACEBACK", "TRUE")
# set an option that produces a configuration error
monkeypatch.setenv("CIBW_BUILD_FRONTEND", "invalid_value")
with pytest.raises(SystemExit) as exit:
main()
assert exit.value.code == 2
_, err = capfd.readouterr()
if method == "unset":
assert "Traceback (most recent call last)" not in err
else:
assert "Traceback (most recent call last)" in err
@pytest.mark.parametrize("method", ["unset", "command_line", "env_var"])
def test_enable(
method: str, intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.delenv("CIBW_ENABLE", raising=False)
if method == "command_line":
monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy", "--enable", "graalpy"])
elif method == "env_var":
monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy")
main()
enable_groups = intercepted_build_args.args[0].globals.build_selector.enable
if method == "unset":
assert enable_groups == frozenset()
else:
assert enable_groups == frozenset([EnableGroup.PyPy, EnableGroup.GraalPy])
def test_enable_all(
intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "all"])
monkeypatch.delenv("CIBW_ENABLE", raising=False)
main()
enable_groups = intercepted_build_args.args[0].globals.build_selector.enable
assert enable_groups == EnableGroup.all_groups()
def test_enable_arg_inherits(
intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy")
monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"])
main()
enable_groups = intercepted_build_args.args[0].globals.build_selector.enable
assert enable_groups == frozenset(
(EnableGroup.PyPy, EnableGroup.GraalPy, EnableGroup.CPythonPrerelease)
)
def test_enable_arg_error_message(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "invalid_group"])
with pytest.raises(SystemExit) as ex:
main()
assert ex.value.code == 2
_, err = capsys.readouterr()
assert "Valid group names are:" in err
def test_defaults(platform: str, intercepted_build_args: "ArgsInterceptor") -> None:
main()
build_options: BuildOptions = intercepted_build_args.args[0].build_options(identifier=None)
with resources.DEFAULTS.open("rb") as f:
defaults_toml = tomllib.load(f)
root_defaults = defaults_toml["tool"]["cibuildwheel"]
platform_defaults = defaults_toml["tool"]["cibuildwheel"][platform]
defaults = {}
defaults.update(root_defaults)
defaults.update(platform_defaults)
# test a few options
assert build_options.before_all == defaults["before-all"]
repair_wheel_default = defaults["repair-wheel-command"]
if isinstance(repair_wheel_default, list):
repair_wheel_default = " && ".join(repair_wheel_default)
assert build_options.repair_command == repair_wheel_default
assert build_options.build_frontend.name == "build"
assert build_options.build_frontend.args == ()
if platform == "linux":
assert build_options.manylinux_images
pinned_images = _get_pinned_container_images()
default_x86_64_image = pinned_images["x86_64"][defaults["manylinux-x86_64-image"]]
assert build_options.manylinux_images["x86_64"] == default_x86_64_image