diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 325eaac5b..21a64625c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: name: artifact path: dist - - uses: pypa/gh-action-pypi-publish@v1.5.1 + - uses: pypa/gh-action-pypi-publish@v1.6.1 with: user: __token__ password: ${{ secrets.pypi_password }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0646e2157..1e387efe1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,7 +56,6 @@ jobs: echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list curl -sSfL "https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key" | sudo apt-key add - sudo apt-get update - sudo apt-get -y upgrade sudo apt-get -y install podman - name: Install dependencies diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml index 2b15fbb7d..50d940c6f 100644 --- a/.github/workflows/update-dependencies.yml +++ b/.github/workflows/update-dependencies.yml @@ -22,9 +22,9 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: wntrblm/nox@2022.8.7 + - uses: wntrblm/nox@2022.11.21 with: - python-versions: "3.6, 3.7, 3.8, 3.9, 3.10, 3.11-dev" + python-versions: "3.6, 3.7, 3.8, 3.9, 3.10, 3.11" - name: "Run update: dependencies" run: nox --force-color -s update_constraints diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 713f38dbb..f503e6d5b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,7 +26,6 @@ windows: - choco install python -y --version 3.8.6 script: - py -m pip install -e ".[dev]" pytest-custom-exit-code - - py -m cibuildwheel --output-dir wheelhouse --platform windows - py bin\run_tests.py tags: - windows diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fbe6410b1..d9464be48 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: check-case-conflict - id: check-merge-conflict @@ -14,7 +14,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.3.0 hooks: - id: pyupgrade args: ["--py37-plus"] @@ -22,7 +22,7 @@ repos: # Autoremoves unused imports - repo: https://github.com/hadialqattan/pycln - rev: v2.1.1 + rev: v2.1.2 hooks: - id: pycln args: [--all] @@ -41,13 +41,13 @@ repos: - id: black - repo: https://github.com/asottile/setup-cfg-fmt - rev: v2.1.0 + rev: v2.2.0 hooks: - id: setup-cfg-fmt args: [--include-version-classifiers, --max-py-version=3.11] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 + rev: v0.991 hooks: - id: mypy name: mypy 3.7 on cibuildwheel/ @@ -70,6 +70,7 @@ repos: - dataclasses - id: mypy name: mypy 3.10 + exclude: ^cibuildwheel/resources/.*py$ args: ["--python-version=3.10"] additional_dependencies: *mypy-dependencies @@ -81,7 +82,7 @@ repos: - flake8-bugbear - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 exclude: ^cibuildwheel/resources/ diff --git a/README.md b/README.md index 1d31bee5c..446ece5cc 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ jobs: - uses: actions/setup-python@v3 - name: Install cibuildwheel - run: python -m pip install cibuildwheel==2.11.2 + run: python -m pip install cibuildwheel==2.11.3 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse @@ -209,6 +209,14 @@ Changelog +### v2.11.3 + +_5 Dec 2022_ + +- ✨ Improves the 'build options' log output that's printed at the start of each run (#1352) +- ✨ Added a friendly error message to a common misconfiguration of the `CIBW_TEST_COMMAND` option - not specifying path using the `{project}` placeholder (#1336) +- πŸ›  The GitHub Action now uses Powershell on Windows to avoid occasional incompabilities with bash (#1346) + ### v2.11.2 _26 October 2022_ @@ -248,12 +256,6 @@ _25 September 2022_ - πŸ› Fix computation of `auto`/`auto64`/`auto32` archs when targeting a different platform to the one that you're running cibuildwheel on. (#1266) - πŸ“š Fix an mistake in the 'how it works' diagram. (#1274) -### v2.10.1 - -_18 September 2022_ - -- πŸ› Fix a bug that stopped environment variables specified in TOML from being expanded. (#1273) - --- diff --git a/action.yml b/action.yml index 31c6b42bc..9ce2e2577 100644 --- a/action.yml +++ b/action.yml @@ -37,9 +37,23 @@ runs: --python '${{ steps.python.outputs.python-path }}' --spec '${{ github.action_path }}' cibuildwheel - ${{ inputs.package-dir }} - --output-dir ${{ inputs.output-dir }} + "${{ inputs.package-dir }}" + --output-dir "${{ inputs.output-dir }}" --config-file "${{ inputs.config-file }}" --only "${{ inputs.only }}" 2>&1 shell: bash + if: runner.os != 'Windows' + + # Windows needs powershell to interact nicely with Meson + - run: > + pipx run + --python "${{ steps.python.outputs.python-path }}" + --spec "${{ github.action_path }}" + cibuildwheel + "${{ inputs.package-dir }}" + --output-dir '"${{ inputs.output-dir }}"' + --config-file '"${{ inputs.config-file }}"' + --only '"${{ inputs.only }}"' + shell: pwsh + if: runner.os == 'Windows' diff --git a/bin/projects.py b/bin/projects.py index dfd05d96c..6b577189e 100644 --- a/bin/projects.py +++ b/bin/projects.py @@ -243,7 +243,7 @@ def projects( README_FILE, projects=projects[:10], input_filename=input.name, include_info=False ) insert_projects_table( - DOCS_PAGE, projects=projects, input_filename=input.name, include_info=True + DOCS_PAGE, projects=projects, input_filename=input.name, include_info=False ) diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py index 6f62a93ed..ce865e898 100644 --- a/cibuildwheel/__init__.py +++ b/cibuildwheel/__init__.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "2.11.2" +__version__ = "2.11.3" diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 48ba61af3..1ff447aa5 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -22,9 +22,11 @@ from cibuildwheel.util import ( CIBW_CACHE_PATH, BuildSelector, + CIProvider, Unbuffered, chdir, detect_ci_provider, + fix_ansi_codes_for_github_actions, ) @@ -229,7 +231,7 @@ def build_in_directory(args: CommandLineArguments) -> None: ) 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_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("Build options:") - print(f" platform: {platform!r}") - print(textwrap.indent(options.summary(identifiers), " ")) + print(f" platform: {platform}") + 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}") warnings = detect_warnings(options=options, identifiers=identifiers) diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py index 90d30c582..7be0bb3a4 100644 --- a/cibuildwheel/architecture.py +++ b/cibuildwheel/architecture.py @@ -43,6 +43,9 @@ class Architecture(Enum): def __lt__(self, other: Architecture) -> bool: return self.value < other.value + def __str__(self) -> str: + return self.name + @staticmethod def parse_config(config: str, platform: PlatformName) -> set[Architecture]: result = set() diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py index 23ea98770..fd17175d1 100644 --- a/cibuildwheel/environment.py +++ b/cibuildwheel/environment.py @@ -70,7 +70,7 @@ def __init__(self, name: str, value: str): self.value = value def __repr__(self) -> str: - return f"{self.name}: {self.value}" + return f"{self.name}={self.value}" def evaluated_value(self, **_: Any) -> str: return self.value @@ -131,6 +131,9 @@ def add(self, name: str, value: str) -> None: def __repr__(self) -> str: 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: env_items = split_env_items(env_string) diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index c30c2ef4c..1b63e8284 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -21,6 +21,7 @@ prepare_command, read_python_configs, split_config_settings, + test_fail_cwd_file, unwrap, ) @@ -306,9 +307,11 @@ def build_in_container( # set up a virtual environment to install and test from, to make sure # there are no dependencies that were pulled in at build time. container.call(["pip", "install", "virtualenv", *dependency_constraint_flags], env=env) - venv_dir = ( - PurePath(container.call(["mktemp", "-d"], capture_output=True).strip()) / "venv" + + testing_temp_dir = PurePosixPath( + container.call(["mktemp", "-d"], capture_output=True).strip() ) + venv_dir = testing_temp_dir / "venv" container.call(["python", "-m", "virtualenv", "--no-download", venv_dir], env=env) @@ -345,10 +348,14 @@ def build_in_container( project=container_project_path, package=container_package_dir, ) - container.call(["sh", "-c", test_command_prepared], cwd="/root", env=virtualenv_env) + test_cwd = testing_temp_dir / "test_cwd" + container.call(["mkdir", "-p", test_cwd]) + container.copy_into(test_fail_cwd_file, test_cwd / "test_fail.py") + + container.call(["sh", "-c", test_command_prepared], cwd=test_cwd, env=virtualenv_env) # clean up test environment - container.call(["rm", "-rf", venv_dir]) + container.call(["rm", "-rf", testing_temp_dir]) # move repaired wheels to output if compatible_wheel is None: diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py index 70f22997c..a95ed2e50 100644 --- a/cibuildwheel/logger.py +++ b/cibuildwheel/logger.py @@ -228,6 +228,7 @@ def __init__(self, *, enabled: bool) -> None: self.bright_red = "\033[91m" if enabled else "" self.bright_green = "\033[92m" 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 "" diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index b7a939e25..48239b9ec 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -36,6 +36,7 @@ read_python_configs, shell, split_config_settings, + test_fail_cwd_file, unwrap, virtualenv, ) @@ -563,7 +564,7 @@ def build(options: Options, tmp_path: Path) -> None: "pip", "install", *build_options.test_requires, env=virtualenv_env ) - # run the tests from $HOME, with an absolute path in the command + # run the tests from a temp dir, with an absolute path in the command # (this ensures that Python runs the tests against the installed wheel # and not the repo code) test_command_prepared = prepare_command( @@ -571,9 +572,12 @@ def build(options: Options, tmp_path: Path) -> None: project=Path(".").resolve(), package=build_options.package_dir.resolve(), ) - shell_with_arch( - test_command_prepared, cwd=os.environ["HOME"], env=virtualenv_env - ) + + test_cwd = identifier_tmp_dir / "test_cwd" + test_cwd.mkdir(exist_ok=True) + (test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text()) + + shell_with_arch(test_command_prepared, cwd=test_cwd, env=virtualenv_env) # we're all done here; move it to output (overwrite existing) if compatible_wheel is None: diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index fa3138008..6b7b335f5 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -1,13 +1,14 @@ from __future__ import annotations +import collections import configparser import contextlib import dataclasses import difflib import functools -import os import shlex import sys +import textwrap import traceback from pathlib import Path from typing import Any, Callable, Dict, Generator, Iterator, List, Mapping, Union, cast @@ -21,6 +22,7 @@ from .architecture import Architecture from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment +from .logger import log from .oci_container import ContainerEngine from .projectfiles import get_requires_python_str from .typing import PLATFORMS, Literal, NotRequired, PlatformName, TypedDict @@ -52,6 +54,20 @@ class CommandLineArguments: allow_empty: 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) class GlobalOptions: @@ -176,9 +192,11 @@ def __init__( config_file_path: Path | None = None, *, platform: PlatformName, + env: Mapping[str, str], disallow: dict[str, set[str]] | None = None, ) -> None: self.platform = platform + self.env = env self.disallow = disallow or {} # Open defaults.toml, loading both global and platform sections @@ -319,8 +337,8 @@ def get( # get the option from the environment, then the config file, then finally the default. # platform-specific options are preferred, if they're allowed. result = _dig_first( - (os.environ if env_plat else {}, plat_envvar), # type: ignore[arg-type] - (os.environ, envvar), + (self.env if env_plat else {}, plat_envvar), + (self.env, envvar), *[(o.options, name) for o in active_config_overrides], (self.config_platform_options, name), (self.config_options, name), @@ -362,13 +380,21 @@ def _inner_fmt(k: str, v: Any, table: TableFmt) -> Iterator[str]: 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.command_line_arguments = command_line_arguments + self.env = env self.reader = OptionsReader( - self.config_file_path, + self.config_file_path if read_config_file else None, platform=platform, + env=env, disallow=DISALLOWED_OPTIONS, ) @@ -402,13 +428,13 @@ def globals(self) -> GlobalOptions: test_skip = self.reader.get("test-skip", env_plat=False, sep=" ") 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. # Passing this in as an environment variable will override pyproject.toml, setup.cfg, or setup.py 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) @@ -497,7 +523,7 @@ def build_options(self, identifier: str | None) -> BuildOptions: if self.platform == "linux": for env_var_name in environment_pass: 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": dependency_constraints: None | ( @@ -594,37 +620,119 @@ def check_for_deprecated_options(self) -> None: deprecated_selectors("CIBW_SKIP", build_selector.skip_config) deprecated_selectors("CIBW_TEST_SKIP", test_selector.skip_config) + @cached_property + def defaults(self) -> Options: + return Options( + platform=self.platform, + command_line_arguments=CommandLineArguments.defaults(), + env={}, + read_config_file=False, + ) + def summary(self, identifiers: list[str]) -> str: - lines = [ - f"{option_name}: {option_value!r}" - for option_name, option_value in sorted(dataclasses.asdict(self.globals).items()) - ] + lines = [] + global_option_names = sorted(f.name for f in dataclasses.fields(self.globals)) - build_option_defaults = self.build_options(identifier=None) + 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 = { 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": 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 - for identifier in identifiers: - option_value = getattr(build_options_for_identifier[identifier], option_name) - if option_value != default_value: - lines.append(f" {identifier}: {option_value!r}") + lines.append( + self.option_summary(option_name, option_value, default_value, overrides=overrides) + ) 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( platform: PlatformName, command_line_arguments: CommandLineArguments, + env: Mapping[str, str], ) -> 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() return options diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt index 5d8deff16..b67253c05 100644 --- a/cibuildwheel/resources/constraints-python310.txt +++ b/cibuildwheel/resources/constraints-python310.txt @@ -4,25 +4,27 @@ # # nox -s update_constraints-3.10 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via delocate -virtualenv==20.16.5 +virtualenv==20.17.0 + # via -r cibuildwheel/resources/constraints.in +wheel==0.38.4 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt index 5b9294d09..c669ec4e9 100644 --- a/cibuildwheel/resources/constraints-python311.txt +++ b/cibuildwheel/resources/constraints-python311.txt @@ -4,25 +4,27 @@ # # nox -s update_constraints-3.11 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via delocate -virtualenv==20.16.5 +virtualenv==20.17.0 + # via -r cibuildwheel/resources/constraints.in +wheel==0.38.4 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/constraints-python36.txt b/cibuildwheel/resources/constraints-python36.txt index 41b899a8d..e58ba3cf9 100644 --- a/cibuildwheel/resources/constraints-python36.txt +++ b/cibuildwheel/resources/constraints-python36.txt @@ -20,7 +20,7 @@ typing-extensions==4.1.1 # via # delocate # importlib-metadata -virtualenv==20.16.5 +virtualenv==20.17.0 # via -r cibuildwheel/resources/constraints.in wheel==0.37.1 # via diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt index 8c76e85f6..4b1ca57fe 100644 --- a/cibuildwheel/resources/constraints-python37.txt +++ b/cibuildwheel/resources/constraints-python37.txt @@ -4,31 +4,33 @@ # # nox -s update_constraints-3.7 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -importlib-metadata==5.0.0 +importlib-metadata==5.1.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via # delocate # importlib-metadata -virtualenv==20.16.5 +virtualenv==20.17.0 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate -zipp==3.10.0 +wheel==0.38.4 + # via -r cibuildwheel/resources/constraints.in +zipp==3.11.0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt index d13350fde..1f5c0adb1 100644 --- a/cibuildwheel/resources/constraints-python38.txt +++ b/cibuildwheel/resources/constraints-python38.txt @@ -4,25 +4,27 @@ # # nox -s update_constraints-3.8 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via delocate -virtualenv==20.16.5 +virtualenv==20.17.0 + # via -r cibuildwheel/resources/constraints.in +wheel==0.38.4 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt index 8f51d507e..d0a1fc3ed 100644 --- a/cibuildwheel/resources/constraints-python39.txt +++ b/cibuildwheel/resources/constraints-python39.txt @@ -4,25 +4,27 @@ # # nox -s update_constraints-3.9 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via delocate -virtualenv==20.16.5 +virtualenv==20.17.0 + # via -r cibuildwheel/resources/constraints.in +wheel==0.38.4 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt index 5b9294d09..c669ec4e9 100644 --- a/cibuildwheel/resources/constraints.txt +++ b/cibuildwheel/resources/constraints.txt @@ -4,25 +4,27 @@ # # nox -s update_constraints-3.11 # -delocate==0.10.2 +delocate==0.10.3 # via -r cibuildwheel/resources/constraints.in distlib==0.3.6 # via virtualenv filelock==3.8.0 # via virtualenv -platformdirs==2.5.2 +packaging==21.3 + # via delocate +platformdirs==2.5.4 # via virtualenv +pyparsing==3.0.9 + # via packaging typing-extensions==4.4.0 # via delocate -virtualenv==20.16.5 +virtualenv==20.17.0 + # via -r cibuildwheel/resources/constraints.in +wheel==0.38.4 # via -r cibuildwheel/resources/constraints.in -wheel==0.37.1 - # via - # -r cibuildwheel/resources/constraints.in - # delocate # The following packages are considered to be unsafe in a requirements file: -pip==22.3 +pip==22.3.1 # via -r cibuildwheel/resources/constraints.in -setuptools==65.5.0 +setuptools==65.6.3 # via -r cibuildwheel/resources/constraints.in diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg index 3cd926b3d..a25f79ead 100644 --- a/cibuildwheel/resources/pinned_docker_images.cfg +++ b/cibuildwheel/resources/pinned_docker_images.cfg @@ -1,49 +1,49 @@ [x86_64] -manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-10-15-7701e25 +manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-11-27-4b40699 manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177 -manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-10-25-fbea779 -musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-11-27-b2d7fda +musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-11-27-b2d7fda [i686] -manylinux1 = quay.io/pypa/manylinux1_i686:2022-10-15-7701e25 +manylinux1 = quay.io/pypa/manylinux1_i686:2022-11-27-4b40699 manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177 -manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-10-25-fbea779 -musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-11-27-b2d7fda +musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-11-27-b2d7fda [pypy_x86_64] manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177 -manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-11-27-b2d7fda [pypy_i686] manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177 -manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-11-27-b2d7fda [aarch64] -manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-10-25-fbea779 -musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-11-27-b2d7fda +musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-11-27-b2d7fda [ppc64le] -manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2022-10-25-fbea779 -musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2022-11-27-b2d7fda +musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-11-27-b2d7fda [s390x] -manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2022-10-25-fbea779 -musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2022-11-27-b2d7fda +musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-11-27-b2d7fda [pypy_aarch64] -manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-10-25-fbea779 -manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-10-25-fbea779 -manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-10-25-fbea779 +manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-11-27-b2d7fda +manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-11-27-b2d7fda +manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-11-27-b2d7fda diff --git a/cibuildwheel/resources/testing_temp_dir_file.py b/cibuildwheel/resources/testing_temp_dir_file.py new file mode 100644 index 000000000..1788e7cf7 --- /dev/null +++ b/cibuildwheel/resources/testing_temp_dir_file.py @@ -0,0 +1,17 @@ +# this file is copied to the testing cwd, to raise the below error message if +# pytest/unittest is run from there. + +import unittest + + +class TestStringMethods(unittest.TestCase): + def test_fail(self): + self.fail( + "cibuildwheel executes tests from a different working directory to " + "your project. This ensures only your wheel is imported, preventing " + "Python from accessing files that haven't been packaged into the " + "wheel. Please specify a path to your tests when invoking pytest " + "using the {project} placeholder, e.g. `pytest {project}` or " + "`pytest {project}/tests`. cibuildwheel will replace {project} with " + "the path to your project." + ) diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml index 0fbf028c1..d70cab4c2 100644 --- a/cibuildwheel/resources/virtualenv.toml +++ b/cibuildwheel/resources/virtualenv.toml @@ -1,2 +1,2 @@ -version = "20.16.5" -url = "https://github.com/pypa/get-virtualenv/blob/20.16.5/public/virtualenv.pyz?raw=true" +version = "20.17.0" +url = "https://github.com/pypa/get-virtualenv/blob/20.17.0/public/virtualenv.pyz?raw=true" diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 29ccda979..c7932212c 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -67,6 +67,8 @@ install_certifi_script: Final[Path] = resources_dir / "install_certifi.py" +test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py" + BuildFrontend = Literal["pip", "build"] MANYLINUX_ARCHS: Final[tuple[str, ...]] = ( @@ -270,6 +272,14 @@ def __call__(self, build_id: str) -> bool: 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) class TestSelector: @@ -283,6 +293,9 @@ def __call__(self, build_id: str) -> bool: should_skip = selector_matches(self.skip_config, build_id) return not should_skip + def options_summary(self) -> Any: + return {"skip_config": self.skip_config} + # Taken from https://stackoverflow.com/a/107717 class Unbuffered: @@ -356,6 +369,12 @@ def __eq__(self, o: object) -> bool: 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): def __init__(self) -> None: @@ -655,3 +674,31 @@ def chdir(new_path: Path | str) -> Generator[None, None, None]: yield finally: 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 diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index b62ea8461..58f5a4242 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -35,6 +35,7 @@ read_python_configs, shell, split_config_settings, + test_fail_cwd_file, unwrap, virtualenv, ) @@ -542,7 +543,7 @@ def build(options: Options, tmp_path: Path) -> None: if build_options.test_requires: call("pip", "install", *build_options.test_requires, env=virtualenv_env) - # run the tests from c:\, with an absolute path in the command + # run the tests from a temp dir, with an absolute path in the command # (this ensures that Python runs the tests against the installed wheel # and not the repo code) test_command_prepared = prepare_command( @@ -550,7 +551,11 @@ def build(options: Options, tmp_path: Path) -> None: project=Path(".").resolve(), package=options.globals.package_dir.resolve(), ) - shell(test_command_prepared, cwd="c:\\", env=virtualenv_env) + test_cwd = identifier_tmp_dir / "test_cwd" + test_cwd.mkdir() + (test_cwd / "test_fail.py").write_text(test_fail_cwd_file.read_text()) + + shell(test_command_prepared, cwd=test_cwd, env=virtualenv_env) # we're all done here; move it to output (remove if already exists) if compatible_wheel is None: diff --git a/docs/changelog.md b/docs/changelog.md index 31db1b2d2..a945beb40 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,14 @@ title: Changelog # Changelog +### v2.11.3 + +_5 Dec 2022_ + +- ✨ Improves the 'build options' log output that's printed at the start of each run (#1352) +- ✨ Added a friendly error message to a common misconfiguration of the `CIBW_TEST_COMMAND` option - not specifying path using the `{project}` placeholder (#1336) +- πŸ›  The GitHub Action now uses Powershell on Windows to avoid occasional incompabilities with bash (#1346) + ### v2.11.2 _26 October 2022_ diff --git a/docs/data/projects.yml b/docs/data/projects.yml index 648bdb07d..eef5094f5 100644 --- a/docs/data/projects.yml +++ b/docs/data/projects.yml @@ -621,3 +621,10 @@ os: [windows, apple, linux] pypi: pyril notes: A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. + +- name: Picologging + gh: microsoft/picologging + ci: [github] + os: [windows, apple, linux] + pypi: picologging + notes: A high-performance logging library for Python. diff --git a/docs/faq.md b/docs/faq.md index 7d73e3e2f..9f77193f6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -143,7 +143,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead If you use GitHub Actions for builds, you can use cibuildwheel as an action: ```yaml -uses: pypa/cibuildwheel@v2.11.2 +uses: pypa/cibuildwheel@v2.11.3 ``` This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal. @@ -165,7 +165,7 @@ The second option, and the only one that supports other CI systems, is using a ` ```bash # requirements-cibw.txt -cibuildwheel==2.11.2 +cibuildwheel==2.11.3 ``` Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this: @@ -309,7 +309,7 @@ Solutions to this vary, but the simplest is to use pipx: # most runners have pipx preinstalled, but in case you don't python3 -m pip install pipx -pipx run cibuildwheel==2.11.2 --output-dir wheelhouse +pipx run cibuildwheel==2.11.3 --output-dir wheelhouse pipx run twine upload wheelhouse/*.whl ``` diff --git a/docs/setup.md b/docs/setup.md index 480d11e98..eff80745b 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -184,7 +184,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/ - uses: actions/checkout@v3 - name: Build wheels - run: pipx run cibuildwheel==2.11.2 + run: pipx run cibuildwheel==2.11.3 - uses: actions/upload-artifact@v3 with: @@ -219,7 +219,7 @@ To build Linux, Mac, and Windows wheels using GitHub Actions, create a `.github/ - uses: actions/setup-python@v3 - name: Install cibuildwheel - run: python -m pip install cibuildwheel==2.11.2 + run: python -m pip install cibuildwheel==2.11.3 - name: Build wheels run: python -m cibuildwheel --output-dir wheelhouse diff --git a/docs/working-examples.md b/docs/working-examples.md index 5f0cad7f1..c7c88aca3 100644 --- a/docs/working-examples.md +++ b/docs/working-examples.md @@ -21,8 +21,8 @@ title: Working examples | [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python | | [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. | | [psutil][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Cross-platform lib for process and system monitoring in Python | -| [vaex][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Out-of-Core hybrid Apache Arrow/NumPy DataFrame for Python, ML, visualization and exploration of big tabular data at a billion rows per second πŸš€ | | [duckdb][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | DuckDB is an in-process SQL OLAP Database Management System | +| [vaex][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Out-of-Core hybrid Apache Arrow/NumPy DataFrame for Python, ML, visualization and exploration of big tabular data at a billion rows per second πŸš€ | | [Google Benchmark][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A microbenchmark support library | | [Apache Beam][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Apache Beam is a unified programming model for Batch and Streaming data processing. | | [asyncpg][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A fast PostgreSQL Database Client Library for Python/asyncio. | @@ -32,8 +32,8 @@ title: Working examples | [twisted-iocpsupport][] | ![github icon][] | ![windows icon][] | A submodule of Twisted that hooks into native C APIs using Cython. | | [PyOxidizer][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool | | [websockets][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Library for building WebSocket servers and clients. Mostly written in Python, with a small C 'speedups' extension module. | -| [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. | | [Triton][] | ![github icon][] | ![linux icon][] | Self hosted runners | +| [cvxpy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A Python-embedded modeling language for convex optimization problems. | | [UltraJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Ultra fast JSON decoder and encoder written in C with Python bindings | | [River][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python | | [OpenSpiel][] | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. | @@ -55,9 +55,9 @@ title: Working examples | [SimpleJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | simplejson is a simple, fast, extensible JSON encoder/decoder for Python | | [Line Profiler][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Line-by-line profiling for Python | | [OpenColorIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. | +| [envd][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | πŸ•οΈ Reproducible development environment for Python, R and Julia | | [PyTables][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python package to manage extremely large amounts of data | | [OpenTimelineIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Open Source API and interchange format for editorial timeline information. | -| [envd][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | πŸ•οΈ Development environment for AI/ML | | [aioquic][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | QUIC and HTTP/3 implementation in Python | | [ruptures][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Extensive Cython + NumPy [pyproject.toml](https://github.com/deepcharles/ruptures/blob/master/pyproject.toml) example. | | [Psycopg 3][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A modern implementation of a PostgreSQL adapter for Python | @@ -68,8 +68,9 @@ title: Working examples | [H3-py][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for H3, a hierarchical hexagonal geospatial indexing system | | [Rtree][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Rtree: spatial index for Python GIS ΒΆ | | [markupsafe][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Safely add untrusted strings to HTML/XML markup. | -| [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson | +| [Picologging][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A high-performance logging library for Python. | | [pybind11 cmake_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a CMake-based build system | +| [python-rapidjson][] | ![travisci icon][] ![gitlab icon][] ![appveyor icon][] | ![windows icon][] ![linux icon][] | Python wrapper around rapidjson | | [python-snappy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Python bindings for the snappy google library | | [KDEpy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Kernel Density Estimation in Python | | [tgcalls][] | ![github icon][] | ![apple icon][] ![windows icon][] | Python `pybind11` binding to Telegram's WebRTC library with third party dependencies like `OpenSSL`, `MozJPEG`, `FFmpeg`, etc. | @@ -84,8 +85,8 @@ title: Working examples | [jq.py][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq | | [iminuit][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 | | [Tokenizer][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support | -| [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python | | [mosec][] | ![github icon][] | ![linux icon][] ![apple icon][] | A high-performance serving framework for ML models, offers dynamic batching and multi-stage pipeline to fully exploit your compute machine | +| [PyGLM][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Fast OpenGL Mathematics (GLM) for Python | | [bx-python][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | A library that includes Cython extensions. | | [TgCrypto][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes a Windows Travis build. | | [boost-histogram][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Supports full range of wheels, including PyPy and alternate archs. | @@ -94,9 +95,9 @@ title: Working examples | [pybase64][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python | | [Arbor][] | ![github icon][] | ![apple icon][] ![linux icon][] | Arbor is a multi-compartment neuron simulation library; compatible with next-generation accelerators; best-practices applied to research software; focused on community-driven development. Includes a [small script](https://github.com/arbor-sim/arbor/blob/master/scripts/patchwheel.py) patching `rpath` in bundled libraries. | | [fathon][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms | +| [pillow-heif][] | ![github icon][] | ![apple icon][] ![linux icon][] | Python CFFI binding to libheif library with third party dependencies like `libde265`, `x265`, `libaom` with test & publishing on PyPi. | | [etebase-py][] | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. | | [polaroid][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. | -| [pillow-heif][] | ![github icon][] | ![apple icon][] ![linux icon][] | Python CFFI binding to libheif library with third party dependencies like `libde265`, `x265`, `libaom` with test & publishing on PyPi. | | [power-grid-model][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python/C++ library for distribution power system analysis | | [Imagecodecs (fork)][] | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. | | [cf-units][] | ![github icon][] | ![apple icon][] ![linux icon][] | Units of measure as required by the Climate and Forecast (CF) Metadata Conventions | @@ -107,9 +108,9 @@ title: Working examples | [ninja][] | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. | | [GSD][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. | | [CorrectionLib][] | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. | +| [ril][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. | | [pyinstrument_cext][] | ![travisci icon][] ![appveyor icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies | | [SiPM][] | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. | -| [ril][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A python binding to Rust Imaging library using maturin and Pyo3, utilizes Github Action cache to improve speed. Builds abi3 wheels. | | [xmlstarlet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. | | [pybind11 cross build example][] | ![github icon][] ![gitlab icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Same as pybind11 cmake_example but used to demo Linux ARM + Windows + macOS builds on GitLab | @@ -126,8 +127,8 @@ title: Working examples [MemRay]: https://github.com/bloomberg/memray [uvloop]: https://github.com/MagicStack/uvloop [psutil]: https://github.com/giampaolo/psutil -[vaex]: https://github.com/vaexio/vaex [duckdb]: https://github.com/duckdb/duckdb +[vaex]: https://github.com/vaexio/vaex [Google Benchmark]: https://github.com/google/benchmark [Apache Beam]: https://github.com/apache/beam [asyncpg]: https://github.com/MagicStack/asyncpg @@ -137,8 +138,8 @@ title: Working examples [twisted-iocpsupport]: https://github.com/twisted/twisted-iocpsupport [PyOxidizer]: https://github.com/indygreg/PyOxidizer [websockets]: https://github.com/aaugustin/websockets -[cvxpy]: https://github.com/cvxpy/cvxpy [Triton]: https://github.com/openai/triton +[cvxpy]: https://github.com/cvxpy/cvxpy [UltraJSON]: https://github.com/ultrajson/ultrajson [River]: https://github.com/online-ml/river [OpenSpiel]: https://github.com/deepmind/open_spiel @@ -160,9 +161,9 @@ title: Working examples [SimpleJSON]: https://github.com/simplejson/simplejson [Line Profiler]: https://github.com/pyutils/line_profiler [OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO +[envd]: https://github.com/tensorchord/envd [PyTables]: https://github.com/PyTables/PyTables [OpenTimelineIO]: https://github.com/PixarAnimationStudios/OpenTimelineIO -[envd]: https://github.com/tensorchord/envd [aioquic]: https://github.com/aiortc/aioquic [ruptures]: https://github.com/deepcharles/ruptures [Psycopg 3]: https://github.com/psycopg/psycopg @@ -173,8 +174,9 @@ title: Working examples [H3-py]: https://github.com/uber/h3-py [Rtree]: https://github.com/Toblerity/rtree [markupsafe]: https://github.com/pallets/markupsafe -[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson +[Picologging]: https://github.com/microsoft/picologging [pybind11 cmake_example]: https://github.com/pybind/cmake_example +[python-rapidjson]: https://github.com/python-rapidjson/python-rapidjson [python-snappy]: https://github.com/andrix/python-snappy [KDEpy]: https://github.com/tommyod/KDEpy [tgcalls]: https://github.com/MarshalX/tgcalls @@ -189,8 +191,8 @@ title: Working examples [jq.py]: https://github.com/mwilliamson/jq.py [iminuit]: https://github.com/scikit-hep/iminuit [Tokenizer]: https://github.com/OpenNMT/Tokenizer -[PyGLM]: https://github.com/Zuzu-Typ/PyGLM [mosec]: https://github.com/mosecorg/mosec +[PyGLM]: https://github.com/Zuzu-Typ/PyGLM [bx-python]: https://github.com/bxlab/bx-python [TgCrypto]: https://github.com/pyrogram/tgcrypto [boost-histogram]: https://github.com/scikit-hep/boost-histogram @@ -199,9 +201,9 @@ title: Working examples [pybase64]: https://github.com/mayeut/pybase64 [Arbor]: https://github.com/arbor-sim/arbor [fathon]: https://github.com/stfbnc/fathon +[pillow-heif]: https://github.com/bigcat88/pillow_heif [etebase-py]: https://github.com/etesync/etebase-py [polaroid]: https://github.com/daggy1234/polaroid -[pillow-heif]: https://github.com/bigcat88/pillow_heif [power-grid-model]: https://github.com/alliander-opensource/power-grid-model [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build [cf-units]: https://github.com/SciTools/cf-units @@ -212,9 +214,9 @@ title: Working examples [ninja]: https://github.com/scikit-build/ninja-python-distributions [GSD]: https://github.com/glotzerlab/gsd [CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib +[ril]: https://github.com/Cryptex-github/ril-py [pyinstrument_cext]: https://github.com/joerick/pyinstrument_cext [SiPM]: https://github.com/EdoPro98/SimSiPM -[ril]: https://github.com/Cryptex-github/ril-py [xmlstarlet]: https://github.com/dimitern/xmlstarlet [pybind11 cross build example]: https://github.com/wbarnha/pybind_cmake_example_crossbuild @@ -229,111 +231,6 @@ title: Working examples [apple icon]: data/readme_icons/apple.svg [linux icon]: data/readme_icons/linux.svg - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - > Add your repo here! Let us know on [GitHub Discussions](https://github.com/pypa/cibuildwheel/discussions/485), or send a PR, adding your information to `docs/data/projects.yml`. diff --git a/examples/appveyor-minimal.yml b/examples/appveyor-minimal.yml index eb2783982..cd4af7c41 100644 --- a/examples/appveyor-minimal.yml +++ b/examples/appveyor-minimal.yml @@ -12,7 +12,7 @@ stack: python 3.7 init: - cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH% -install: python -m pip install cibuildwheel==2.11.2 +install: python -m pip install cibuildwheel==2.11.3 build_script: python -m cibuildwheel --output-dir wheelhouse diff --git a/examples/azure-pipelines-minimal.yml b/examples/azure-pipelines-minimal.yml index 203e0b34e..ea1e09505 100644 --- a/examples/azure-pipelines-minimal.yml +++ b/examples/azure-pipelines-minimal.yml @@ -6,7 +6,7 @@ jobs: - bash: | set -o errexit python3 -m pip install --upgrade pip - pip3 install cibuildwheel==2.11.2 + pip3 install cibuildwheel==2.11.3 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels @@ -20,7 +20,7 @@ jobs: - bash: | set -o errexit python3 -m pip install --upgrade pip - python3 -m pip install cibuildwheel==2.11.2 + python3 -m pip install cibuildwheel==2.11.3 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels @@ -34,7 +34,7 @@ jobs: - bash: | set -o errexit python -m pip install --upgrade pip - pip install cibuildwheel==2.11.2 + pip install cibuildwheel==2.11.3 displayName: Install dependencies - bash: cibuildwheel --output-dir wheelhouse . displayName: Build wheels diff --git a/examples/circleci-minimal.yml b/examples/circleci-minimal.yml index e553d3591..648e7bb06 100644 --- a/examples/circleci-minimal.yml +++ b/examples/circleci-minimal.yml @@ -11,7 +11,7 @@ jobs: - run: name: Build the Linux wheels. command: | - pip3 install --user cibuildwheel==2.11.2 + pip3 install --user cibuildwheel==2.11.3 cibuildwheel --output-dir wheelhouse - store_artifacts: path: wheelhouse/ @@ -28,7 +28,7 @@ jobs: - run: name: Build the Linux aarch64 wheels. command: | - python3 -m pip install --user cibuildwheel==2.11.2 + python3 -m pip install --user cibuildwheel==2.11.3 python3 -m cibuildwheel --output-dir wheelhouse - store_artifacts: path: wheelhouse/ @@ -42,7 +42,7 @@ jobs: - run: name: Build the OS X wheels. command: | - pip3 install cibuildwheel==2.11.2 + pip3 install cibuildwheel==2.11.3 cibuildwheel --output-dir wheelhouse - store_artifacts: path: wheelhouse/ diff --git a/examples/cirrus-ci-intel-mac.yml b/examples/cirrus-ci-intel-mac.yml index 965710b81..91b8640c1 100644 --- a/examples/cirrus-ci-intel-mac.yml +++ b/examples/cirrus-ci-intel-mac.yml @@ -1,6 +1,6 @@ build_and_store_wheels: &BUILD_AND_STORE_WHEELS install_cibuildwheel_script: - - python -m pip install cibuildwheel==2.11.2 + - python -m pip install cibuildwheel==2.11.3 run_cibuildwheel_script: - cibuildwheel wheels_artifacts: diff --git a/examples/cirrus-ci-minimal.yml b/examples/cirrus-ci-minimal.yml index 312bfd9fa..c1a7454cb 100644 --- a/examples/cirrus-ci-minimal.yml +++ b/examples/cirrus-ci-minimal.yml @@ -1,6 +1,6 @@ build_and_store_wheels: &BUILD_AND_STORE_WHEELS install_cibuildwheel_script: - - python -m pip install cibuildwheel==2.11.2 + - python -m pip install cibuildwheel==2.11.3 run_cibuildwheel_script: - cibuildwheel wheels_artifacts: diff --git a/examples/github-apple-silicon.yml b/examples/github-apple-silicon.yml index d0f5a7b7f..be4faedfa 100644 --- a/examples/github-apple-silicon.yml +++ b/examples/github-apple-silicon.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v3 - name: Build wheels - uses: pypa/cibuildwheel@v2.11.2 + uses: pypa/cibuildwheel@v2.11.3 env: CIBW_ARCHS_MACOS: x86_64 arm64 diff --git a/examples/github-deploy.yml b/examples/github-deploy.yml index baeb5e5fd..1d1e0f843 100644 --- a/examples/github-deploy.yml +++ b/examples/github-deploy.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v3 - name: Build wheels - uses: pypa/cibuildwheel@v2.11.2 + uses: pypa/cibuildwheel@v2.11.3 - uses: actions/upload-artifact@v3 with: diff --git a/examples/github-minimal.yml b/examples/github-minimal.yml index 1acf6e42a..34156ea16 100644 --- a/examples/github-minimal.yml +++ b/examples/github-minimal.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v3 - name: Build wheels - uses: pypa/cibuildwheel@v2.11.2 + uses: pypa/cibuildwheel@v2.11.3 # env: # CIBW_SOME_OPTION: value # ... diff --git a/examples/github-with-qemu.yml b/examples/github-with-qemu.yml index d2ce5e90e..74399abda 100644 --- a/examples/github-with-qemu.yml +++ b/examples/github-with-qemu.yml @@ -20,7 +20,7 @@ jobs: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v2.11.2 + uses: pypa/cibuildwheel@v2.11.3 env: # configure cibuildwheel to build native archs ('auto'), and some # emulated ones diff --git a/examples/gitlab-minimal.yml b/examples/gitlab-minimal.yml index e8c4a9a01..26f484deb 100644 --- a/examples/gitlab-minimal.yml +++ b/examples/gitlab-minimal.yml @@ -12,7 +12,7 @@ linux: DOCKER_TLS_CERTDIR: "" script: - curl -sSL https://get.docker.com/ | sh - - python -m pip install cibuildwheel==2.11.2 + - python -m pip install cibuildwheel==2.11.3 - cibuildwheel --output-dir wheelhouse artifacts: paths: @@ -23,7 +23,7 @@ windows: before_script: - choco install python -y --version 3.8.6 - choco install git.install -y - - py -m pip install cibuildwheel==2.11.2 + - py -m pip install cibuildwheel==2.11.3 script: - py -m cibuildwheel --output-dir wheelhouse --platform windows artifacts: diff --git a/examples/gitlab-with-qemu.yml b/examples/gitlab-with-qemu.yml index 0ebaca409..aa73020d3 100644 --- a/examples/gitlab-with-qemu.yml +++ b/examples/gitlab-with-qemu.yml @@ -14,7 +14,7 @@ linux: - curl -sSL https://get.docker.com/ | sh # Warning: This is extremely slow, be careful with how many wheels you build - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes - - python -m pip install cibuildwheel==2.11.2 + - python -m pip install cibuildwheel==2.11.3 # Assuming your CI runner's default architecture is x86_64... - cibuildwheel --output-dir wheelhouse --platform linux --archs aarch64 artifacts: diff --git a/examples/travis-ci-deploy.yml b/examples/travis-ci-deploy.yml index 95e9ebe1f..0e87ee20a 100644 --- a/examples/travis-ci-deploy.yml +++ b/examples/travis-ci-deploy.yml @@ -20,7 +20,7 @@ jobs: - ln -s /c/Python38/python.exe /c/Python38/python3.exe install: - - python3 -m pip install cibuildwheel==2.11.2 + - python3 -m pip install cibuildwheel==2.11.3 script: # build the wheels, put them into './dist' diff --git a/examples/travis-ci-minimal.yml b/examples/travis-ci-minimal.yml index 97135548f..dabe340cd 100644 --- a/examples/travis-ci-minimal.yml +++ b/examples/travis-ci-minimal.yml @@ -26,7 +26,7 @@ jobs: - ln -s /c/Python38/python.exe /c/Python38/python3.exe install: - - python3 -m pip install cibuildwheel==2.11.2 + - python3 -m pip install cibuildwheel==2.11.3 script: # build the wheels, put them into './wheelhouse' diff --git a/examples/travis-ci-test-and-deploy.yml b/examples/travis-ci-test-and-deploy.yml index da9e9dd24..11b7b6320 100644 --- a/examples/travis-ci-test-and-deploy.yml +++ b/examples/travis-ci-test-and-deploy.yml @@ -54,7 +54,7 @@ jobs: - stage: deploy name: Build and deploy Linux wheels services: docker - install: python3 -m pip install cibuildwheel==2.11.2 twine + install: python3 -m pip install cibuildwheel==2.11.3 twine script: python3 -m cibuildwheel --output-dir wheelhouse after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl # Deploy on windows @@ -62,7 +62,7 @@ jobs: name: Build and deploy Windows wheels os: windows language: shell - install: python3 -m pip install cibuildwheel==2.11.2 twine + install: python3 -m pip install cibuildwheel==2.11.3 twine script: python3 -m cibuildwheel --output-dir wheelhouse after_success: python3 -m twine upload --skip-existing wheelhouse/*.whl diff --git a/setup.cfg b/setup.cfg index 97a24556e..03523b434 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = cibuildwheel -version = 2.11.2 +version = 2.11.3 description = Build Python wheels on CI with minimal configuration. long_description = file: README.md long_description_content_type = text/markdown diff --git a/test/test_testing.py b/test/test_testing.py index fd9f1b6a2..f012819ad 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -3,6 +3,7 @@ import os import subprocess import textwrap +from pathlib import Path import pytest @@ -151,3 +152,37 @@ def test_failing_test(tmp_path): ) assert len(os.listdir(output_dir)) == 0 + + +@pytest.mark.parametrize("test_runner", ["pytest", "unittest"]) +def test_bare_pytest_invocation( + tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str +): + """Check that if a user runs pytest in the the test cwd, it raises a helpful error""" + project_dir = tmp_path / "project" + output_dir = tmp_path / "output" + project_with_a_test.generate(project_dir) + + with pytest.raises(subprocess.CalledProcessError): + utils.cibuildwheel_run( + project_dir, + output_dir=output_dir, + add_env={ + "CIBW_TEST_REQUIRES": "pytest" if test_runner == "pytest" else "", + "CIBW_TEST_COMMAND": ( + "python -m pytest" if test_runner == "pytest" else "python -m unittest" + ), + # Skip CPython 3.8 on macOS arm64, see comment above in + # 'test_failing_test' + "CIBW_SKIP": "cp38-macosx_arm64", + }, + ) + + assert len(os.listdir(output_dir)) == 0 + + captured = capfd.readouterr() + + assert ( + "Please specify a path to your tests when invoking pytest using the {project} placeholder" + in captured.out + captured.err + ) diff --git a/unit_test/linux_build_steps_test.py b/unit_test/linux_build_steps_test.py index 8423d423c..065c53c35 100644 --- a/unit_test/linux_build_steps_test.py +++ b/unit_test/linux_build_steps_test.py @@ -6,9 +6,7 @@ import cibuildwheel.linux import cibuildwheel.oci_container -from cibuildwheel.options import Options - -from .utils import get_default_command_line_arguments +from cibuildwheel.options import CommandLineArguments, Options 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 """ - args = get_default_command_line_arguments() + args = CommandLineArguments.defaults() args.platform = "linux" (tmp_path / "pyproject.toml").write_text( @@ -42,7 +40,7 @@ def test_linux_container_split(tmp_path: Path, monkeypatch): ) 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( options.globals.build_selector, options.globals.architectures diff --git a/unit_test/options_test.py b/unit_test/options_test.py index 73d558f4e..4acb8d74b 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -10,13 +10,16 @@ from cibuildwheel.__main__ import get_build_identifiers from cibuildwheel.bashlex_eval import local_environment_executor from cibuildwheel.environment import parse_environment -from cibuildwheel.options import Options, _get_pinned_container_images - -from .utils import get_default_command_line_arguments +from cibuildwheel.options import ( + CommandLineArguments, + Options, + _get_pinned_container_images, +) PYPROJECT_1 = """ [tool.cibuildwheel] build = ["cp38*", "cp37*"] +skip = ["*musllinux*"] environment = {FOO="BAR"} test-command = "pyproject" @@ -39,12 +42,12 @@ def test_options_1(tmp_path, monkeypatch): with tmp_path.joinpath("pyproject.toml").open("w") as f: f.write(PYPROJECT_1) - args = get_default_command_line_arguments() + args = CommandLineArguments.defaults() args.package_dir = tmp_path 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( platform="linux", @@ -53,9 +56,8 @@ def test_options_1(tmp_path, monkeypatch): ) override_display = """\ -test_command: 'pyproject' - cp37-manylinux_x86_64: 'pyproject-override'""" - + *: pyproject + cp37-manylinux_x86_64, cp37-manylinux_i686: pyproject-override""" print(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: f.write(PYPROJECT_1) - args = get_default_command_line_arguments() + args = CommandLineArguments.defaults() args.package_dir = tmp_path 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) @@ -110,14 +111,16 @@ def test_passthrough(tmp_path, monkeypatch): ], ) def test_passthrough_evil(tmp_path, monkeypatch, env_var_value): - args = get_default_command_line_arguments() + args = CommandLineArguments.defaults() args.package_dir = tmp_path monkeypatch.setattr(platform_module, "machine", lambda: "x86_64") - monkeypatch.setenv("CIBW_ENVIRONMENT_PASS_LINUX", "ENV_VAR") - options = Options(platform="linux", command_line_arguments=args) + options = Options( + 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 assert parsed_environment.as_dictionary(prev_environment={}) == {"ENV_VAR": env_var_value} @@ -138,7 +141,7 @@ def test_passthrough_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 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 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): - args = get_default_command_line_arguments() + args = CommandLineArguments.defaults() args.package_dir = tmp_path 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 environment_values = parsed_environment.as_dictionary( prev_environment={**os.environ, "PARAM": "spam"}, diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py index 5c01a5fe4..767608797 100644 --- a/unit_test/options_toml_test.py +++ b/unit_test/options_toml_test.py @@ -35,7 +35,7 @@ def test_simple_settings(tmp_path, platform, fname): config_file_path: Path = tmp_path / fname 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*" @@ -72,16 +72,20 @@ def test_simple_settings(tmp_path, platform, fname): 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.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" @@ -104,18 +108,18 @@ def test_project_global_override_default_platform(tmp_path, platform): 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" def test_env_global_override_default_platform(tmp_path, platform, monkeypatch): - monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global") - options_reader = OptionsReader(platform=platform) + options_reader = OptionsReader( + platform=platform, env={"CIBW_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): - monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND", "repair-env-global") pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -127,7 +131,13 @@ def test_env_global_override_project_platform(tmp_path, platform, monkeypatch): 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" @@ -145,7 +155,7 @@ def test_global_platform_order(tmp_path, platform): 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}" @@ -161,7 +171,7 @@ def test_unexpected_key(tmp_path): ) 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) @@ -178,7 +188,7 @@ def test_underscores_in_key(tmp_path): ) 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) @@ -192,7 +202,7 @@ def test_unexpected_table(tmp_path): """ ) with pytest.raises(ConfigOptionError): - OptionsReader(pyproject_toml, platform="linux") + OptionsReader(pyproject_toml, platform="linux", env={}) def test_unsupported_join(tmp_path): @@ -203,7 +213,7 @@ def test_unsupported_join(tmp_path): 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=", ") with pytest.raises(ConfigOptionError): @@ -219,9 +229,9 @@ def test_disallowed_a(tmp_path): """ ) 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): - OptionsReader(pyproject_toml, platform="windows", disallow=disallow) + OptionsReader(pyproject_toml, platform="windows", disallow=disallow, env={}) def test_environment_override_empty(tmp_path, monkeypatch): @@ -234,10 +244,14 @@ def test_environment_override_empty(tmp_path, monkeypatch): """ ) - monkeypatch.setenv("CIBW_MANYLINUX_I686_IMAGE", "") - monkeypatch.setenv("CIBW_MANYLINUX_AARCH64_IMAGE", "manylinux1") - - options_reader = OptionsReader(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-i686-image") == "" @@ -306,7 +320,7 @@ def test_pyproject_2(tmp_path, platform): pyproject_toml: Path = tmp_path / "pyproject.toml" 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" with options_reader.identifier("random"): @@ -330,7 +344,7 @@ def test_overrides_not_a_list(tmp_path, platform): ) 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): @@ -343,7 +357,7 @@ def test_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 ( options_reader.get("config-settings", table={"item": '{k}="{v}"', "sep": " "}) == '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 ( options_reader.get( "config-settings", table={"item": "--config-settings='{k}=\"{v}\"'", "sep": " "} diff --git a/unit_test/utils.py b/unit_test/utils.py deleted file mode 100644 index bf44382aa..000000000 --- a/unit_test/utils.py +++ /dev/null @@ -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 diff --git a/unit_test/utils_test.py b/unit_test/utils_test.py index adf214358..4628ff6da 100644 --- a/unit_test/utils_test.py +++ b/unit_test/utils_test.py @@ -1,10 +1,16 @@ from __future__ import annotations +import textwrap from pathlib import PurePath 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(): @@ -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): 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