feat: stricter selector parsing, refactor to platforms module (#2291)

* feat: stricter selector parsing, refactor to `platforms` module

- Use a different method to build nothing
- Make the check aware of enable groups

* optimise unit tests - a specific platform module API for all configs

Unit test time: 26.2s -> 13.1s

* Remove unnecessary get_platform_module function
This commit is contained in:
Joe Rickerby
2025-03-24 16:58:47 +00:00
committed by GitHub
parent 4db404ca50
commit 141e702872
17 changed files with 283 additions and 183 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ import setuptools._distutils.util
from cibuildwheel.ci import CIProvider, detect_ci_provider
from cibuildwheel.errors import FatalError
from cibuildwheel.windows import PythonConfiguration, setup_setuptools_cross_compile
from cibuildwheel.platforms.windows import PythonConfiguration, setup_setuptools_cross_compile
# monkeypatching os.name is too flaky. E.g. It works on my machine, but fails in pipeline
if not sys.platform.startswith("win"):
+8 -6
View File
@@ -4,7 +4,7 @@ from pprint import pprint
import pytest
import cibuildwheel.linux
import cibuildwheel.platforms.linux
from cibuildwheel.oci_container import OCIContainerEngineConfig
from cibuildwheel.options import CommandLineArguments, Options
@@ -46,20 +46,22 @@ def test_linux_container_split(tmp_path: Path, monkeypatch: pytest.MonkeyPatch)
monkeypatch.chdir(tmp_path)
options = Options("linux", command_line_arguments=args, env={})
python_configurations = cibuildwheel.linux.get_python_configurations(
python_configurations = cibuildwheel.platforms.linux.get_python_configurations(
options.globals.build_selector, options.globals.architectures
)
build_steps = list(cibuildwheel.linux.get_build_steps(options, python_configurations))
build_steps = list(cibuildwheel.platforms.linux.get_build_steps(options, python_configurations))
# helper functions to extract test info
def identifiers(step: cibuildwheel.linux.BuildStep) -> list[str]:
def identifiers(step: cibuildwheel.platforms.linux.BuildStep) -> list[str]:
return [c.identifier for c in step.platform_configs]
def before_alls(step: cibuildwheel.linux.BuildStep) -> list[str]:
def before_alls(step: cibuildwheel.platforms.linux.BuildStep) -> list[str]:
return [options.build_options(c.identifier).before_all for c in step.platform_configs]
def container_engines(step: cibuildwheel.linux.BuildStep) -> list[OCIContainerEngineConfig]:
def container_engines(
step: cibuildwheel.platforms.linux.BuildStep,
) -> list[OCIContainerEngineConfig]:
return [options.build_options(c.identifier).container_engine for c in step.platform_configs]
pprint(build_steps)
+2 -1
View File
@@ -6,7 +6,8 @@ from pathlib import Path
import pytest
from cibuildwheel import __main__, architecture, linux, macos, pyodide, windows
from cibuildwheel import __main__, architecture
from cibuildwheel.platforms import linux, macos, pyodide, windows
from cibuildwheel.util import file
+35 -8
View File
@@ -50,22 +50,49 @@ def test_output_dir_argument(also_set_environment, intercepted_build_args, monke
@pytest.mark.usefixtures("platform", "allow_empty")
def test_build_selector(intercepted_build_args, monkeypatch):
BUILD = "some build* *-selector"
SKIP = "some skip* *-selector"
monkeypatch.setenv("CIBW_BUILD", BUILD)
monkeypatch.setenv("CIBW_SKIP", SKIP)
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("build24-this")
assert not intercepted_build_selector("skip65-that")
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, capsys):
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, capsys, option_name, option_env_var):
monkeypatch.setenv(option_env_var, "invalid")
main()
_, err = capsys.readouterr()
print(err)
assert f"Invalid {option_name} selector" in err
@pytest.mark.usefixtures("platform", "intercepted_build_args")
def test_empty_selector(monkeypatch):
monkeypatch.setenv("CIBW_SKIP", "*")
@@ -313,7 +340,7 @@ def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys)
stderr = capsys.readouterr().err
series = "2" if "6" in pattern else "1"
msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the {series}.x series or update {selector}"
msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the {series}.x series or update"
assert msg in stderr
+6 -5
View File
@@ -8,7 +8,7 @@ from unittest import mock
import pytest
from cibuildwheel import linux
from cibuildwheel import platforms
from cibuildwheel.__main__ import main
from cibuildwheel.oci_container import OCIPlatform
from cibuildwheel.util import file
@@ -39,10 +39,11 @@ def mock_build_container(monkeypatch):
monkeypatch.setattr(subprocess, "Popen", fail_on_call)
monkeypatch.setattr(subprocess, "run", ignore_call)
monkeypatch.setattr(file, "download", fail_on_call)
monkeypatch.setattr("cibuildwheel.linux.OCIContainer", ignore_context_call)
monkeypatch.setattr("cibuildwheel.platforms.linux.OCIContainer", ignore_context_call)
monkeypatch.setattr(
"cibuildwheel.linux.build_in_container", mock.Mock(spec=linux.build_in_container)
"cibuildwheel.platforms.linux.build_in_container",
mock.Mock(spec=platforms.linux.build_in_container),
)
monkeypatch.setattr("cibuildwheel.__main__.print_new_wheels", ignore_context_call)
@@ -53,7 +54,7 @@ def test_build_default_launches(monkeypatch):
main()
build_in_container = typing.cast(mock.Mock, linux.build_in_container)
build_in_container = typing.cast(mock.Mock, platforms.linux.build_in_container)
assert build_in_container.call_count == 4
@@ -121,7 +122,7 @@ before-all = "true"
main()
build_in_container = typing.cast(mock.Mock, linux.build_in_container)
build_in_container = typing.cast(mock.Mock, platforms.linux.build_in_container)
assert build_in_container.call_count == 6
+2 -2
View File
@@ -6,13 +6,13 @@ from pathlib import Path
import pytest
from cibuildwheel import errors
from cibuildwheel.__main__ import get_build_identifiers, get_platform_module
from cibuildwheel.bashlex_eval import local_environment_executor
from cibuildwheel.options import (
CommandLineArguments,
Options,
_get_pinned_container_images,
)
from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers
from cibuildwheel.selector import EnableGroup
from cibuildwheel.util import resources
from cibuildwheel.util.packaging import DependencyConstraints
@@ -51,7 +51,7 @@ def test_options_1(tmp_path, monkeypatch):
options = Options(platform="linux", command_line_arguments=args, env={})
module = get_platform_module("linux")
module = ALL_PLATFORM_MODULES["linux"]
identifiers = get_build_identifiers(
platform_module=module,
build_selector=options.globals.build_selector,