diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index e16adca9c..eded443de 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -9,25 +9,21 @@ import time import traceback import typing -from collections.abc import Generator, Iterable, Sequence, Set +from collections.abc import Generator, Iterable, Sequence from pathlib import Path from tempfile import mkdtemp -from typing import Any, Protocol, TextIO, assert_never +from typing import Any, Literal, TextIO import cibuildwheel -import cibuildwheel.ios -import cibuildwheel.linux -import cibuildwheel.macos -import cibuildwheel.pyodide import cibuildwheel.util -import cibuildwheel.windows from cibuildwheel import errors from cibuildwheel.architecture import Architecture, allowed_architectures_check from cibuildwheel.ci import CIProvider, detect_ci_provider, fix_ansi_codes_for_github_actions from cibuildwheel.logger import log from cibuildwheel.options import CommandLineArguments, Options, compute_options -from cibuildwheel.selector import BuildSelector, EnableGroup -from cibuildwheel.typing import PLATFORMS, GenericPythonConfiguration, PlatformName +from cibuildwheel.platforms import ALL_PLATFORM_MODULES, get_build_identifiers +from cibuildwheel.selector import BuildSelector, EnableGroup, selector_matches +from cibuildwheel.typing import PLATFORMS, PlatformName from cibuildwheel.util.file import CIBW_CACHE_PATH from cibuildwheel.util.helpers import strtobool @@ -286,30 +282,6 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName: return _compute_platform_auto() -class PlatformModule(Protocol): - # note that as per PEP544, the self argument is ignored when the protocol - # is applied to a module - def get_python_configurations( - self, build_selector: BuildSelector, architectures: Set[Architecture] - ) -> Sequence[GenericPythonConfiguration]: ... - - def build(self, options: Options, tmp_path: Path) -> None: ... - - -def get_platform_module(platform: PlatformName) -> PlatformModule: - if platform == "linux": - return cibuildwheel.linux - if platform == "windows": - return cibuildwheel.windows - if platform == "macos": - return cibuildwheel.macos - if platform == "pyodide": - return cibuildwheel.pyodide - if platform == "ios": - return cibuildwheel.ios - assert_never(platform) - - @contextlib.contextmanager def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]: """ @@ -362,7 +334,7 @@ def build_in_directory(args: CommandLineArguments) -> None: msg = f"Could not find any of {{{names}}} at root of package" raise errors.ConfigurationError(msg) - platform_module = get_platform_module(platform) + platform_module = ALL_PLATFORM_MODULES[platform] identifiers = get_build_identifiers( platform_module=platform_module, build_selector=options.globals.build_selector, @@ -448,15 +420,6 @@ def print_preamble(platform: str, options: Options, identifiers: Sequence[str]) print("Here we go!\n") -def get_build_identifiers( - platform_module: PlatformModule, - build_selector: BuildSelector, - architectures: Set[Architecture], -) -> list[str]: - python_configurations = platform_module.get_python_configurations(build_selector, architectures) - return [config.identifier for config in python_configurations] - - def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]: warnings = [] @@ -482,6 +445,75 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str ) raise errors.ConfigurationError(msg) + build_selector = options.globals.build_selector + test_selector = options.globals.test_selector + + all_valid_identifiers = [ + config.identifier + for module in ALL_PLATFORM_MODULES.values() + for config in module.all_python_configurations() + ] + + enabled_selector = BuildSelector( + build_config="*", skip_config="", enable=options.globals.build_selector.enable + ) + all_enabled_identifiers = [ + identifier for identifier in all_valid_identifiers if enabled_selector(identifier) + ] + + warnings += check_for_invalid_selectors( + selector_name="build", + selector_value=build_selector.build_config, + all_valid_identifiers=all_valid_identifiers, + all_enabled_identifiers=all_enabled_identifiers, + ) + warnings += check_for_invalid_selectors( + selector_name="skip", + selector_value=build_selector.skip_config, + all_valid_identifiers=all_valid_identifiers, + all_enabled_identifiers=all_enabled_identifiers, + ) + warnings += check_for_invalid_selectors( + selector_name="test_skip", + selector_value=test_selector.skip_config, + all_valid_identifiers=all_valid_identifiers, + all_enabled_identifiers=all_enabled_identifiers, + ) + + return warnings + + +def check_for_invalid_selectors( + *, + selector_name: Literal["build", "skip", "test_skip"], + selector_value: str, + all_valid_identifiers: Sequence[str], + all_enabled_identifiers: Sequence[str], +) -> list[str]: + warnings = [] + + for selector in selector_value.split(): + if not any(selector_matches(selector, i) for i in all_enabled_identifiers): + msg = f"Invalid {selector_name} selector: {selector!r}. " + error_type: type = errors.ConfigurationError + + if any(selector_matches(selector, i) for i in all_valid_identifiers): + msg += "This selector matches a group that wasn't enabled. Enable it using the `enable` option or remove this selector. " + + if "p2" in selector or "p35" in selector: + msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update `{selector_name}`. " + error_type = errors.DeprecationError + if "p36" in selector or "p37" in selector: + msg += f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update `{selector_name}`. " + error_type = errors.DeprecationError + + if selector_name == "build": + raise error_type(msg) + + msg += "This selector will have no effect. " + + warnings.append(msg) + return warnings diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index 5ab256232..482035a8e 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -863,14 +863,6 @@ def check_for_invalid_configuration(self, identifiers: Iterable[str]) -> None: ) ) - def check_for_deprecated_options(self) -> None: - build_selector = self.globals.build_selector - test_selector = self.globals.test_selector - - deprecated_selectors("CIBW_BUILD", build_selector.build_config, error=True) - deprecated_selectors("CIBW_SKIP", build_selector.skip_config) - deprecated_selectors("CIBW_TEST_SKIP", test_selector.skip_config) - @functools.cached_property def defaults(self) -> Self: return self.__class__( @@ -985,9 +977,7 @@ def compute_options( command_line_arguments: CommandLineArguments, env: Mapping[str, str], ) -> Options: - options = Options(platform=platform, command_line_arguments=command_line_arguments, env=env) - options.check_for_deprecated_options() - return options + return Options(platform=platform, command_line_arguments=command_line_arguments, env=env) @functools.cache @@ -1002,16 +992,3 @@ def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]: all_pinned_images = configparser.ConfigParser() all_pinned_images.read(resources.PINNED_DOCKER_IMAGES) return all_pinned_images - - -def deprecated_selectors(name: str, selector: str, *, error: bool = False) -> None: - if "p2" in selector or "p35" in selector: - msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 1.x series or update {name}" - if error: - raise errors.DeprecationError(msg) - log.warning(msg) - if "p36" in selector or "p37" in selector: - msg = f"cibuildwheel 3.x no longer supports Python < 3.8. Please use the 2.x series or update {name}" - if error: - raise errors.DeprecationError(msg) - log.warning(msg) diff --git a/cibuildwheel/platforms/__init__.py b/cibuildwheel/platforms/__init__.py new file mode 100644 index 000000000..a56fb9036 --- /dev/null +++ b/cibuildwheel/platforms/__init__.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from collections.abc import Sequence +from pathlib import Path +from typing import Final, Protocol + +from cibuildwheel.architecture import Architecture +from cibuildwheel.options import Options +from cibuildwheel.platforms import ios, linux, macos, pyodide, windows +from cibuildwheel.selector import BuildSelector +from cibuildwheel.typing import GenericPythonConfiguration, PlatformName + + +class PlatformModule(Protocol): + # note that as per PEP544, the self argument is ignored when the protocol + # is applied to a module + def all_python_configurations(self) -> Sequence[GenericPythonConfiguration]: ... + + def get_python_configurations( + self, build_selector: BuildSelector, architectures: set[Architecture] + ) -> Sequence[GenericPythonConfiguration]: ... + + def build(self, options: Options, tmp_path: Path) -> None: ... + + +ALL_PLATFORM_MODULES: Final[dict[PlatformName, PlatformModule]] = { + "linux": linux, + "windows": windows, + "macos": macos, + "pyodide": pyodide, + "ios": ios, +} + + +def get_build_identifiers( + platform_module: PlatformModule, + build_selector: BuildSelector, + architectures: set[Architecture], +) -> list[str]: + python_configurations = platform_module.get_python_configurations(build_selector, architectures) + return [config.identifier for config in python_configurations] diff --git a/cibuildwheel/ios.py b/cibuildwheel/platforms/ios.py similarity index 97% rename from cibuildwheel/ios.py rename to cibuildwheel/platforms/ios.py index 52411e31a..25dc2a461 100644 --- a/cibuildwheel/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -12,34 +12,34 @@ from filelock import FileLock -from . import errors -from .architecture import Architecture -from .environment import ParsedEnvironment -from .frontend import ( +from .. import errors +from ..architecture import Architecture +from ..environment import ParsedEnvironment +from ..frontend import ( BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags, ) -from .logger import log -from .macos import install_cpython as install_build_cpython -from .options import Options -from .selector import BuildSelector -from .typing import PathOrStr -from .util import resources -from .util.cmd import call, shell -from .util.file import ( +from ..logger import log +from ..options import Options +from ..selector import BuildSelector +from ..typing import PathOrStr +from ..util import resources +from ..util.cmd import call, shell +from ..util.file import ( CIBW_CACHE_PATH, copy_test_sources, download, move_file, ) -from .util.helpers import prepare_command -from .util.packaging import ( +from ..util.helpers import prepare_command +from ..util.packaging import ( combine_constraints, find_compatible_wheel, get_pip_version, ) -from .venv import virtualenv +from ..venv import virtualenv +from .macos import install_cpython as install_build_cpython @dataclass(frozen=True) @@ -72,10 +72,7 @@ def xcframework_slice(self) -> str: return "ios-arm64_x86_64-simulator" if self.is_simulator else "ios-arm64" -def get_python_configurations( - build_selector: BuildSelector, - architectures: Set[Architecture], -) -> list[PythonConfiguration]: +def all_python_configurations() -> list[PythonConfiguration]: # iOS builds are always cross builds; we need to install a macOS Python as # well. Rather than duplicate the location of the URL of macOS installers, # load the macos configurations, determine the macOS configuration that @@ -97,7 +94,7 @@ def build_url(config_dict: dict[str, str]) -> str: # Load the platform configuration full_python_configs = resources.read_python_configs("ios") # Build the configurations, annotating with macOS URL details. - python_configurations = [ + return [ PythonConfiguration( **item, build_url=build_url(item), @@ -105,6 +102,13 @@ def build_url(config_dict: dict[str, str]) -> str: for item in full_python_configs ] + +def get_python_configurations( + build_selector: BuildSelector, + architectures: Set[Architecture], +) -> list[PythonConfiguration]: + python_configurations = all_python_configurations() + # Filter out configs that don't match any of the selected architectures python_configurations = [ c diff --git a/cibuildwheel/linux.py b/cibuildwheel/platforms/linux.py similarity index 96% rename from cibuildwheel/linux.py rename to cibuildwheel/platforms/linux.py index 7f0a6e77d..4932298aa 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/platforms/linux.py @@ -8,18 +8,18 @@ from pathlib import Path, PurePath, PurePosixPath from typing import assert_never -from . import errors -from .architecture import Architecture -from .frontend import BuildFrontendConfig, get_build_frontend_extra_flags -from .logger import log -from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform -from .options import BuildOptions, Options -from .selector import BuildSelector -from .typing import PathOrStr -from .util import resources -from .util.file import copy_test_sources -from .util.helpers import prepare_command, unwrap -from .util.packaging import find_compatible_wheel +from .. import errors +from ..architecture import Architecture +from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags +from ..logger import log +from ..oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform +from ..options import BuildOptions, Options +from ..selector import BuildSelector +from ..typing import PathOrStr +from ..util import resources +from ..util.file import copy_test_sources +from ..util.helpers import prepare_command, unwrap +from ..util.packaging import find_compatible_wheel ARCHITECTURE_OCI_PLATFORM_MAP = { Architecture.x86_64: OCIPlatform.AMD64, @@ -50,13 +50,16 @@ class BuildStep: container_image: str +def all_python_configurations() -> list[PythonConfiguration]: + config_dicts = resources.read_python_configs("linux") + return [PythonConfiguration(**item) for item in config_dicts] + + def get_python_configurations( build_selector: BuildSelector, architectures: Set[Architecture], ) -> list[PythonConfiguration]: - full_python_configs = resources.read_python_configs("linux") - - python_configurations = [PythonConfiguration(**item) for item in full_python_configs] + python_configurations = all_python_configurations() # return all configurations whose arch is in our `architectures` set, # and match the build/skip rules diff --git a/cibuildwheel/macos.py b/cibuildwheel/platforms/macos.py similarity index 97% rename from cibuildwheel/macos.py rename to cibuildwheel/platforms/macos.py index b27348d7a..4c75859f0 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/platforms/macos.py @@ -15,26 +15,26 @@ from filelock import FileLock from packaging.version import Version -from . import errors -from .architecture import Architecture -from .ci import detect_ci_provider -from .environment import ParsedEnvironment -from .frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags -from .logger import log -from .options import Options -from .selector import BuildSelector -from .typing import PathOrStr -from .util import resources -from .util.cmd import call, shell -from .util.file import ( +from .. import errors +from ..architecture import Architecture +from ..ci import detect_ci_provider +from ..environment import ParsedEnvironment +from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags +from ..logger import log +from ..options import Options +from ..selector import BuildSelector +from ..typing import PathOrStr +from ..util import resources +from ..util.cmd import call, shell +from ..util.file import ( CIBW_CACHE_PATH, copy_test_sources, download, move_file, ) -from .util.helpers import prepare_command, unwrap -from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version -from .venv import find_uv, virtualenv +from ..util.helpers import prepare_command, unwrap +from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version +from ..venv import find_uv, virtualenv @functools.cache @@ -84,12 +84,15 @@ class PythonConfiguration: url: str +def all_python_configurations() -> list[PythonConfiguration]: + config_dicts = resources.read_python_configs("macos") + return [PythonConfiguration(**item) for item in config_dicts] + + def get_python_configurations( build_selector: BuildSelector, architectures: Set[Architecture] ) -> list[PythonConfiguration]: - full_python_configs = resources.read_python_configs("macos") - - python_configurations = [PythonConfiguration(**item) for item in full_python_configs] + python_configurations = all_python_configurations() # filter out configs that don't match any of the selected architectures python_configurations = [ diff --git a/cibuildwheel/pyodide.py b/cibuildwheel/platforms/pyodide.py similarity index 95% rename from cibuildwheel/pyodide.py rename to cibuildwheel/platforms/pyodide.py index 3c56df7e7..ca9edcb20 100644 --- a/cibuildwheel/pyodide.py +++ b/cibuildwheel/platforms/pyodide.py @@ -11,17 +11,17 @@ from filelock import FileLock -from . import errors -from .architecture import Architecture -from .environment import ParsedEnvironment -from .frontend import BuildFrontendConfig, get_build_frontend_extra_flags -from .logger import log -from .options import Options -from .selector import BuildSelector -from .typing import PathOrStr -from .util import resources -from .util.cmd import call, shell -from .util.file import ( +from .. import errors +from ..architecture import Architecture +from ..environment import ParsedEnvironment +from ..frontend import BuildFrontendConfig, get_build_frontend_extra_flags +from ..logger import log +from ..options import Options +from ..selector import BuildSelector +from ..typing import PathOrStr +from ..util import resources +from ..util.cmd import call, shell +from ..util.file import ( CIBW_CACHE_PATH, copy_test_sources, download, @@ -29,9 +29,9 @@ extract_zip, move_file, ) -from .util.helpers import prepare_command -from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version -from .venv import virtualenv +from ..util.helpers import prepare_command +from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version +from ..venv import virtualenv IS_WIN: Final[bool] = sys.platform.startswith("win") @@ -215,15 +215,16 @@ def setup_python( return env +def all_python_configurations() -> list[PythonConfiguration]: + full_python_configs = resources.read_python_configs("pyodide") + return [PythonConfiguration(**item) for item in full_python_configs] + + def get_python_configurations( build_selector: BuildSelector, architectures: Set[Architecture], # noqa: ARG001 ) -> list[PythonConfiguration]: - full_python_configs = resources.read_python_configs("pyodide") - - python_configurations = [PythonConfiguration(**item) for item in full_python_configs] - python_configurations = [c for c in python_configurations if build_selector(c.identifier)] - return python_configurations + return [c for c in all_python_configurations() if build_selector(c.identifier)] def build(options: Options, tmp_path: Path) -> None: diff --git a/cibuildwheel/windows.py b/cibuildwheel/platforms/windows.py similarity index 96% rename from cibuildwheel/windows.py rename to cibuildwheel/platforms/windows.py index 374b68d71..6d56236a2 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/platforms/windows.py @@ -11,20 +11,20 @@ from filelock import FileLock -from . import errors -from .architecture import Architecture -from .environment import ParsedEnvironment -from .frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags -from .logger import log -from .options import Options -from .selector import BuildSelector -from .typing import PathOrStr -from .util import resources -from .util.cmd import call, shell -from .util.file import CIBW_CACHE_PATH, copy_test_sources, download, extract_zip, move_file -from .util.helpers import prepare_command, unwrap -from .util.packaging import combine_constraints, find_compatible_wheel, get_pip_version -from .venv import find_uv, virtualenv +from .. import errors +from ..architecture import Architecture +from ..environment import ParsedEnvironment +from ..frontend import BuildFrontendConfig, BuildFrontendName, get_build_frontend_extra_flags +from ..logger import log +from ..options import Options +from ..selector import BuildSelector +from ..typing import PathOrStr +from ..util import resources +from ..util.cmd import call, shell +from ..util.file import CIBW_CACHE_PATH, copy_test_sources, download, extract_zip, move_file +from ..util.helpers import prepare_command, unwrap +from ..util.packaging import combine_constraints, find_compatible_wheel, get_pip_version +from ..venv import find_uv, virtualenv def get_nuget_args( @@ -59,13 +59,16 @@ class PythonConfiguration: url: str | None = None +def all_python_configurations() -> list[PythonConfiguration]: + config_dicts = resources.read_python_configs("windows") + return [PythonConfiguration(**item) for item in config_dicts] + + def get_python_configurations( build_selector: BuildSelector, architectures: Set[Architecture], ) -> list[PythonConfiguration]: - full_python_configs = resources.read_python_configs("windows") - - python_configurations = [PythonConfiguration(**item) for item in full_python_configs] + python_configurations = all_python_configurations() map_arch = {"32": Architecture.x86, "64": Architecture.AMD64, "ARM64": Architecture.ARM64} diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py index bd0ce597c..0e9736f53 100644 --- a/cibuildwheel/selector.py +++ b/cibuildwheel/selector.py @@ -1,6 +1,6 @@ import itertools from dataclasses import dataclass -from enum import Enum +from enum import StrEnum from fnmatch import fnmatch from typing import Any @@ -24,7 +24,7 @@ def selector_matches(patterns: str, string: str) -> bool: return any(fnmatch(string, pat) for pat in expanded_patterns) -class EnableGroup(Enum): +class EnableGroup(StrEnum): """ Groups of build selectors that are not enabled by default. """ @@ -33,6 +33,10 @@ class EnableGroup(Enum): CPythonPrerelease = "cpython-prerelease" PyPy = "pypy" + @classmethod + def all_groups(cls) -> frozenset["EnableGroup"]: + return frozenset(cls) + @dataclass(frozen=True, kw_only=True) class BuildSelector: diff --git a/test/test_0_basic.py b/test/test_0_basic.py index 317e876d4..4ef40a2c2 100644 --- a/test/test_0_basic.py +++ b/test/test_0_basic.py @@ -83,7 +83,7 @@ def test_allow_empty(tmp_path, add_args, env_allow_empty): # without error actual_wheels = utils.cibuildwheel_run( project_dir, - add_env={"CIBW_BUILD": "BUILD_NOTHING_AT_ALL", **env_allow_empty}, + add_env={"CIBW_SKIP": "*", **env_allow_empty}, add_args=add_args, ) diff --git a/test/utils.py b/test/utils.py index 4548f0a52..4ea3dd4c5 100644 --- a/test/utils.py +++ b/test/utils.py @@ -17,6 +17,7 @@ from cibuildwheel.architecture import Architecture from cibuildwheel.ci import CIProvider, detect_ci_provider +from cibuildwheel.selector import EnableGroup from cibuildwheel.util.file import CIBW_CACHE_PATH EMULATED_ARCHS: Final[list[str]] = sorted( @@ -120,7 +121,7 @@ def cibuildwheel_run( _update_pip_cache_dir(env) - env["CIBW_ENABLE"] = "cpython-prerelease cpython-freethreading pypy" + env["CIBW_ENABLE"] = " ".join(EnableGroup.all_groups()) if single_python: env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION) diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py index 6f691bf6f..9bd46cd99 100644 --- a/unit_test/get_platform_test.py +++ b/unit_test/get_platform_test.py @@ -7,7 +7,7 @@ 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"): diff --git a/unit_test/linux_build_steps_test.py b/unit_test/linux_build_steps_test.py index b493496ef..cd295a5e9 100644 --- a/unit_test/linux_build_steps_test.py +++ b/unit_test/linux_build_steps_test.py @@ -4,7 +4,7 @@ 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) diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py index 3751b78a5..e01c8c027 100644 --- a/unit_test/main_tests/conftest.py +++ b/unit_test/main_tests/conftest.py @@ -6,7 +6,8 @@ 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 diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index a869d615f..efc1074cc 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -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 diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py index 27b25377c..f17841fbe 100644 --- a/unit_test/option_prepare_test.py +++ b/unit_test/option_prepare_test.py @@ -8,7 +8,7 @@ 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 ignore_context_call(*args, **kwargs): 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 @@ def test_build_with_override_launches(monkeypatch, tmp_path): 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 diff --git a/unit_test/options_test.py b/unit_test/options_test.py index 7f497a375..143f7fdc0 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -6,13 +6,13 @@ 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,