feat: adding overrides

This commit is contained in:
Henry Schreiner
2021-10-16 21:34:02 -04:00
parent f511961b40
commit cd07697fe5
13 changed files with 405 additions and 89 deletions
+1
View File
@@ -59,6 +59,7 @@ repos:
- tomli
- types-certifi
- types-click
- types-dataclasses
- types-jinja2
- types-pyyaml
- types-requests
+17 -14
View File
@@ -14,7 +14,7 @@ from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.options import compute_options
from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
from cibuildwheel.util import (
BuildOptions,
AllBuildOptions,
BuildSelector,
Unbuffered,
detect_ci_provider,
@@ -148,12 +148,12 @@ def main() -> None:
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
)
build_options = compute_options(
all_build_options, build_options_by_selector = compute_options(
platform, package_dir, output_dir, args.config_file, args.archs, args.prerelease_pythons
)
identifiers = get_build_identifiers(
platform, build_options.build_selector, build_options.architectures
platform, all_build_options.build_selector, all_build_options.architectures
)
if args.print_build_identifiers:
@@ -161,6 +161,8 @@ def main() -> None:
print(identifier)
sys.exit(0)
build_options = AllBuildOptions(all_build_options, build_options_by_selector, identifiers)
# Add CIBUILDWHEEL environment variable
# This needs to be passed on to the docker container in linux.py
os.environ["CIBUILDWHEEL"] = "1"
@@ -200,7 +202,7 @@ def main() -> None:
assert_never(platform)
def print_preamble(platform: str, build_options: BuildOptions) -> None:
def print_preamble(platform: str, build_options: AllBuildOptions) -> None:
print(
textwrap.dedent(
"""
@@ -254,20 +256,21 @@ def get_build_identifiers(
return [config.identifier for config in python_configurations]
def detect_warnings(platform: str, build_options: BuildOptions) -> List[str]:
def detect_warnings(platform: str, all_options: AllBuildOptions) -> List[str]:
warnings = []
# warn about deprecated {python} and {pip}
for option_name in ["test_command", "before_build"]:
option_value = getattr(build_options, option_name)
for build_options in all_options.values():
for option_name in ["test_command", "before_build"]:
option_value = getattr(build_options, option_name)
if option_value and ("{python}" in option_value or "{pip}" in option_value):
# Reminder: in an f-string, double braces means literal single brace
msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
"and will be removed in a future release. Simply use 'python' or 'pip' instead."
)
warnings.append(msg)
if option_value and ("{python}" in option_value or "{pip}" in option_value):
# Reminder: in an f-string, double braces means literal single brace
msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, "
"and will be removed in a future release. Simply use 'python' or 'pip' instead."
)
warnings.append(msg)
return warnings
+9 -3
View File
@@ -1,3 +1,4 @@
import dataclasses
from typing import Dict, List, Mapping, Optional
import bashlex
@@ -60,10 +61,15 @@ class EnvironmentAssignment:
def __repr__(self) -> str:
return f"{self.name}={self.value}"
def __eq__(self, other: object) -> bool:
if isinstance(other, EnvironmentAssignment):
return self.name == other.name and self.value == other.value
return False
@dataclasses.dataclass
class ParsedEnvironment:
def __init__(self, assignments: List[EnvironmentAssignment]):
self.assignments = assignments
assignments: List[EnvironmentAssignment]
def as_dictionary(
self,
@@ -82,7 +88,7 @@ class ParsedEnvironment:
return [a.as_shell_assignment() for a in self.assignments]
def __repr__(self) -> str:
return f"ParsedEnvironment({[repr(a) for a in self.assignments]!r})"
return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})"
def parse_environment(env_string: str) -> ParsedEnvironment:
+26 -28
View File
@@ -9,7 +9,7 @@ from .docker_container import DockerContainer
from .logger import log
from .typing import PathOrStr, assert_never
from .util import (
BuildOptions,
AllBuildOptions,
BuildSelector,
NonPlatformWheelError,
get_build_verbosity_extra_flags,
@@ -54,7 +54,7 @@ def get_python_configurations(
def get_build_steps(
options: BuildOptions, python_configurations: List[PythonConfiguration]
all_options: AllBuildOptions, python_configurations: List[PythonConfiguration]
) -> Iterator[BuildStep]:
platforms = [
("cp", "manylinux_x86_64", "x86_64"),
@@ -73,15 +73,6 @@ def get_build_steps(
]
for implementation, platform_tag, platform_arch in platforms:
assert options.manylinux_images is not None
assert options.musllinux_images is not None
docker_image = (
options.manylinux_images[platform_arch]
if platform_tag.startswith("manylinux")
else options.musllinux_images[platform_arch]
)
platform_configs = [
c
for c in python_configurations
@@ -90,11 +81,15 @@ def get_build_steps(
if not platform_configs:
continue
yield BuildStep(platform_configs, platform_tag, docker_image)
for local_configs, docker_image in all_options.produce_image_batches(
platform_configs, platform_tag, platform_arch
):
# TODO: Validate that the options are not invalid for these selectors
yield BuildStep(local_configs, platform_tag, docker_image)
def build_on_docker(
options: BuildOptions,
all_options: AllBuildOptions,
platform_configs: List[PythonConfiguration],
docker: DockerContainer,
container_project_path: PurePath,
@@ -105,16 +100,16 @@ def build_on_docker(
log.step("Copying project into Docker...")
docker.copy_into(Path.cwd(), container_project_path)
if options.before_all:
if all_options.before_all:
log.step("Running before_all...")
env = docker.get_environment()
env["PATH"] = f'/opt/python/cp38-cp38/bin:{env["PATH"]}'
env["PIP_DISABLE_PIP_VERSION_CHECK"] = "1"
env = options.environment.as_dictionary(env, executor=docker.environment_executor)
env = all_options.environment.as_dictionary(env, executor=docker.environment_executor)
before_all_prepared = prepare_command(
options.before_all,
all_options.before_all,
project=container_project_path,
package=container_package_dir,
)
@@ -122,6 +117,7 @@ def build_on_docker(
for config in platform_configs:
log.build_start(config.identifier)
options = all_options[config.identifier]
dependency_constraint_flags: List[PathOrStr] = []
@@ -284,11 +280,11 @@ def build_on_docker(
log.step("Copying wheels back to host...")
# copy the output back into the host
docker.copy_out(container_output_dir, options.output_dir)
docker.copy_out(container_output_dir, all_options.output_dir)
log.step_end()
def build(options: BuildOptions) -> None:
def build(all_options: AllBuildOptions) -> None:
try:
# check docker is installed
subprocess.run(["docker", "--version"], check=True, stdout=subprocess.DEVNULL)
@@ -301,19 +297,19 @@ def build(options: BuildOptions) -> None:
)
sys.exit(2)
assert options.manylinux_images is not None
assert options.musllinux_images is not None
python_configurations = get_python_configurations(options.build_selector, options.architectures)
python_configurations = get_python_configurations(
all_options.build_selector, all_options.architectures
)
cwd = Path.cwd()
abs_package_dir = options.package_dir.resolve()
abs_package_dir = all_options.package_dir.resolve()
if cwd != abs_package_dir and cwd not in abs_package_dir.parents:
raise Exception("package_dir must be inside the working directory")
container_project_path = PurePath("/project")
container_package_dir = container_project_path / abs_package_dir.relative_to(cwd)
for build_step in get_build_steps(options, python_configurations):
for build_step in get_build_steps(all_options, python_configurations):
try:
log.step(f"Starting Docker image {build_step.docker_image}...")
@@ -324,7 +320,7 @@ def build(options: BuildOptions) -> None:
) as docker:
build_on_docker(
options,
all_options,
build_step.platform_configs,
docker,
container_project_path,
@@ -335,7 +331,7 @@ def build(options: BuildOptions) -> None:
log.step_end_with_error(
f"Command {error.cmd} failed with code {error.returncode}. {error.stdout}"
)
troubleshoot(options, error)
troubleshoot(all_options, error)
sys.exit(1)
@@ -346,16 +342,18 @@ def _matches_prepared_command(error_cmd: List[str], command_template: str) -> bo
return error_cmd[2].startswith(command_prefix)
def troubleshoot(options: BuildOptions, error: Exception) -> None:
def troubleshoot(all_options: AllBuildOptions, error: Exception) -> None:
if isinstance(error, subprocess.CalledProcessError) and (
error.cmd[0:4] == ["python", "-m", "pip", "wheel"]
or error.cmd[0:3] == ["python", "-m", "build"]
or _matches_prepared_command(error.cmd, options.repair_command)
or _matches_prepared_command(
error.cmd, all_options.general_build_options.repair_command
) # TODO
):
# the wheel build step failed
print("Checking for common errors...")
so_files = list(options.package_dir.glob("**/*.so"))
so_files = list(all_options.package_dir.glob("**/*.so"))
if so_files:
print(
+9 -6
View File
@@ -14,8 +14,8 @@ from .environment import ParsedEnvironment
from .logger import log
from .typing import Literal, PathOrStr, assert_never
from .util import (
AllBuildOptions,
BuildFrontend,
BuildOptions,
BuildSelector,
NonPlatformWheelError,
download,
@@ -343,26 +343,29 @@ def setup_python(
return env
def build(options: BuildOptions) -> None:
def build(all_options: AllBuildOptions) -> None:
temp_dir = Path(tempfile.mkdtemp(prefix="cibuildwheel"))
built_wheel_dir = temp_dir / "built_wheel"
repaired_wheel_dir = temp_dir / "repaired_wheel"
all_options.check_build_selectors()
try:
if options.before_all:
if all_options.before_all:
log.step("Running before_all...")
env = options.environment.as_dictionary(prev_environment=os.environ)
env = all_options.environment.as_dictionary(prev_environment=os.environ)
env.setdefault("MACOSX_DEPLOYMENT_TARGET", "10.9")
before_all_prepared = prepare_command(
options.before_all, project=".", package=options.package_dir
all_options.before_all, project=".", package=all_options.package_dir
)
call([before_all_prepared], shell=True, env=env)
python_configurations = get_python_configurations(
options.build_selector, options.architectures
all_options.build_selector, all_options.architectures
)
for config in python_configurations:
options = all_options[config.identifier]
log.build_start(config.identifier)
config_is_arm64 = config.identifier.endswith("arm64")
+70 -12
View File
@@ -1,9 +1,10 @@
import copy
import os
import sys
import traceback
from configparser import ConfigParser
from pathlib import Path
from typing import Any, Dict, List, Mapping, Optional, Set, Tuple, Union
from typing import Any, Dict, List, Mapping, Optional, Set, Tuple, TypeVar, Union
import tomli
from packaging.specifiers import SpecifierSet
@@ -58,6 +59,9 @@ def _dig_first(*pairs: Tuple[Mapping[str, Setting], str], ignore_empty: bool = F
raise KeyError(key)
T = TypeVar("T", bound="ConfigOptions")
class ConfigOptions:
"""
Gets options from the environment, config or defaults, optionally scoped
@@ -117,12 +121,27 @@ class ConfigOptions:
self.config_options = config_options
self.config_platform_options = config_platform_options
self.overrides: Dict[str, Dict[str, Any]] = {}
self.current_override: str = "*"
overrides = self.config_options.get("overrides")
if overrides is not None:
if not isinstance(overrides, list):
raise ConfigOptionError('"tool.cibuildwheel.overrides" must be a list')
for override in overrides:
selector = override.pop("select")
if isinstance(selector, list):
selector = " ".join(selector)
if selector in {"", "*"}:
raise ConfigOptionError("select all must not be used in an override")
self.overrides[selector.strip()] = override
def _is_valid_global_option(self, name: str) -> bool:
"""
Returns True if an option with this name is allowed in the
[tool.cibuildwheel] section of a config file.
"""
allowed_option_names = self.default_options.keys() | PLATFORMS
allowed_option_names = self.default_options.keys() | PLATFORMS | {"overrides"}
return name in allowed_option_names
@@ -151,6 +170,14 @@ class ConfigOptions:
return global_options, platform_options
def override(self: T, selector: str) -> T:
"""
Start an override scope.
"""
other = copy.copy(self)
other.current_override = selector
return other
def __call__(
self,
name: str,
@@ -179,9 +206,11 @@ class ConfigOptions:
# get the option from the environment, then the config file, then finally the default.
# platform-specific options are preferred, if they're allowed.
empty: Dict[str, Any] = {}
result = _dig_first(
(os.environ if env_plat else {}, plat_envvar), # type: ignore[arg-type]
(os.environ, envvar),
(self.overrides.get(self.current_override, empty), name),
(self.config_platform_options, name),
(self.config_options, name),
(self.default_platform_options, name),
@@ -210,7 +239,7 @@ def compute_options(
config_file: Optional[str],
args_archs: Optional[str],
prerelease_pythons: bool,
) -> BuildOptions:
) -> Tuple[BuildOptions, Dict[str, BuildOptions]]:
"""
Compute the options from the environment and configuration file.
"""
@@ -263,11 +292,25 @@ def compute_options(
)
test_selector = TestSelector(skip_config=test_skip)
return _compute_single_options(
return _compute_all_options(
options, args_archs, build_selector, test_selector, platform, package_dir, output_dir
)
def _get_pinned_docker_images() -> Mapping[str, Mapping[str, str]]:
"""
This looks like a dict of dicts, e.g.
{ 'x86_64': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
'i686': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
'pypy_x86_64': {'manylinux2010': '...' }
... }
"""
pinned_docker_images_file = resources_dir / "pinned_docker_images.cfg"
all_pinned_docker_images = ConfigParser()
all_pinned_docker_images.read(pinned_docker_images_file)
return all_pinned_docker_images
def _compute_single_options(
options: ConfigOptions,
args_archs: Optional[str],
@@ -337,14 +380,7 @@ def _compute_single_options(
manylinux_images: Dict[str, str] = {}
musllinux_images: Dict[str, str] = {}
if platform == "linux":
pinned_docker_images_file = resources_dir / "pinned_docker_images.cfg"
all_pinned_docker_images = ConfigParser()
all_pinned_docker_images.read(pinned_docker_images_file)
# all_pinned_docker_images looks like a dict of dicts, e.g.
# { 'x86_64': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
# 'i686': {'manylinux1': '...', 'manylinux2010': '...', 'manylinux2014': '...'},
# 'pypy_x86_64': {'manylinux2010': '...' }
# ... }
all_pinned_docker_images = _get_pinned_docker_images()
for build_platform in MANYLINUX_ARCHS:
pinned_images = all_pinned_docker_images[build_platform]
@@ -359,6 +395,7 @@ def _compute_single_options(
else:
image = config_value
assert image is not None
manylinux_images[build_platform] = image
for build_platform in MUSLLINUX_ARCHS:
@@ -397,6 +434,27 @@ def _compute_single_options(
)
def _compute_all_options(
options: ConfigOptions,
args_archs: Optional[str],
build_selector: BuildSelector,
test_selector: TestSelector,
platform: PlatformName,
package_dir: Path,
output_dir: Path,
) -> Tuple[BuildOptions, Dict[str, BuildOptions]]:
args = (args_archs, build_selector, test_selector, platform, package_dir, output_dir)
general_build_options = _compute_single_options(options, *args)
selectors = options.overrides.keys()
build_options_by_selector = {
s: _compute_single_options(options.override(s), *args) for s in selectors
}
return general_build_options, build_options_by_selector
def deprecated_selectors(name: str, selector: str, *, error: bool = False) -> None:
if "p2" in selector or "p35" in selector:
msg = f"cibuildwheel 2.x no longer supports Python < 3.6. Please use the 1.x series or update {name}"
+6 -4
View File
@@ -12,14 +12,16 @@ else:
__all__ = (
"Final",
"Literal",
"TypedDict",
"Set",
"Union",
"PopenBytes",
"PLATFORMS",
"PathOrStr",
"PlatformName",
"Protocol",
"PLATFORMS",
"PopenBytes",
"Protocol",
"Set",
"TypedDict",
"Union",
"assert_never",
)
+129 -2
View File
@@ -1,4 +1,5 @@
import contextlib
import dataclasses
import fnmatch
import itertools
import os
@@ -9,10 +10,23 @@ import sys
import textwrap
import time
import urllib.request
from collections import defaultdict
from enum import Enum
from pathlib import Path
from time import sleep
from typing import Dict, Iterator, List, NamedTuple, Optional, Set
from typing import (
Counter,
Dict,
Iterable,
Iterator,
List,
Mapping,
NamedTuple,
Optional,
Set,
Tuple,
TypeVar,
)
import bracex
import certifi
@@ -22,7 +36,7 @@ from packaging.version import Version
from .architecture import Architecture
from .environment import ParsedEnvironment
from .typing import Literal, PathOrStr, PlatformName
from .typing import Literal, PathOrStr, PlatformName, Protocol
resources_dir = Path(__file__).parent / "resources"
@@ -250,6 +264,119 @@ class BuildOptions(NamedTuple):
return "\n".join(res)
class SimpleConfig(Protocol):
@property
def identifier(self) -> str:
...
SC = TypeVar("SC", bound=SimpleConfig)
T = TypeVar("T", bound="AllBuildOptions")
@dataclasses.dataclass
class AllBuildOptions:
general_build_options: BuildOptions
build_options_by_selector: Dict[str, BuildOptions]
identifiers: List[str]
def __getitem__(self, identifier: str) -> BuildOptions:
for sel in self.build_options_by_selector:
bs = BuildSelector(build_config=sel, skip_config="")
if bs(identifier):
return self.build_options_by_selector[sel]
return self.general_build_options
def values(self) -> Iterable[BuildOptions]:
return itertools.chain(
[self.general_build_options], self.build_options_by_selector.values()
)
# These values are not overridable in some cases
@property
def package_dir(self) -> Path:
return self.general_build_options.package_dir
@property
def build_selector(self) -> BuildSelector:
return self.general_build_options.build_selector
@property
def output_dir(self) -> Path:
return self.general_build_options.output_dir
@property
def architectures(self) -> Set[Architecture]:
return self.general_build_options.architectures
@property
def environment(self) -> ParsedEnvironment:
return self.general_build_options.environment
@property
def before_all(self) -> str:
return self.general_build_options.before_all
def produce_image_batches(
self,
configurations: List[SC],
platform_tag: str,
platform_arch: str,
) -> Iterator[Tuple[List[SC], str]]:
docker_images: Mapping[str, List[SC]] = defaultdict(list)
for config in configurations:
build_options = self[config.identifier]
images = (
build_options.manylinux_images
if platform_tag.startswith("manylinux")
else build_options.musllinux_images
)
assert images is not None
docker_images[images[platform_arch]].append(config)
for image, configs in docker_images.items():
# TODO: check for colisions for identifiers in configs
# Some settings (before-all) are not overridable in the same image
yield configs, image
def check_build_selectors(self) -> None:
hits = Counter[str]()
for sel in self.build_options_by_selector:
bs = BuildSelector(build_config=sel, skip_config="")
hits += Counter(i for i in self.identifiers if bs(i))
non_unique_identifers = {idnt for idnt, count in hits.items() if count > 1}
if non_unique_identifers:
msg = "cibuildwheel: error, the windows/macOS selectors must match uniquely"
print(msg, file=sys.stderr)
for sel in self.build_options_by_selector:
bs = BuildSelector(build_config=sel, skip_config="")
for i in non_unique_identifers:
if bs(i):
print(f" {sel}: {i} (nonunique match)")
sys.exit(1)
def __str__(self) -> str:
results = []
for option in sorted(self.general_build_options._asdict().keys()):
variations = {
key: value._asdict()[option]
for key, value in self.build_options_by_selector.items()
}
variations["*"] = self.general_build_options._asdict()[option]
if len({repr(v) for v in variations.values()}) == 1:
results.append(f"{option}: {variations['*']!r}")
else:
results.append(f"{option}:")
for key, value in sorted(variations.items()):
results.append(f" {key}: {value!r}")
return "\n".join(results)
class NonPlatformWheelError(Exception):
def __init__(self) -> None:
message = textwrap.dedent(
+9 -6
View File
@@ -12,8 +12,8 @@ from .environment import ParsedEnvironment
from .logger import log
from .typing import PathOrStr, assert_never
from .util import (
AllBuildOptions,
BuildFrontend,
BuildOptions,
BuildSelector,
NonPlatformWheelError,
download,
@@ -246,25 +246,28 @@ def setup_python(
return env
def build(options: BuildOptions) -> None:
def build(all_options: AllBuildOptions) -> None:
temp_dir = Path(tempfile.mkdtemp(prefix="cibuildwheel"))
built_wheel_dir = temp_dir / "built_wheel"
repaired_wheel_dir = temp_dir / "repaired_wheel"
all_options.check_build_selectors()
try:
if options.before_all:
if all_options.before_all:
log.step("Running before_all...")
env = options.environment.as_dictionary(prev_environment=os.environ)
env = all_options.environment.as_dictionary(prev_environment=os.environ)
before_all_prepared = prepare_command(
options.before_all, project=".", package=options.package_dir
all_options.before_all, project=".", package=all_options.package_dir
)
shell(before_all_prepared, env=env)
python_configurations = get_python_configurations(
options.build_selector, options.architectures
all_options.build_selector, all_options.architectures
)
for config in python_configurations:
options = all_options[config.identifier]
log.build_start(config.identifier)
dependency_constraint_flags: Sequence[PathOrStr] = []
+1
View File
@@ -36,6 +36,7 @@ install_requires =
certifi
packaging
tomli
dataclasses;python_version < '3.7'
typing_extensions;python_version < '3.8'
python_requires = >=3.6
include_package_data = True
+61
View File
@@ -0,0 +1,61 @@
from pathlib import Path
from cibuildwheel.__main__ import get_build_identifiers
from cibuildwheel.environment import parse_environment
from cibuildwheel.options import _get_pinned_docker_images, compute_options
from cibuildwheel.util import AllBuildOptions
PYPROJECT_1 = """
[tool.cibuildwheel]
build = ["cp38*", "cp37*"]
environment = {FOO="BAR"}
test-command = "pyproject"
manylinux-x86_64-image = "manylinux1"
[tool.cibuildwheel.macos]
test-requires = "else"
[[tool.cibuildwheel.overrides]]
select = "cp37*"
test-command = "pyproject-override"
manylinux-x86_64-image = "manylinux2014"
"""
def test_all_build_options_1(tmp_path):
with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_1)
all_build_options, build_options_by_selector = compute_options(
"linux", tmp_path, Path("dist"), None, None, False
)
identifiers = get_build_identifiers(
"linux", all_build_options.build_selector, all_build_options.architectures
)
build_options = AllBuildOptions(all_build_options, build_options_by_selector, identifiers)
override_display = """\
test_command:
*: 'pyproject'
cp37*: 'pyproject-override'"""
assert override_display in str(build_options)
assert build_options.environment == parse_environment('FOO="BAR"')
all_pinned_docker_images = _get_pinned_docker_images()
pinned_x86_64_docker_image = all_pinned_docker_images["x86_64"]
local = build_options["cp38-manylinux_x86_64"]
assert local.manylinux_images is not None
assert local.test_command == "pyproject"
assert local.manylinux_images["x86_64"] == pinned_x86_64_docker_image["manylinux1"]
local = build_options["cp37-manylinux_x86_64"]
assert local.manylinux_images is not None
assert local.test_command == "pyproject-override"
assert local.manylinux_images["x86_64"] == pinned_x86_64_docker_image["manylinux2014"]
+22 -14
View File
@@ -18,13 +18,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
main()
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
assert intercepted_build_args.args[0].general_build_options.output_dir == OUTPUT_DIR
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
main()
assert intercepted_build_args.args[0].output_dir == Path("wheelhouse")
assert intercepted_build_args.args[0].general_build_options.output_dir == Path("wheelhouse")
@pytest.mark.parametrize("also_set_environment", [False, True])
@@ -37,7 +37,7 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a
main()
assert intercepted_build_args.args[0].output_dir == OUTPUT_DIR
assert intercepted_build_args.args[0].general_build_options.output_dir == OUTPUT_DIR
def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty):
@@ -49,7 +49,7 @@ def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_emp
main()
intercepted_build_selector = intercepted_build_args.args[0].build_selector
intercepted_build_selector = intercepted_build_args.args[0].general_build_options.build_selector
assert isinstance(intercepted_build_selector, BuildSelector)
assert intercepted_build_selector("build24-this")
assert not intercepted_build_selector("skip65-that")
@@ -98,9 +98,12 @@ def test_manylinux_images(
main()
if platform == "linux":
assert fnmatch(intercepted_build_args.args[0].manylinux_images[architecture], full_image)
assert fnmatch(
intercepted_build_args.args[0].general_build_options.manylinux_images[architecture],
full_image,
)
else:
assert intercepted_build_args.args[0].manylinux_images is None
assert intercepted_build_args.args[0].general_build_options.manylinux_images is None
def get_default_repair_command(platform):
@@ -129,7 +132,7 @@ def test_repair_command(
main()
expected_repair = repair_command or get_default_repair_command(platform)
assert intercepted_build_args.args[0].repair_command == expected_repair
assert intercepted_build_args.args[0].general_build_options.repair_command == expected_repair
@pytest.mark.parametrize(
@@ -147,7 +150,7 @@ def test_environment(environment, platform_specific, platform, intercepted_build
main()
intercepted_environment = intercepted_build_args.args[0].environment
intercepted_environment = intercepted_build_args.args[0].general_build_options.environment
assert isinstance(intercepted_environment, ParsedEnvironment)
assert intercepted_environment.as_dictionary(prev_environment={}) == environment
@@ -166,7 +169,10 @@ def test_test_requires(
main()
assert intercepted_build_args.args[0].test_requires == (test_requires or "").split()
assert (
intercepted_build_args.args[0].general_build_options.test_requires
== (test_requires or "").split()
)
@pytest.mark.parametrize("test_extras", [None, "extras"])
@@ -181,7 +187,7 @@ def test_test_extras(test_extras, platform_specific, platform, intercepted_build
main()
assert intercepted_build_args.args[0].test_extras == (
assert intercepted_build_args.args[0].general_build_options.test_extras == (
"[" + test_extras + "]" if test_extras else ""
)
@@ -200,7 +206,7 @@ def test_test_command(
main()
assert intercepted_build_args.args[0].test_command == (test_command or "")
assert intercepted_build_args.args[0].general_build_options.test_command == (test_command or "")
@pytest.mark.parametrize("before_build", [None, "before --build"])
@@ -217,7 +223,7 @@ def test_before_build(
main()
assert intercepted_build_args.args[0].before_build == (before_build or "")
assert intercepted_build_args.args[0].general_build_options.before_build == (before_build or "")
@pytest.mark.parametrize("build_verbosity", [None, 0, 2, -2, 4, -4])
@@ -235,7 +241,9 @@ def test_build_verbosity(
main()
expected_verbosity = max(-3, min(3, int(build_verbosity or 0)))
assert intercepted_build_args.args[0].build_verbosity == expected_verbosity
assert (
intercepted_build_args.args[0].general_build_options.build_verbosity == expected_verbosity
)
@pytest.mark.parametrize(
@@ -286,4 +294,4 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a
main()
assert intercepted_build_args.args[0].before_all == (before_all or "")
assert intercepted_build_args.args[0].general_build_options.before_all == (before_all or "")
+45
View File
@@ -246,3 +246,48 @@ def test_dig_first(ignore_empty):
(d4, "other"),
ignore_empty=ignore_empty,
)
PYPROJECT_2 = """
[tool.cibuildwheel]
build = ["cp38*", "cp37*"]
environment = {FOO="BAR"}
test-command = "pyproject"
manylinux-x86_64-image = "manylinux1"
[tool.cibuildwheel.macos]
test-requires = "else"
[[tool.cibuildwheel.overrides]]
select = "cp37*"
test-command = "pyproject-override"
manylinux-x86_64-image = "manylinux2014"
"""
def test_pyproject_2(tmp_path, platform):
with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_2)
options = ConfigOptions(tmp_path, platform=platform)
assert options("test-command") == "pyproject"
assert options.override("random")("test-command") == "pyproject"
assert options.override("cp37*")("test-command") == "pyproject-override"
def test_overrides_not_a_list(tmp_path, platform):
with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(
"""\
[tool.cibuildwheel]
build = ["cp38*", "cp37*"]
[tool.cibuildwheel.overrides]
select = "cp37*"
test-command = "pyproject-override"
"""
)
with pytest.raises(ConfigOptionError):
ConfigOptions(tmp_path, platform=platform)