Merge pull request #1352 from pypa/improve-preamble

Improve the formatting of the preamble
This commit is contained in:
Joe Rickerby
2022-12-05 21:17:30 +00:00
committed by GitHub
11 changed files with 294 additions and 100 deletions
+9 -3
View File
@@ -22,9 +22,11 @@ from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
from cibuildwheel.util import ( from cibuildwheel.util import (
CIBW_CACHE_PATH, CIBW_CACHE_PATH,
BuildSelector, BuildSelector,
CIProvider,
Unbuffered, Unbuffered,
chdir, chdir,
detect_ci_provider, detect_ci_provider,
fix_ansi_codes_for_github_actions,
) )
@@ -229,7 +231,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
) )
sys.exit(2) sys.exit(2)
options = compute_options(platform=platform, command_line_arguments=args) options = compute_options(platform=platform, command_line_arguments=args, env=os.environ)
package_dir = options.globals.package_dir package_dir = options.globals.package_dir
package_files = {"setup.py", "setup.cfg", "pyproject.toml"} package_files = {"setup.py", "setup.cfg", "pyproject.toml"}
@@ -318,9 +320,13 @@ def print_preamble(platform: str, options: Options, identifiers: list[str]) -> N
print(f"cibuildwheel version {cibuildwheel.__version__}\n") print(f"cibuildwheel version {cibuildwheel.__version__}\n")
print("Build options:") print("Build options:")
print(f" platform: {platform!r}") print(f" platform: {platform}")
print(textwrap.indent(options.summary(identifiers), " ")) options_summary = textwrap.indent(options.summary(identifiers), " ")
if detect_ci_provider() == CIProvider.github_actions:
options_summary = fix_ansi_codes_for_github_actions(options_summary)
print(options_summary)
print()
print(f"Cache folder: {CIBW_CACHE_PATH}") print(f"Cache folder: {CIBW_CACHE_PATH}")
warnings = detect_warnings(options=options, identifiers=identifiers) warnings = detect_warnings(options=options, identifiers=identifiers)
+3
View File
@@ -43,6 +43,9 @@ class Architecture(Enum):
def __lt__(self, other: Architecture) -> bool: def __lt__(self, other: Architecture) -> bool:
return self.value < other.value return self.value < other.value
def __str__(self) -> str:
return self.name
@staticmethod @staticmethod
def parse_config(config: str, platform: PlatformName) -> set[Architecture]: def parse_config(config: str, platform: PlatformName) -> set[Architecture]:
result = set() result = set()
+4 -1
View File
@@ -70,7 +70,7 @@ class EnvironmentAssignmentRaw:
self.value = value self.value = value
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.name}: {self.value}" return f"{self.name}={self.value}"
def evaluated_value(self, **_: Any) -> str: def evaluated_value(self, **_: Any) -> str:
return self.value return self.value
@@ -131,6 +131,9 @@ class ParsedEnvironment:
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})" return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})"
def options_summary(self) -> Any:
return self.assignments
def parse_environment(env_string: str) -> ParsedEnvironment: def parse_environment(env_string: str) -> ParsedEnvironment:
env_items = split_env_items(env_string) env_items = split_env_items(env_string)
+1
View File
@@ -228,6 +228,7 @@ class Colors:
self.bright_red = "\033[91m" if enabled else "" self.bright_red = "\033[91m" if enabled else ""
self.bright_green = "\033[92m" if enabled else "" self.bright_green = "\033[92m" if enabled else ""
self.white = "\033[37m\033[97m" if enabled else "" self.white = "\033[37m\033[97m" if enabled else ""
self.gray = "\033[38;5;244m" if enabled else ""
self.bg_grey = "\033[48;5;235m" if enabled else "" self.bg_grey = "\033[48;5;235m" if enabled else ""
+130 -22
View File
@@ -1,13 +1,14 @@
from __future__ import annotations from __future__ import annotations
import collections
import configparser import configparser
import contextlib import contextlib
import dataclasses import dataclasses
import difflib import difflib
import functools import functools
import os
import shlex import shlex
import sys import sys
import textwrap
import traceback import traceback
from pathlib import Path from pathlib import Path
from typing import Any, Callable, Dict, Generator, Iterator, List, Mapping, Union, cast from typing import Any, Callable, Dict, Generator, Iterator, List, Mapping, Union, cast
@@ -21,6 +22,7 @@ from packaging.specifiers import SpecifierSet
from .architecture import Architecture from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .logger import log
from .oci_container import ContainerEngine from .oci_container import ContainerEngine
from .projectfiles import get_requires_python_str from .projectfiles import get_requires_python_str
from .typing import PLATFORMS, Literal, NotRequired, PlatformName, TypedDict from .typing import PLATFORMS, Literal, NotRequired, PlatformName, TypedDict
@@ -52,6 +54,20 @@ class CommandLineArguments:
allow_empty: bool allow_empty: bool
prerelease_pythons: bool prerelease_pythons: bool
@staticmethod
def defaults() -> CommandLineArguments:
return CommandLineArguments(
platform="auto",
allow_empty=False,
archs=None,
only=None,
config_file="",
output_dir=Path("wheelhouse"),
package_dir=Path("."),
prerelease_pythons=False,
print_build_identifiers=False,
)
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class GlobalOptions: class GlobalOptions:
@@ -176,9 +192,11 @@ class OptionsReader:
config_file_path: Path | None = None, config_file_path: Path | None = None,
*, *,
platform: PlatformName, platform: PlatformName,
env: Mapping[str, str],
disallow: dict[str, set[str]] | None = None, disallow: dict[str, set[str]] | None = None,
) -> None: ) -> None:
self.platform = platform self.platform = platform
self.env = env
self.disallow = disallow or {} self.disallow = disallow or {}
# Open defaults.toml, loading both global and platform sections # Open defaults.toml, loading both global and platform sections
@@ -319,8 +337,8 @@ class OptionsReader:
# get the option from the environment, then the config file, then finally the default. # get the option from the environment, then the config file, then finally the default.
# platform-specific options are preferred, if they're allowed. # platform-specific options are preferred, if they're allowed.
result = _dig_first( result = _dig_first(
(os.environ if env_plat else {}, plat_envvar), # type: ignore[arg-type] (self.env if env_plat else {}, plat_envvar),
(os.environ, envvar), (self.env, envvar),
*[(o.options, name) for o in active_config_overrides], *[(o.options, name) for o in active_config_overrides],
(self.config_platform_options, name), (self.config_platform_options, name),
(self.config_options, name), (self.config_options, name),
@@ -362,13 +380,21 @@ def _inner_fmt(k: str, v: Any, table: TableFmt) -> Iterator[str]:
class Options: class Options:
def __init__(self, platform: PlatformName, command_line_arguments: CommandLineArguments): def __init__(
self,
platform: PlatformName,
command_line_arguments: CommandLineArguments,
env: Mapping[str, str],
read_config_file: bool = True,
):
self.platform = platform self.platform = platform
self.command_line_arguments = command_line_arguments self.command_line_arguments = command_line_arguments
self.env = env
self.reader = OptionsReader( self.reader = OptionsReader(
self.config_file_path, self.config_file_path if read_config_file else None,
platform=platform, platform=platform,
env=env,
disallow=DISALLOWED_OPTIONS, disallow=DISALLOWED_OPTIONS,
) )
@@ -402,13 +428,13 @@ class Options:
test_skip = self.reader.get("test-skip", env_plat=False, sep=" ") test_skip = self.reader.get("test-skip", env_plat=False, sep=" ")
prerelease_pythons = args.prerelease_pythons or strtobool( prerelease_pythons = args.prerelease_pythons or strtobool(
os.environ.get("CIBW_PRERELEASE_PYTHONS", "0") self.env.get("CIBW_PRERELEASE_PYTHONS", "0")
) )
# This is not supported in tool.cibuildwheel, as it comes from a standard location. # This is not supported in tool.cibuildwheel, as it comes from a standard location.
# Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py # Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py
requires_python_str: str | None = ( requires_python_str: str | None = (
os.environ.get("CIBW_PROJECT_REQUIRES_PYTHON") or self.package_requires_python_str self.env.get("CIBW_PROJECT_REQUIRES_PYTHON") or self.package_requires_python_str
) )
requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str) requires_python = None if requires_python_str is None else SpecifierSet(requires_python_str)
@@ -497,7 +523,7 @@ class Options:
if self.platform == "linux": if self.platform == "linux":
for env_var_name in environment_pass: for env_var_name in environment_pass:
with contextlib.suppress(KeyError): with contextlib.suppress(KeyError):
environment.add(env_var_name, os.environ[env_var_name]) environment.add(env_var_name, self.env[env_var_name])
if dependency_versions == "pinned": if dependency_versions == "pinned":
dependency_constraints: None | ( dependency_constraints: None | (
@@ -594,37 +620,119 @@ class Options:
deprecated_selectors("CIBW_SKIP", build_selector.skip_config) deprecated_selectors("CIBW_SKIP", build_selector.skip_config)
deprecated_selectors("CIBW_TEST_SKIP", test_selector.skip_config) deprecated_selectors("CIBW_TEST_SKIP", test_selector.skip_config)
def summary(self, identifiers: list[str]) -> str: @cached_property
lines = [ def defaults(self) -> Options:
f"{option_name}: {option_value!r}" return Options(
for option_name, option_value in sorted(dataclasses.asdict(self.globals).items()) platform=self.platform,
] command_line_arguments=CommandLineArguments.defaults(),
env={},
read_config_file=False,
)
build_option_defaults = self.build_options(identifier=None) def summary(self, identifiers: list[str]) -> str:
lines = []
global_option_names = sorted(f.name for f in dataclasses.fields(self.globals))
for option_name in global_option_names:
option_value = getattr(self.globals, option_name)
default_value = getattr(self.defaults.globals, option_name)
lines.append(self.option_summary(option_name, option_value, default_value))
build_options = self.build_options(identifier=None)
build_options_defaults = self.defaults.build_options(identifier=None)
build_options_for_identifier = { build_options_for_identifier = {
identifier: self.build_options(identifier) for identifier in identifiers identifier: self.build_options(identifier) for identifier in identifiers
} }
for option_name, default_value in sorted(dataclasses.asdict(build_option_defaults).items()): build_option_names = sorted(f.name for f in dataclasses.fields(build_options))
for option_name in build_option_names:
if option_name == "globals": if option_name == "globals":
continue continue
lines.append(f"{option_name}: {default_value!r}") option_value = getattr(build_options, option_name)
default_value = getattr(build_options_defaults, option_name)
overrides = {
i: getattr(build_options_for_identifier[i], option_name) for i in identifiers
}
# if any identifiers have an overridden value, print that too lines.append(
for identifier in identifiers: self.option_summary(option_name, option_value, default_value, overrides=overrides)
option_value = getattr(build_options_for_identifier[identifier], option_name) )
if option_value != default_value:
lines.append(f" {identifier}: {option_value!r}")
return "\n".join(lines) return "\n".join(lines)
def option_summary(
self,
option_name: str,
option_value: Any,
default_value: Any,
overrides: dict[str, Any] | None = None,
) -> str:
"""
Return a summary of the option value, including any overrides, with
ANSI 'dim' color if it's the default.
"""
value_str = self.option_summary_value(option_value)
default_value_str = self.option_summary_value(default_value)
overrides_value_strs = {
k: self.option_summary_value(v) for k, v in (overrides or {}).items()
}
# if the override value is the same as the non-overridden value, don't print it
overrides_value_strs = {k: v for k, v in overrides_value_strs.items() if v != value_str}
has_been_set = (value_str != default_value_str) or overrides_value_strs
c = log.colors
result = c.gray if not has_been_set else ""
result += f"{option_name}: "
if overrides_value_strs:
overrides_groups = collections.defaultdict(list)
for k, v in overrides_value_strs.items():
overrides_groups[v].append(k)
result += "\n *: "
result += self.indent_if_multiline(value_str, " ")
for override_value_str, identifiers in overrides_groups.items():
result += f"\n {', '.join(identifiers)}: "
result += self.indent_if_multiline(override_value_str, " ")
else:
result += self.indent_if_multiline(value_str, " ")
result += c.end
return result
def indent_if_multiline(self, value: str, indent: str) -> str:
if "\n" in value:
return "\n" + textwrap.indent(value.strip(), indent)
else:
return value
def option_summary_value(self, option_value: Any) -> str:
if hasattr(option_value, "options_summary"):
option_value = option_value.options_summary()
if isinstance(option_value, list):
return "".join(f"{el}\n" for el in option_value)
if isinstance(option_value, set):
return ", ".join(str(el) for el in sorted(option_value))
if isinstance(option_value, dict):
return "".join(f"{k}: {v}\n" for k, v in option_value.items())
return str(option_value)
def compute_options( def compute_options(
platform: PlatformName, platform: PlatformName,
command_line_arguments: CommandLineArguments, command_line_arguments: CommandLineArguments,
env: Mapping[str, str],
) -> Options: ) -> Options:
options = Options(platform=platform, command_line_arguments=command_line_arguments) options = Options(platform=platform, command_line_arguments=command_line_arguments, env=env)
options.check_for_deprecated_options() options.check_for_deprecated_options()
return options return options
+45
View File
@@ -272,6 +272,14 @@ class BuildSelector:
return should_build and not should_skip return should_build and not should_skip
def options_summary(self) -> Any:
return {
"build_config": self.build_config,
"skip_config": self.skip_config,
"requires_python": str(self.requires_python),
"prerelease_pythons": self.prerelease_pythons,
}
@dataclass(frozen=True) @dataclass(frozen=True)
class TestSelector: class TestSelector:
@@ -285,6 +293,9 @@ class TestSelector:
should_skip = selector_matches(self.skip_config, build_id) should_skip = selector_matches(self.skip_config, build_id)
return not should_skip return not should_skip
def options_summary(self) -> Any:
return {"skip_config": self.skip_config}
# Taken from https://stackoverflow.com/a/107717 # Taken from https://stackoverflow.com/a/107717
class Unbuffered: class Unbuffered:
@@ -358,6 +369,12 @@ class DependencyConstraints:
return self.base_file_path == o.base_file_path return self.base_file_path == o.base_file_path
def options_summary(self) -> Any:
if self == DependencyConstraints.with_defaults():
return "pinned"
else:
return self.base_file_path.name
class NonPlatformWheelError(Exception): class NonPlatformWheelError(Exception):
def __init__(self) -> None: def __init__(self) -> None:
@@ -657,3 +674,31 @@ def chdir(new_path: Path | str) -> Generator[None, None, None]:
yield yield
finally: finally:
os.chdir(cwd) os.chdir(cwd)
def fix_ansi_codes_for_github_actions(text: str) -> str:
"""
Github Actions forgets the current ANSI style on every new line. This
function repeats the current ANSI style on every new line.
"""
ansi_code_regex = re.compile(r"(\033\[[0-9;]*m)")
ansi_codes: list[str] = []
output = ""
for line in text.splitlines(keepends=True):
# add the current ANSI codes to the beginning of the line
output += "".join(ansi_codes) + line
# split the line at each ANSI code
parts = ansi_code_regex.split(line)
# if there are any ANSI codes, save them
if len(parts) > 1:
# iterate over the ANSI codes in this line
for code in parts[1::2]:
if code == "\033[0m":
# reset the list of ANSI codes when the clear code is found
ansi_codes = []
else:
ansi_codes.append(code)
return output
+3 -5
View File
@@ -6,9 +6,7 @@ from pprint import pprint
import cibuildwheel.linux import cibuildwheel.linux
import cibuildwheel.oci_container import cibuildwheel.oci_container
from cibuildwheel.options import Options from cibuildwheel.options import CommandLineArguments, Options
from .utils import get_default_command_line_arguments
def test_linux_container_split(tmp_path: Path, monkeypatch): def test_linux_container_split(tmp_path: Path, monkeypatch):
@@ -16,7 +14,7 @@ def test_linux_container_split(tmp_path: Path, monkeypatch):
Tests splitting linux builds by container image and before_all Tests splitting linux builds by container image and before_all
""" """
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.platform = "linux" args.platform = "linux"
(tmp_path / "pyproject.toml").write_text( (tmp_path / "pyproject.toml").write_text(
@@ -42,7 +40,7 @@ def test_linux_container_split(tmp_path: Path, monkeypatch):
) )
monkeypatch.chdir(tmp_path) monkeypatch.chdir(tmp_path)
options = Options("linux", command_line_arguments=args) options = Options("linux", command_line_arguments=args, env={})
python_configurations = cibuildwheel.linux.get_python_configurations( python_configurations = cibuildwheel.linux.get_python_configurations(
options.globals.build_selector, options.globals.architectures options.globals.build_selector, options.globals.architectures
+22 -19
View File
@@ -10,13 +10,16 @@ import pytest
from cibuildwheel.__main__ import get_build_identifiers from cibuildwheel.__main__ import get_build_identifiers
from cibuildwheel.bashlex_eval import local_environment_executor from cibuildwheel.bashlex_eval import local_environment_executor
from cibuildwheel.environment import parse_environment from cibuildwheel.environment import parse_environment
from cibuildwheel.options import Options, _get_pinned_container_images from cibuildwheel.options import (
CommandLineArguments,
from .utils import get_default_command_line_arguments Options,
_get_pinned_container_images,
)
PYPROJECT_1 = """ PYPROJECT_1 = """
[tool.cibuildwheel] [tool.cibuildwheel]
build = ["cp38*", "cp37*"] build = ["cp38*", "cp37*"]
skip = ["*musllinux*"]
environment = {FOO="BAR"} environment = {FOO="BAR"}
test-command = "pyproject" test-command = "pyproject"
@@ -39,12 +42,12 @@ def test_options_1(tmp_path, monkeypatch):
with tmp_path.joinpath("pyproject.toml").open("w") as f: with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_1) f.write(PYPROJECT_1)
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
options = Options(platform="linux", command_line_arguments=args) options = Options(platform="linux", command_line_arguments=args, env={})
identifiers = get_build_identifiers( identifiers = get_build_identifiers(
platform="linux", platform="linux",
@@ -53,9 +56,8 @@ def test_options_1(tmp_path, monkeypatch):
) )
override_display = """\ override_display = """\
test_command: 'pyproject' *: pyproject
cp37-manylinux_x86_64: 'pyproject-override'""" cp37-manylinux_x86_64, cp37-manylinux_i686: pyproject-override"""
print(options.summary(identifiers)) print(options.summary(identifiers))
assert override_display in options.summary(identifiers) assert override_display in options.summary(identifiers)
@@ -82,13 +84,12 @@ def test_passthrough(tmp_path, monkeypatch):
with tmp_path.joinpath("pyproject.toml").open("w") as f: with tmp_path.joinpath("pyproject.toml").open("w") as f:
f.write(PYPROJECT_1) f.write(PYPROJECT_1)
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
monkeypatch.setenv("EXAMPLE_ENV", "ONE")
options = Options(platform="linux", command_line_arguments=args) options = Options(platform="linux", command_line_arguments=args, env={"EXAMPLE_ENV": "ONE"})
default_build_options = options.build_options(identifier=None) default_build_options = options.build_options(identifier=None)
@@ -110,14 +111,16 @@ def test_passthrough(tmp_path, monkeypatch):
], ],
) )
def test_passthrough_evil(tmp_path, monkeypatch, env_var_value): def test_passthrough_evil(tmp_path, monkeypatch, env_var_value):
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") monkeypatch.setattr(platform_module, "machine", lambda: "x86_64")
monkeypatch.setenv("CIBW_ENVIRONMENT_PASS_LINUX", "ENV_VAR") options = Options(
options = Options(platform="linux", command_line_arguments=args) platform="linux",
command_line_arguments=args,
env={"CIBW_ENVIRONMENT_PASS_LINUX": "ENV_VAR", "ENV_VAR": env_var_value},
)
monkeypatch.setenv("ENV_VAR", env_var_value)
parsed_environment = options.build_options(identifier=None).environment parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value} assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value}
@@ -138,7 +141,7 @@ xfail_env_parse = pytest.mark.xfail(
], ],
) )
def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value): def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value):
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text( tmp_path.joinpath("pyproject.toml").write_text(
@@ -150,7 +153,7 @@ def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value):
) )
) )
options = Options(platform="linux", command_line_arguments=args) options = Options(platform="linux", command_line_arguments=args, env={})
parsed_environment = options.build_options(identifier=None).environment parsed_environment = options.build_options(identifier=None).environment
assert parsed_environment.as_dictionary(prev_environment={}) == {"EXAMPLE": env_var_value} assert parsed_environment.as_dictionary(prev_environment={}) == {"EXAMPLE": env_var_value}
@@ -174,7 +177,7 @@ def test_toml_environment_evil(tmp_path, monkeypatch, env_var_value):
], ],
) )
def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value): def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value):
args = get_default_command_line_arguments() args = CommandLineArguments.defaults()
args.package_dir = tmp_path args.package_dir = tmp_path
tmp_path.joinpath("pyproject.toml").write_text( tmp_path.joinpath("pyproject.toml").write_text(
@@ -186,7 +189,7 @@ def test_toml_environment_quoting(tmp_path: Path, toml_assignment, result_value)
) )
) )
options = Options(platform="linux", command_line_arguments=args) options = Options(platform="linux", command_line_arguments=args, env={})
parsed_environment = options.build_options(identifier=None).environment parsed_environment = options.build_options(identifier=None).environment
environment_values = parsed_environment.as_dictionary( environment_values = parsed_environment.as_dictionary(
prev_environment={**os.environ, "PARAM": "spam"}, prev_environment={**os.environ, "PARAM": "spam"},
+42 -28
View File
@@ -35,7 +35,7 @@ def test_simple_settings(tmp_path, platform, fname):
config_file_path: Path = tmp_path / fname config_file_path: Path = tmp_path / fname
config_file_path.write_text(PYPROJECT_1) config_file_path.write_text(PYPROJECT_1)
options_reader = OptionsReader(config_file_path, platform=platform) options_reader = OptionsReader(config_file_path, platform=platform, env={})
assert options_reader.get("build", env_plat=False, sep=" ") == "cp39*" assert options_reader.get("build", env_plat=False, sep=" ") == "cp39*"
@@ -72,16 +72,20 @@ def test_simple_settings(tmp_path, platform, fname):
def test_envvar_override(tmp_path, platform, monkeypatch): def test_envvar_override(tmp_path, platform, monkeypatch):
monkeypatch.setenv("CIBW_BUILD", "cp38*")
monkeypatch.setenv("CIBW_MANYLINUX_X86_64_IMAGE", "manylinux_2_24")
monkeypatch.setenv("CIBW_TEST_COMMAND", "mytest")
monkeypatch.setenv("CIBW_TEST_REQUIRES", "docs")
monkeypatch.setenv("CIBW_TEST_REQUIRES_LINUX", "scod")
config_file_path: Path = tmp_path / "pyproject.toml" config_file_path: Path = tmp_path / "pyproject.toml"
config_file_path.write_text(PYPROJECT_1) config_file_path.write_text(PYPROJECT_1)
options_reader = OptionsReader(config_file_path, platform=platform) options_reader = OptionsReader(
config_file_path,
platform=platform,
env={
"CIBW_BUILD": "cp38*",
"CIBW_MANYLINUX_X86_64_IMAGE": "manylinux_2_24",
"CIBW_TEST_COMMAND": "mytest",
"CIBW_TEST_REQUIRES": "docs",
"CIBW_TEST_REQUIRES_LINUX": "scod",
},
)
assert options_reader.get("archs", sep=" ") == "auto" assert options_reader.get("archs", sep=" ") == "auto"
@@ -104,18 +108,18 @@ def test_project_global_override_default_platform(tmp_path, platform):
repair-wheel-command = "repair-project-global" repair-wheel-command = "repair-project-global"
""" """
) )
options_reader = OptionsReader(pyproject_toml, platform=platform) options_reader = OptionsReader(pyproject_toml, platform=platform, env={})
assert options_reader.get("repair-wheel-command") == "repair-project-global" assert options_reader.get("repair-wheel-command") == "repair-project-global"
def test_env_global_override_default_platform(tmp_path, platform, monkeypatch): def test_env_global_override_default_platform(tmp_path, platform, monkeypatch):
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global") options_reader = OptionsReader(
options_reader = OptionsReader(platform=platform) platform=platform, env={"CIBW_REPAIR_WHEEL_COMMAND": "repair-env-global"}
)
assert options_reader.get("repair-wheel-command") == "repair-env-global" assert options_reader.get("repair-wheel-command") == "repair-env-global"
def test_env_global_override_project_platform(tmp_path, platform, monkeypatch): def test_env_global_override_project_platform(tmp_path, platform, monkeypatch):
monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global")
pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml = tmp_path / "pyproject.toml"
pyproject_toml.write_text( pyproject_toml.write_text(
""" """
@@ -127,7 +131,13 @@ repair-wheel-command = "repair-project-windows"
repair-wheel-command = "repair-project-macos" repair-wheel-command = "repair-project-macos"
""" """
) )
options_reader = OptionsReader(pyproject_toml, platform=platform) options_reader = OptionsReader(
pyproject_toml,
platform=platform,
env={
"CIBW_REPAIR_WHEEL_COMMAND": "repair-env-global",
},
)
assert options_reader.get("repair-wheel-command") == "repair-env-global" assert options_reader.get("repair-wheel-command") == "repair-env-global"
@@ -145,7 +155,7 @@ repair-wheel-command = "repair-project-macos"
repair-wheel-command = "repair-project-global" repair-wheel-command = "repair-project-global"
""" """
) )
options_reader = OptionsReader(pyproject_toml, platform=platform) options_reader = OptionsReader(pyproject_toml, platform=platform, env={})
assert options_reader.get("repair-wheel-command") == f"repair-project-{platform}" assert options_reader.get("repair-wheel-command") == f"repair-project-{platform}"
@@ -161,7 +171,7 @@ repairs-wheel-command = "repair-project-linux"
) )
with pytest.raises(ConfigOptionError) as excinfo: with pytest.raises(ConfigOptionError) as excinfo:
OptionsReader(pyproject_toml, platform="linux") OptionsReader(pyproject_toml, platform="linux", env={})
assert "repair-wheel-command" in str(excinfo.value) assert "repair-wheel-command" in str(excinfo.value)
@@ -178,7 +188,7 @@ repair_wheel_command = "repair-project-linux"
) )
with pytest.raises(ConfigOptionError) as excinfo: with pytest.raises(ConfigOptionError) as excinfo:
OptionsReader(pyproject_toml, platform="linux") OptionsReader(pyproject_toml, platform="linux", env={})
assert "repair-wheel-command" in str(excinfo.value) assert "repair-wheel-command" in str(excinfo.value)
@@ -192,7 +202,7 @@ repair-wheel-command = "repair-project-linux"
""" """
) )
with pytest.raises(ConfigOptionError): with pytest.raises(ConfigOptionError):
OptionsReader(pyproject_toml, platform="linux") OptionsReader(pyproject_toml, platform="linux", env={})
def test_unsupported_join(tmp_path): def test_unsupported_join(tmp_path):
@@ -203,7 +213,7 @@ def test_unsupported_join(tmp_path):
build = ["1", "2"] build = ["1", "2"]
""" """
) )
options_reader = OptionsReader(pyproject_toml, platform="linux") options_reader = OptionsReader(pyproject_toml, platform="linux", env={})
assert "1, 2" == options_reader.get("build", sep=", ") assert "1, 2" == options_reader.get("build", sep=", ")
with pytest.raises(ConfigOptionError): with pytest.raises(ConfigOptionError):
@@ -219,9 +229,9 @@ manylinux-x86_64-image = "manylinux1"
""" """
) )
disallow = {"windows": {"manylinux-x86_64-image"}} disallow = {"windows": {"manylinux-x86_64-image"}}
OptionsReader(pyproject_toml, platform="linux", disallow=disallow) OptionsReader(pyproject_toml, platform="linux", disallow=disallow, env={})
with pytest.raises(ConfigOptionError): with pytest.raises(ConfigOptionError):
OptionsReader(pyproject_toml, platform="windows", disallow=disallow) OptionsReader(pyproject_toml, platform="windows", disallow=disallow, env={})
def test_environment_override_empty(tmp_path, monkeypatch): def test_environment_override_empty(tmp_path, monkeypatch):
@@ -234,10 +244,14 @@ manylinux-x86_64-image = ""
""" """
) )
monkeypatch.setenv("CIBW_MANYLINUX_I686_IMAGE", "") options_reader = OptionsReader(
monkeypatch.setenv("CIBW_MANYLINUX_AARCH64_IMAGE", "manylinux1") pyproject_toml,
platform="linux",
options_reader = OptionsReader(pyproject_toml, platform="linux") env={
"CIBW_MANYLINUX_I686_IMAGE": "",
"CIBW_MANYLINUX_AARCH64_IMAGE": "manylinux1",
},
)
assert options_reader.get("manylinux-x86_64-image") == "" assert options_reader.get("manylinux-x86_64-image") == ""
assert options_reader.get("manylinux-i686-image") == "" assert options_reader.get("manylinux-i686-image") == ""
@@ -306,7 +320,7 @@ def test_pyproject_2(tmp_path, platform):
pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml: Path = tmp_path / "pyproject.toml"
pyproject_toml.write_text(PYPROJECT_2) pyproject_toml.write_text(PYPROJECT_2)
options_reader = OptionsReader(config_file_path=pyproject_toml, platform=platform) options_reader = OptionsReader(config_file_path=pyproject_toml, platform=platform, env={})
assert options_reader.get("test-command") == "pyproject" assert options_reader.get("test-command") == "pyproject"
with options_reader.identifier("random"): with options_reader.identifier("random"):
@@ -330,7 +344,7 @@ test-command = "pyproject-override"
) )
with pytest.raises(ConfigOptionError): with pytest.raises(ConfigOptionError):
OptionsReader(config_file_path=pyproject_toml, platform=platform) OptionsReader(config_file_path=pyproject_toml, platform=platform, env={})
def test_config_settings(tmp_path): def test_config_settings(tmp_path):
@@ -343,7 +357,7 @@ other = ["two", "three"]
""" """
) )
options_reader = OptionsReader(config_file_path=pyproject_toml, platform="linux") options_reader = OptionsReader(config_file_path=pyproject_toml, platform="linux", env={})
assert ( assert (
options_reader.get("config-settings", table={"item": '{k}="{v}"', "sep": " "}) options_reader.get("config-settings", table={"item": '{k}="{v}"', "sep": " "})
== 'example="one" other="two" other="three"' == 'example="one" other="two" other="three"'
@@ -359,7 +373,7 @@ def test_pip_config_settings(tmp_path):
""" """
) )
options_reader = OptionsReader(config_file_path=pyproject_toml, platform="linux") options_reader = OptionsReader(config_file_path=pyproject_toml, platform="linux", env={})
assert ( assert (
options_reader.get( options_reader.get(
"config-settings", table={"item": "--config-settings='{k}=\"{v}\"'", "sep": " "} "config-settings", table={"item": "--config-settings='{k}=\"{v}\"'", "sep": " "}
-21
View File
@@ -1,21 +0,0 @@
from __future__ import annotations
from pathlib import Path
from cibuildwheel.options import CommandLineArguments
def get_default_command_line_arguments() -> CommandLineArguments:
defaults = CommandLineArguments(
platform="auto",
allow_empty=False,
archs=None,
only=None,
config_file="",
output_dir=Path("wheelhouse"),
package_dir=Path("."),
prerelease_pythons=False,
print_build_identifiers=False,
)
return defaults
+35 -1
View File
@@ -1,10 +1,16 @@
from __future__ import annotations from __future__ import annotations
import textwrap
from pathlib import PurePath from pathlib import PurePath
import pytest import pytest
from cibuildwheel.util import find_compatible_wheel, format_safe, prepare_command from cibuildwheel.util import (
find_compatible_wheel,
fix_ansi_codes_for_github_actions,
format_safe,
prepare_command,
)
def test_format_safe(): def test_format_safe():
@@ -90,3 +96,31 @@ def test_find_compatible_wheel_found(wheel: str, identifier: str):
) )
def test_find_compatible_wheel_not_found(wheel: str, identifier: str): def test_find_compatible_wheel_not_found(wheel: str, identifier: str):
assert find_compatible_wheel([PurePath(wheel)], identifier) is None assert find_compatible_wheel([PurePath(wheel)], identifier) is None
def test_fix_ansi_codes_for_github_actions():
input = textwrap.dedent(
"""
This line is normal
\033[1mThis line is bold
This line is also bold
\033[31m this line is red and bold
This line is red and bold, too\033[0m
This line is normal again
"""
)
expected = textwrap.dedent(
"""
This line is normal
\033[1mThis line is bold
\033[1mThis line is also bold
\033[1m\033[31m this line is red and bold
\033[1m\033[31mThis line is red and bold, too\033[0m
This line is normal again
"""
)
output = fix_ansi_codes_for_github_actions(input)
assert output == expected