diff --git a/.cirrus.yml b/.cirrus.yml index 6faa9bf56..6c13ee1a7 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -7,7 +7,6 @@ run_tests: &RUN_TESTS run_cibuildwheel_tests_script: - python ./bin/run_tests.py - linux_x86_task: timeout_in: 120m compute_engine_instance: @@ -16,10 +15,15 @@ linux_x86_task: platform: linux cpu: 8 memory: 8G - + env: + VENV_ROOT: ${HOME}/venv-cibuildwheel + PATH: ${VENV_ROOT}/bin:${PATH} install_pre_requirements_script: - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all - - apt install -y python3-venv python-is-python3 + - add-apt-repository -y ppa:deadsnakes/ppa + - apt-get update + - apt-get install -y python3.12-venv + - python3.12 -m venv ${VENV_ROOT} <<: *RUN_TESTS linux_aarch64_task: @@ -30,10 +34,15 @@ linux_aarch64_task: platform: linux cpu: 4 memory: 4G - + env: + VENV_ROOT: ${HOME}/venv-cibuildwheel + PATH: ${VENV_ROOT}/bin:${PATH} install_pre_requirements_script: - docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all - - apt install -y python3-venv python-is-python3 + - add-apt-repository -y ppa:deadsnakes/ppa + - apt-get update + - apt-get install -y python3.12-venv + - python3.12 -m venv ${VENV_ROOT} <<: *RUN_TESTS windows_x86_task: @@ -46,7 +55,7 @@ windows_x86_task: memory: 8G install_pre_requirements_script: - - choco install -y --no-progress python3 --version 3.10.6 + - choco install -y --no-progress python3 --version 3.12.4 - refreshenv - echo PATH=%PATH% >> "%CIRRUS_ENV%" <<: *RUN_TESTS @@ -54,22 +63,24 @@ windows_x86_task: macos_arm64_task: macos_instance: image: ghcr.io/cirruslabs/macos-runner:sonoma - env: - PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH + VENV_ROOT: ${HOME}/venv-cibuildwheel + PATH: ${VENV_ROOT}/bin:${PATH} install_pre_requirements_script: - - brew install python@3.10 + - brew install python@3.12 + - python3.12 -m venv ${VENV_ROOT} <<: *RUN_TESTS macos_arm64_cp38_task: macos_instance: image: ghcr.io/cirruslabs/macos-runner:sonoma - env: - PATH: /opt/homebrew/opt/python@3.10/libexec/bin:$PATH + VENV_ROOT: ${HOME}/venv-cibuildwheel + PATH: ${VENV_ROOT}/bin:${PATH} PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto' install_pre_requirements_script: - - brew install python@3.10 + - brew install python@3.12 + - python3.12 -m venv ${VENV_ROOT} - curl -fsSLO https://www.python.org/ftp/python/3.8.10/python-3.8.10-macos11.pkg - sudo installer -pkg python-3.8.10-macos11.pkg -target / - rm python-3.8.10-macos11.pkg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 40c2a8657..2b1c71a48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -41,7 +41,7 @@ jobs: python_version: ['3.13'] include: - os: ubuntu-latest - python_version: '3.8' + python_version: '3.11' timeout-minutes: 180 steps: - uses: actions/checkout@v4 @@ -81,8 +81,7 @@ jobs: env: CIBW_ARCHS_MACOS: x86_64 universal2 arm64 CIBW_BUILD_FRONTEND: 'build[uv]' - CIBW_FREE_THREADED_SUPPORT: 1 - CIBW_PRERELEASE_PYTHONS: 1 + CIBW_ENABLE: "cpython-prerelease cpython-freethreading pypy" - name: Run a sample build (GitHub Action, only) uses: ./ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index befd44f0b..ef5c7e724 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,9 +24,9 @@ repos: rev: v1.14.0 hooks: - id: mypy - name: mypy 3.8 on cibuildwheel/ + name: mypy 3.11 on cibuildwheel/ exclude: ^cibuildwheel/resources/.*py|bin/generate_schema.py$ - args: ["--python-version=3.8"] + args: ["--python-version=3.11"] additional_dependencies: &mypy-dependencies - bracex - dependency-groups>=1.2 @@ -47,9 +47,9 @@ repos: - uv - validate-pyproject - id: mypy - name: mypy 3.12 + name: mypy 3.13 exclude: ^cibuildwheel/resources/.*py$ - args: ["--python-version=3.12"] + args: ["--python-version=3.13"] additional_dependencies: *mypy-dependencies - repo: https://github.com/shellcheck-py/shellcheck-py diff --git a/.travis.yml b/.travis.yml index b610edf00..808459794 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,13 +8,13 @@ branches: jobs: include: - - name: Linux | x86_64 + i686 | Python 3.9 - python: 3.9 + - name: Linux | x86_64 + i686 | Python 3.11 + python: 3.11 services: docker env: PYTHON=python - - name: Linux | arm64 | Python 3.9 - python: 3.9 + - name: Linux | arm64 | Python 3.11 + python: 3.11 services: docker arch: arm64-graviton2 group: edge @@ -29,8 +29,8 @@ jobs: packages: - docker-ce docker-ce-cli containerd.io - - name: Linux | ppc64le | Python 3.9 - python: 3.9 + - name: Linux | ppc64le | Python 3.11 + python: 3.11 services: docker arch: ppc64le allow_failure: True @@ -40,16 +40,16 @@ jobs: # c.f. https://travis-ci.community/t/running-out-of-disk-space-quota-when-using-docker-on-ppc64le/11634 - PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"' - - name: Windows | x86_64 | Python 3.9 + - name: Windows | x86_64 | Python 3.11 os: windows language: shell before_install: - - choco upgrade python3 -y --version 3.9.13 --limit-output --params "/InstallDir:C:\\Python39" + - choco upgrade python3 -y --version 3.11.9 --limit-output --params "/InstallDir:C:\\Python311" env: - - PYTHON=C:\\Python39\\python + - PYTHON=C:\\Python311\\python - - name: Linux | s390x | Python 3.9 - python: 3.9 + - name: Linux | s390x | Python 3.11 + python: 3.11 services: docker arch: s390x allow_failure: True diff --git a/CI.md b/CI.md index 25b001bf9..15de33c53 100644 --- a/CI.md +++ b/CI.md @@ -1,11 +1,11 @@ This is a summary of the host Python versions and platforms covered by the different CI platforms: -| | 3.8 | 3.9 | 3.10 | 3.11 | 3.12 | -|---------|----------------------------------|-----------|-----------|---------|--------------------------------------------------| -| Linux | Azure Pipelines / GitHub Actions | Travis CI | Cirrus CI | | AppVeyor¹ / CircleCI¹ / GitHub Actions / GitLab¹ | -| macOS | Azure Pipelines | | Cirrus CI | GitLab¹ | AppVeyor¹ /CircleCI¹ / GitHub Actions | -| Windows | Azure Pipelines | Travis CI | Cirrus CI | | AppVeyor¹ / GitHub Actions / GitLab¹ | +| | 3.11 | 3.12 | 3.13 | +|---------|----------------------------------------------|---------------------------------------------|----------------| +| Linux | Azure Pipelines / GitHub Actions / Travis CI | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions | +| macOS | Azure Pipelines / GitLab¹ | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions | +| Windows | Azure Pipelines / Travis CI | AppVeyor¹ / Cirrus CI / GitLab¹ | GitHub Actions | > ¹ Runs a reduced set of tests to reduce CI load -Non-x86 architectures are covered on Travis CI using Python 3.9. +Non-x86 architectures are covered on Travis CI using Python 3.11. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6541244ca..76b1c6186 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,37 +5,37 @@ pr: - .pre-commit-config.yaml jobs: -- job: linux_38 +- job: linux_311 timeoutInMinutes: 120 pool: {vmImage: 'Ubuntu-20.04'} steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' + versionSpec: '3.11' - bash: | docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. python ./bin/run_tests.py -- job: macos_38 +- job: macos_311 pool: {vmImage: 'macOS-13'} steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' + versionSpec: '3.11' - bash: | python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. python ./bin/run_tests.py --num-processes 2 -- job: windows_38 +- job: windows_311 pool: {vmImage: 'windows-2019'} timeoutInMinutes: 180 steps: - task: UsePythonVersion@0 inputs: - versionSpec: '3.8' + versionSpec: '3.11' - bash: | python -m pip install dependency-groups python -m dependency_groups test | xargs python -m pip install -e. diff --git a/bin/bump_version.py b/bin/bump_version.py index e28bdcb97..2c56caff7 100755 --- a/bin/bump_version.py +++ b/bin/bump_version.py @@ -11,17 +11,13 @@ import os import subprocess import sys +import tomllib import urllib.parse from pathlib import Path import click from packaging.version import InvalidVersion, Version -if sys.version_info < (3, 11): - import tomli as tomllib -else: - import tomllib - config = [ # file path, version find/replace format ("pyproject.toml", 'version = "{}"'), diff --git a/bin/generate_schema.py b/bin/generate_schema.py index 0ef4f0125..e4a39e3b0 100755 --- a/bin/generate_schema.py +++ b/bin/generate_schema.py @@ -119,11 +119,6 @@ description: Set environment variables on the host to pass-through to the container during the build. type: string_array - free-threaded-support: - type: boolean - default: false - description: The project supports free-threaded builds of Python (PEP703) - deprecated: Use the `enable` option instead. manylinux-aarch64-image: type: string description: Specify alternative manylinux / musllinux container images @@ -277,7 +272,6 @@ del non_global_options["build"] del non_global_options["skip"] del non_global_options["test-skip"] -del non_global_options["free-threaded-support"] del non_global_options["enable"] overrides["items"]["properties"]["select"]["oneOf"] = string_array diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index c2cb1c0f0..8a08274ae 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -4,6 +4,7 @@ import difflib import logging +import tomllib from dataclasses import dataclass from pathlib import Path from typing import Final @@ -16,8 +17,6 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from cibuildwheel._compat import tomllib - log = logging.getLogger("cibw") # Looking up the dir instead of using utils.resources_dir diff --git a/bin/update_pythons.py b/bin/update_pythons.py index ecf8e3ef2..5921b427b 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -5,9 +5,10 @@ import copy import difflib import logging +import tomllib from collections.abc import Mapping, MutableMapping from pathlib import Path -from typing import Any, Final, Literal, TypedDict, Union +from typing import Any, Final, Literal, TypedDict import click import requests @@ -17,7 +18,6 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from cibuildwheel._compat import tomllib from cibuildwheel.extra import dump_python_configurations log = logging.getLogger("cibw") @@ -50,7 +50,7 @@ class ConfigMacOS(TypedDict): url: str -AnyConfig = Union[ConfigWinCP, ConfigWinPP, ConfigMacOS] +AnyConfig = ConfigWinCP | ConfigWinPP | ConfigMacOS # The following set of "Versions" classes allow the initial call to the APIs to diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index b458c3f89..dffdb2561 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -5,6 +5,7 @@ import difflib import logging import subprocess +import tomllib from dataclasses import dataclass from pathlib import Path from typing import Final @@ -15,8 +16,6 @@ from rich.logging import RichHandler from rich.syntax import Syntax -from cibuildwheel._compat import tomllib - log = logging.getLogger("cibw") # Looking up the dir instead of using utils.resources_dir diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index ffdde57bf..691d88ed1 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -1,6 +1,7 @@ from __future__ import annotations import argparse +import contextlib import dataclasses import os import shutil @@ -12,7 +13,7 @@ from collections.abc import Iterable, Sequence, Set from pathlib import Path from tempfile import mkdtemp -from typing import Protocol +from typing import Protocol, assert_never import cibuildwheel import cibuildwheel.linux @@ -21,7 +22,6 @@ import cibuildwheel.util import cibuildwheel.windows from cibuildwheel import errors -from cibuildwheel._compat.typing import assert_never from cibuildwheel.architecture import Architecture, allowed_architectures_check from cibuildwheel.logger import log from cibuildwheel.options import CommandLineArguments, Options, compute_options @@ -30,8 +30,8 @@ CIBW_CACHE_PATH, BuildSelector, CIProvider, + EnableGroup, Unbuffered, - chdir, detect_ci_provider, fix_ansi_codes_for_github_actions, strtobool, @@ -101,6 +101,17 @@ def main_inner(global_options: GlobalOptions) -> None: """, ) + enable_groups_str = ", ".join(g.value for g in EnableGroup) + parser.add_argument( + "--enable", + action="append", + default=[], + metavar="GROUP", + help=f""" + Enable an additional category of builds. Use multiple times to select multiple groups. Choices: {enable_groups_str}. + """, + ) + parser.add_argument( "--only", default=None, @@ -157,12 +168,6 @@ def main_inner(global_options: GlobalOptions) -> None: help="Do not report an error code if the build does not match any wheels.", ) - parser.add_argument( - "--prerelease-pythons", - action="store_true", - help="Enable pre-release Python versions if available.", - ) - parser.add_argument( "--debug-traceback", action="store_true", @@ -200,7 +205,7 @@ def main_inner(global_options: GlobalOptions) -> None: # This is now the new package dir args.package_dir = project_dir.resolve() - with chdir(project_dir): + with contextlib.chdir(project_dir): build_in_directory(args) finally: # avoid https://github.com/python/cpython/issues/86962 by performing @@ -418,10 +423,10 @@ def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str if any(o and ("{python}" in o or "{pip}" in o) for o in option_values): # Reminder: in an f-string, double braces means literal single brace msg = ( - f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, " - "and will be removed in cibuildwheel 3. Simply use 'python' or 'pip' instead." + f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported " + "and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead." ) - warnings.append(msg) + raise errors.ConfigurationError(msg) return warnings diff --git a/cibuildwheel/_compat/__init__.py b/cibuildwheel/_compat/__init__.py deleted file mode 100644 index 9d48db4f9..000000000 --- a/cibuildwheel/_compat/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import annotations diff --git a/cibuildwheel/_compat/tomllib.py b/cibuildwheel/_compat/tomllib.py deleted file mode 100644 index b061ba911..000000000 --- a/cibuildwheel/_compat/tomllib.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import annotations - -import sys - -if sys.version_info >= (3, 11): - from tomllib import load, loads -else: - from tomli import load, loads - -__all__ = ["load", "loads"] diff --git a/cibuildwheel/_compat/typing.py b/cibuildwheel/_compat/typing.py deleted file mode 100644 index eb11302ab..000000000 --- a/cibuildwheel/_compat/typing.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import annotations - -import sys - -if sys.version_info < (3, 11): - from typing_extensions import NotRequired, Self, assert_never -else: - from typing import NotRequired, Self, assert_never - -__all__ = ( - "NotRequired", - "Self", - "assert_never", -) diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py index 0f622def9..444d37017 100644 --- a/cibuildwheel/architecture.py +++ b/cibuildwheel/architecture.py @@ -6,9 +6,8 @@ import sys from collections.abc import Set from enum import Enum -from typing import Final, Literal +from typing import Final, Literal, assert_never -from ._compat.typing import assert_never from .typing import PlatformName PRETTY_NAMES: Final[dict[PlatformName, str]] = { diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py index c5f805372..749665394 100644 --- a/cibuildwheel/bashlex_eval.py +++ b/cibuildwheel/bashlex_eval.py @@ -1,14 +1,18 @@ from __future__ import annotations import subprocess -from collections.abc import Iterable, Mapping, Sequence +from collections.abc import ( + Callable, + Iterable, + Mapping, + Sequence, +) from dataclasses import dataclass -from typing import Callable, Dict, List # noqa: TID251 import bashlex # a function that takes a command and the environment, and returns the result -EnvironmentExecutor = Callable[[List[str], Dict[str, str]], str] +EnvironmentExecutor = Callable[[list[str], dict[str, str]], str] def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) -> str: diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index 1ffe96f9a..d4bac7bb5 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -4,15 +4,15 @@ import subprocess import sys import textwrap +from collections import OrderedDict from collections.abc import Iterable, Iterator, Sequence, Set from dataclasses import dataclass from pathlib import Path, PurePath, PurePosixPath -from typing import OrderedDict, Tuple +from typing import assert_never from packaging.version import Version from . import errors -from ._compat.typing import assert_never from .architecture import Architecture from .logger import log from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform @@ -104,7 +104,7 @@ def get_build_steps( Groups PythonConfigurations into BuildSteps. Each BuildStep represents a separate container instance. """ - steps = OrderedDict[Tuple[str, str, str, OCIContainerEngineConfig], BuildStep]() + steps = OrderedDict[tuple[str, str, str, OCIContainerEngineConfig], BuildStep]() for config in python_configurations: _, platform_tag = config.identifier.split("-", 1) diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py index 542576076..b88093814 100644 --- a/cibuildwheel/logger.py +++ b/cibuildwheel/logger.py @@ -5,11 +5,11 @@ import re import sys import time -from typing import IO, AnyStr, Final, Tuple +from typing import IO, AnyStr, Final from .util import CIProvider, detect_ci_provider -FoldPattern = Tuple[str, str] +FoldPattern = tuple[str, str] DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "") FOLD_PATTERNS: Final[dict[str, FoldPattern]] = { "azure": ("##[group]{name}", "##[endgroup]"), diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index fcc88dac9..bea0a25d8 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -12,13 +12,12 @@ from collections.abc import Sequence, Set from dataclasses import dataclass from pathlib import Path -from typing import Literal, Tuple +from typing import Literal, assert_never from filelock import FileLock from packaging.version import Version from . import errors -from ._compat.typing import assert_never from .architecture import Architecture from .environment import ParsedEnvironment from .logger import log @@ -50,7 +49,7 @@ ) -@functools.lru_cache(maxsize=None) +@functools.cache def get_macos_version() -> tuple[int, int]: """ Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0) @@ -74,10 +73,10 @@ def get_macos_version() -> tuple[int, int]: capture_stdout=True, ) version = tuple(map(int, version_str.split(".")[:2])) - return typing.cast(Tuple[int, int], version) + return typing.cast(tuple[int, int], version) -@functools.lru_cache(maxsize=None) +@functools.cache def get_test_macosx_deployment_target() -> str: version = get_macos_version() if version >= (11, 0): diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py index 646bbc643..3e4e6ada5 100644 --- a/cibuildwheel/oci_container.py +++ b/cibuildwheel/oci_container.py @@ -16,9 +16,8 @@ from enum import Enum from pathlib import Path, PurePath, PurePosixPath from types import TracebackType -from typing import IO, Dict, Literal +from typing import IO, Literal, Self, assert_never -from ._compat.typing import Self, assert_never from .errors import OCIEngineTooOldError from .logger import log from .typing import PathOrStr, PopenBytes @@ -489,7 +488,7 @@ def get_environment(self) -> dict[str, str]: capture_output=True, ) ) - return typing.cast(Dict[str, str], env) + return typing.cast(dict[str, str], env) def environment_executor(self, command: Sequence[str], environment: dict[str, str]) -> str: # used as an EnvironmentExecutor to evaluate commands and capture output diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index 3fb9ebb73..3ab736e90 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -9,15 +9,14 @@ import functools import shlex import textwrap -from collections.abc import Generator, Iterable, Set +import tomllib +from collections.abc import Generator, Iterable, Mapping, Sequence, Set from pathlib import Path -from typing import Any, Literal, Mapping, Sequence, Union # noqa: TID251 +from typing import Any, Literal, assert_never from packaging.specifiers import SpecifierSet from . import errors -from ._compat import tomllib -from ._compat.typing import assert_never from .architecture import Architecture from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .logger import log @@ -30,10 +29,9 @@ BuildFrontendConfig, BuildSelector, DependencyConstraints, - EnableGroups, + EnableGroup, TestSelector, format_safe, - read_python_configs, resources_dir, selector_matches, strtobool, @@ -51,8 +49,8 @@ class CommandLineArguments: package_dir: Path print_build_identifiers: bool allow_empty: bool - prerelease_pythons: bool debug_traceback: bool + enable: list[str] @staticmethod def defaults() -> CommandLineArguments: @@ -64,9 +62,9 @@ def defaults() -> CommandLineArguments: config_file="", output_dir=Path("wheelhouse"), package_dir=Path("."), - prerelease_pythons=False, print_build_identifiers=False, debug_traceback=False, + enable=[], ) @@ -121,10 +119,10 @@ def architectures(self) -> set[Architecture]: return self.globals.architectures -SettingLeaf = Union[str, int, bool] +SettingLeaf = str | int | bool SettingList = Sequence[SettingLeaf] -SettingTable = Mapping[str, Union[SettingLeaf, SettingList]] -SettingValue = Union[SettingTable, SettingList, SettingLeaf] +SettingTable = Mapping[str, SettingLeaf | SettingList] +SettingValue = SettingTable | SettingList | SettingLeaf @dataclasses.dataclass(frozen=True) @@ -359,7 +357,7 @@ def _stringify_setting( msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list" raise OptionsReaderError(msg) from None - if isinstance(setting, (bool, int)): + if isinstance(setting, bool | int): return str(setting) return setting @@ -617,28 +615,13 @@ def globals(self) -> GlobalOptions: enable_groups = self.reader.get( "enable", env_plat=False, option_format=ListFormat(sep=" "), env_rule=InheritRule.APPEND ) - enable = {EnableGroups(group) for group in enable_groups.split()} - - free_threaded_support = strtobool( - self.reader.get("free-threaded-support", env_plat=False, ignore_empty=True) - ) - - prerelease_pythons = args.prerelease_pythons or strtobool( - self.env.get("CIBW_PRERELEASE_PYTHONS", "0") - ) - - if free_threaded_support or prerelease_pythons: - msg = ( - "free-threaded-support and prerelease-pythons should be specified by enable instead" - ) - if enable: - raise OptionsReaderError(msg) - log.warning(msg) - - if free_threaded_support: - enable.add(EnableGroups.CPythonFreeThreading) - if prerelease_pythons: - enable.add(EnableGroups.CPythonPrerelease) + try: + enable = {EnableGroup(group) for group in enable_groups.split()} + for command_line_group in args.enable: + enable.add(EnableGroup(command_line_group)) + except ValueError as e: + msg = f"Failed to parse enable group. {e}. Valid group names are: {', '.join(g.value for g in EnableGroup)}" + raise errors.ConfigurationError(msg) from e # 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 @@ -655,30 +638,16 @@ def globals(self) -> GlobalOptions: build_config = args.only skip_config = "" architectures = Architecture.all_archs(self.platform) - enable = set(EnableGroups) + enable = set(EnableGroup) build_selector = BuildSelector( build_config=build_config, skip_config=skip_config, requires_python=requires_python, - enable=frozenset( - enable | {EnableGroups.PyPy} - ), # For backwards compatibility, we are adding PyPy for now + enable=frozenset(enable), ) test_selector = TestSelector(skip_config=test_skip) - all_configs = read_python_configs(self.platform) - all_pypy_ids = { - config["identifier"] for config in all_configs if config["identifier"].startswith("pp") - } - if ( - not self._defaults - and EnableGroups.PyPy not in enable - and any(build_selector(build_id) for build_id in all_pypy_ids) - ): - msg = "PyPy builds will be disabled by default in version 3. Enabling PyPy builds should be specified by enable" - log.warning(msg) - return GlobalOptions( package_dir=package_dir, output_dir=output_dir, @@ -976,7 +945,7 @@ def compute_options( return options -@functools.lru_cache(maxsize=None) +@functools.cache def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]: """ This looks like a dict of dicts, e.g. diff --git a/cibuildwheel/resources/cibuildwheel.schema.json b/cibuildwheel/resources/cibuildwheel.schema.json index e9ef3985b..5f432dc42 100644 --- a/cibuildwheel/resources/cibuildwheel.schema.json +++ b/cibuildwheel/resources/cibuildwheel.schema.json @@ -284,13 +284,6 @@ ], "title": "CIBW_ENVIRONMENT_PASS" }, - "free-threaded-support": { - "type": "boolean", - "default": false, - "description": "The project supports free-threaded builds of Python (PEP703)", - "deprecated": "Use the `enable` option instead.", - "title": "CIBW_FREE_THREADED_SUPPORT" - }, "manylinux-aarch64-image": { "type": "string", "description": "Specify alternative manylinux / musllinux container images", diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml index 3c56dfc58..27fd8214d 100644 --- a/cibuildwheel/resources/defaults.toml +++ b/cibuildwheel/resources/defaults.toml @@ -2,7 +2,6 @@ build = "*" skip = "" test-skip = "" -free-threaded-support = false enable = [] archs = ["auto"] diff --git a/cibuildwheel/typing.py b/cibuildwheel/typing.py index 367cfcdf1..34ae273c8 100644 --- a/cibuildwheel/typing.py +++ b/cibuildwheel/typing.py @@ -15,7 +15,7 @@ if typing.TYPE_CHECKING: PopenBytes = subprocess.Popen[bytes] - PathOrStr = Union[str, os.PathLike[str]] + PathOrStr = str | os.PathLike[str] else: PopenBytes = subprocess.Popen PathOrStr = Union[str, "os.PathLike[str]"] diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 905dd5410..abae60db4 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -14,13 +14,14 @@ import tarfile import textwrap import time +import tomllib import typing import urllib.request from collections import defaultdict from collections.abc import Generator, Iterable, Mapping, MutableMapping, Sequence from dataclasses import dataclass from enum import Enum -from functools import lru_cache, total_ordering +from functools import cache, total_ordering from pathlib import Path, PurePath from tempfile import TemporaryDirectory from time import sleep @@ -36,16 +37,14 @@ from packaging.version import Version from platformdirs import user_cache_path -from ._compat import tomllib from .architecture import Architecture from .errors import FatalError from .typing import PathOrStr, PlatformName __all__ = [ "MANYLINUX_ARCHS", - "EnableGroups", + "EnableGroup", "call", - "chdir", "combine_constraints", "find_compatible_wheel", "find_uv", @@ -69,7 +68,7 @@ test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py" -class EnableGroups(enum.Enum): +class EnableGroup(enum.Enum): """ Groups of build selectors that are not enabled by default. """ @@ -220,12 +219,12 @@ def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str: def prepare_command(command: str, **kwargs: PathOrStr) -> str: """ - Preprocesses a command by expanding variables like {python}. + Preprocesses a command by expanding variables like {project}. For example, used in the test_command option to specify the path to the project's root. Unmatched syntax will mostly be allowed through. """ - return format_safe(command, python="python", pip="pip", **kwargs) + return format_safe(command, **kwargs) def get_build_verbosity_extra_flags(level: int) -> list[str]: @@ -280,7 +279,7 @@ class BuildSelector: build_config: str skip_config: str requires_python: SpecifierSet | None = None - enable: frozenset[EnableGroups] = frozenset() + enable: frozenset[EnableGroup] = frozenset() def __call__(self, build_id: str) -> bool: # Filter build selectors by python_requires if set @@ -295,15 +294,15 @@ def __call__(self, build_id: str) -> bool: return False # filter out groups that are not enabled - if EnableGroups.CPythonFreeThreading not in self.enable and selector_matches( + if EnableGroup.CPythonFreeThreading not in self.enable and selector_matches( "cp3??t-*", build_id ): return False - if EnableGroups.CPythonPrerelease not in self.enable and selector_matches( + if EnableGroup.CPythonPrerelease not in self.enable and selector_matches( "cp314*", build_id ): return False - if EnableGroups.PyPy not in self.enable and selector_matches("pp*", build_id): + if EnableGroup.PyPy not in self.enable and selector_matches("pp*", build_id): return False should_build = selector_matches(self.build_config, build_id) @@ -596,7 +595,7 @@ def get_pip_version(env: Mapping[str, str]) -> str: return pip_version -@lru_cache(maxsize=None) +@cache def ensure_node(major_version: str) -> Path: input_file = resources_dir / "nodejs.toml" with input_file.open("rb") as f: @@ -628,7 +627,7 @@ def ensure_node(major_version: str) -> Path: return path -@lru_cache(maxsize=None) +@cache def _ensure_virtualenv(version: str) -> Path: version_parts = version.split(".") key = f"py{version_parts[0]}{version_parts[1]}" @@ -804,19 +803,6 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None: return None -# Can be replaced by contextlib.chdir in Python 3.11 -@contextlib.contextmanager -def chdir(new_path: Path | str) -> Generator[None, None, None]: - """Non thread-safe context manager to change the current working directory.""" - - cwd = os.getcwd() - try: - os.chdir(new_path) - 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 diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index 8f5633d67..c88430dee 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -7,14 +7,14 @@ import textwrap from collections.abc import MutableMapping, Sequence, Set from dataclasses import dataclass -from functools import lru_cache +from functools import cache from pathlib import Path +from typing import assert_never from filelock import FileLock from packaging.version import Version from . import errors -from ._compat.typing import assert_never from .architecture import Architecture from .environment import ParsedEnvironment from .logger import log @@ -96,7 +96,7 @@ def get_python_configurations( return python_configurations -@lru_cache(maxsize=None) +@cache def _ensure_nuget() -> Path: nuget = CIBW_CACHE_PATH / "nuget.exe" with FileLock(str(nuget) + ".lock"): diff --git a/docs/options.md b/docs/options.md index a1452e17a..ad8fee6fb 100644 --- a/docs/options.md +++ b/docs/options.md @@ -567,17 +567,14 @@ values are: - `cpython-prerelease`: Enables beta versions of Pythons if any are available - (May-July, approximately). For backward compatibility, `CIBW_PRERELEASE_PYTHONS` - is also supported until cibuildwheel 3. + (May-July, approximately). - `cpython-freethreading`: [PEP 703](https://www.python.org/dev/peps/pep-0703) introduced variants of CPython that can be built without the Global Interpreter Lock (GIL). Those variants are also known as free-threaded / no-gil. This will enable building these wheels while they are experimental. The build identifiers for those variants have a `t` suffix in their - `python_tag` (e.g. `cp313t-manylinux_x86_64`). For backward compatibility, - `CIBW_FREE_THREADED_SUPPORT` is also supported until cibuildwheel 3. -- `pypy`: Enable PyPy. For backward compatibility, this is always enabled until - cibuildwheel 3 is released. + `python_tag` (e.g. `cp313t-manylinux_x86_64`). +- `pypy`: Enable PyPy. !!! caution @@ -591,7 +588,7 @@ values are: !!! note Free threading is experimental: [What’s New In Python 3.13](https://docs.python.org/3.13/whatsnew/3.13.html#free-threaded-cpython) -Default: empty (`pypy` is always injected). +Default: empty. This option doesn't support overrides or platform specific variants; it is intended as a way to acknowledge that a project is aware that these extra diff --git a/pyproject.toml b/pyproject.toml index 3b0d72d1f..42b903fb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "2.22.0" description = "Build Python wheels on CI with minimal configuration." readme = "README.md" license = "BSD-2-Clause" -requires-python = ">=3.8" +requires-python = ">=3.11" authors = [ { name = "Joe Rickerby", email = "joerick@mac.com" }, ] @@ -30,9 +30,6 @@ classifiers = [ "Natural Language :: English", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -46,9 +43,7 @@ dependencies = [ "dependency-groups>=1.2", "filelock", "packaging>=20.9", - "platformdirs", - "tomli;python_version < '3.11'", - "typing-extensions>=4.1.0;python_version < '3.11'", + "platformdirs" ] [project.optional-dependencies] @@ -109,7 +104,7 @@ log_cli_level = "info" [tool.mypy] -python_version = "3.8" +python_version = "3.11" files = [ "cibuildwheel/*.py", "test/**/*.py", @@ -141,7 +136,7 @@ ignore_missing_imports = true [tool.pylint] -py-version = "3.8" +py-version = "3.11" jobs = "0" fail-on = ["E", "F"] fail-under = "9.8" @@ -211,7 +206,6 @@ ignore = [ "PYI025", # Set as AbstractSet "ISC001", # Conflicts with formatter ] -typing-modules = ["cibuildwheel._compat.typing"] flake8-unused-arguments.ignore-variadic-names = true [tool.ruff.lint.flake8-tidy-imports.banned-api] @@ -220,14 +214,9 @@ flake8-unused-arguments.ignore-variadic-names = true "typing.Iterator".msg = "Use collections.abc.Iterator instead." "typing.Sequence".msg = "Use collections.abc.Sequence instead." "typing.Set".msg = "Use collections.abc.Set instead." -"typing.NotRequired".msg = "Use cibuildwheel._compat.typing.NotRequired instead." -"typing.assert_never".msg = "Use cibuildwheel._compat.typing.assert_never instead." -"tomllib".msg = "Use cibuildwheel._compat.tomllib instead." -"tomli".msg = "Use cibuildwheel._compat.tomllib instead." [tool.ruff.lint.per-file-ignores] "unit_test/*" = ["PLC1901"] -"cibuildwheel/_compat/**.py" = ["TID251"] "bin/*" = ["TID251"] [tool.repo-review] diff --git a/test/conftest.py b/test/conftest.py index a42a3ce9a..42a0d9acf 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -2,7 +2,7 @@ import json import subprocess -from typing import Generator +from collections.abc import Generator import pytest diff --git a/test/test_projects/base.py b/test/test_projects/base.py index d7a9305a5..99ccc8b33 100644 --- a/test/test_projects/base.py +++ b/test/test_projects/base.py @@ -1,12 +1,12 @@ from __future__ import annotations from pathlib import Path -from typing import Any, Dict, Union +from typing import Any import jinja2 -FilesDict = Dict[str, Union[str, jinja2.Template]] -TemplateContext = Dict[str, Any] +FilesDict = dict[str, str | jinja2.Template] +TemplateContext = dict[str, Any] class TestProject: diff --git a/test/utils.py b/test/utils.py index aa23638d6..309a5649a 100644 --- a/test/utils.py +++ b/test/utils.py @@ -47,11 +47,11 @@ def cibuildwheel_get_build_identifiers( for the current platform. """ cmd = [sys.executable, "-m", "cibuildwheel", "--print-build-identifiers", str(project_path)] - if prerelease_pythons: - cmd.append("--prerelease-pythons") if env is None: env = os.environ.copy() - env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1") + env["CIBW_ENABLE"] = "cpython-freethreading pypy" + if prerelease_pythons: + env["CIBW_ENABLE"] += " cpython-prerelease" cmd_output = subprocess.run( cmd, @@ -115,7 +115,7 @@ def cibuildwheel_run( _update_pip_cache_dir(env) - env.setdefault("CIBW_FREE_THREADED_SUPPORT", "1") + env["CIBW_ENABLE"] = "cpython-prerelease cpython-freethreading pypy" if single_python: env["CIBW_BUILD"] = "cp{}{}-*".format(*SINGLE_PYTHON_VERSION) @@ -126,7 +126,6 @@ def cibuildwheel_run( sys.executable, "-m", "cibuildwheel", - "--prerelease-pythons", "--output-dir", str(output_dir or tmp_output_dir), str(package_dir), diff --git a/unit_test/build_ids_test.py b/unit_test/build_ids_test.py index a42af93de..0a444e85c 100644 --- a/unit_test/build_ids_test.py +++ b/unit_test/build_ids_test.py @@ -1,8 +1,9 @@ from __future__ import annotations +import tomllib + from packaging.version import Version -from cibuildwheel._compat import tomllib from cibuildwheel.extra import Printable, dump_python_configurations from cibuildwheel.util import resources_dir diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py index 886202007..84359abba 100644 --- a/unit_test/build_selector_test.py +++ b/unit_test/build_selector_test.py @@ -2,12 +2,12 @@ from packaging.specifiers import SpecifierSet -from cibuildwheel.util import BuildSelector, EnableGroups +from cibuildwheel.util import BuildSelector, EnableGroup def test_build(): build_selector = BuildSelector( - build_config="cp3*-* *-manylinux*", skip_config="", enable=frozenset([EnableGroups.PyPy]) + build_config="cp3*-* *-manylinux*", skip_config="", enable=frozenset([EnableGroup.PyPy]) ) assert build_selector("cp36-manylinux_x86_64") @@ -45,7 +45,7 @@ def test_build_filter_pre(): build_selector = BuildSelector( build_config="cp3*-* *-manylinux*", skip_config="", - enable=frozenset([EnableGroups.CPythonPrerelease, EnableGroups.PyPy]), + enable=frozenset([EnableGroup.CPythonPrerelease, EnableGroup.PyPy]), ) assert build_selector("cp37-manylinux_x86_64") @@ -59,7 +59,7 @@ def test_skip(): build_selector = BuildSelector( build_config="*", skip_config="pp36-* cp3?-manylinux_i686 cp36-win* *-win32", - enable=frozenset([EnableGroups.PyPy]), + enable=frozenset([EnableGroup.PyPy]), ) assert not build_selector("pp36-manylinux_x86_64") @@ -85,7 +85,7 @@ def test_build_and_skip(): build_selector = BuildSelector( build_config="cp36-* cp37-macosx* *-manylinux*", skip_config="pp37-* cp37-manylinux_i686", - enable=frozenset([EnableGroups.PyPy]), + enable=frozenset([EnableGroup.PyPy]), ) assert not build_selector("pp37-manylinux_x86_64") @@ -119,7 +119,7 @@ def test_build_limited_python(): build_config="*", skip_config="", requires_python=SpecifierSet(">=3.7"), - enable=frozenset([EnableGroups.PyPy]), + enable=frozenset([EnableGroup.PyPy]), ) assert not build_selector("cp36-manylinux_x86_64") @@ -155,7 +155,7 @@ def test_build_limited_python_patch(): def test_build_free_threaded_python(): - build_selector = BuildSelector(build_config="*", skip_config="", enable=frozenset(EnableGroups)) + build_selector = BuildSelector(build_config="*", skip_config="", enable=frozenset(EnableGroup)) assert build_selector("cp313t-manylinux_x86_64") diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py index dfcc7d87b..c9c312e50 100644 --- a/unit_test/get_platform_test.py +++ b/unit_test/get_platform_test.py @@ -1,7 +1,6 @@ import contextlib import sys from pathlib import Path -from typing import Dict import pytest import setuptools._distutils.util @@ -16,7 +15,7 @@ @contextlib.contextmanager -def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: Dict[str, str]): +def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: dict[str, str]): with monkeypatch.context() as mp: for envvar, val in environment.items(): mp.setenv(name=envvar, value=val) @@ -25,7 +24,7 @@ def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: Dict[str, def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): arch = "32" - environment: Dict[str, str] = {} + environment: dict[str, str] = {} configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) @@ -39,7 +38,7 @@ def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): arch = "64" - environment: Dict[str, str] = {} + environment: dict[str, str] = {} configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) @@ -56,7 +55,7 @@ def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): ) def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): arch = "ARM64" - environment: Dict[str, str] = {} + environment: dict[str, str] = {} configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py index 20dfb158d..e39c09446 100644 --- a/unit_test/main_tests/conftest.py +++ b/unit_test/main_tests/conftest.py @@ -8,7 +8,7 @@ import pytest -from cibuildwheel import linux, macos, util, windows +from cibuildwheel import linux, macos, pyodide, util, windows class ArgsInterceptor: @@ -42,6 +42,7 @@ def ignore_call(*args, **kwargs): monkeypatch.setattr(windows, "build", fail_on_call) monkeypatch.setattr(linux, "build", fail_on_call) monkeypatch.setattr(macos, "build", fail_on_call) + monkeypatch.setattr(pyodide, "build", fail_on_call) monkeypatch.setattr(Path, "mkdir", ignore_call) @@ -88,6 +89,7 @@ def intercepted_build_args(monkeypatch): monkeypatch.setattr(linux, "build", intercepted) monkeypatch.setattr(macos, "build", intercepted) monkeypatch.setattr(windows, "build", intercepted) + monkeypatch.setattr(pyodide, "build", intercepted) yield intercepted diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index fe3b21189..572076864 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -1,16 +1,16 @@ from __future__ import annotations import sys +import tomllib from fnmatch import fnmatch from pathlib import Path import pytest from cibuildwheel.__main__ import main -from cibuildwheel._compat import tomllib from cibuildwheel.environment import ParsedEnvironment from cibuildwheel.options import BuildOptions, _get_pinned_container_images -from cibuildwheel.util import BuildSelector, resources_dir, split_config_settings +from cibuildwheel.util import BuildSelector, EnableGroup, resources_dir, split_config_settings # CIBW_PLATFORM is tested in main_platform_test.py @@ -126,7 +126,7 @@ def get_default_repair_command(platform: str) -> str: return "auditwheel repair -w {dest_dir} {wheel}" elif platform == "macos": return "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" - elif platform == "windows": + elif platform == "windows" or platform == "pyodide": return "" else: msg = f"Unknown platform: {platform!r}" @@ -368,6 +368,45 @@ def test_debug_traceback(monkeypatch, method, capfd): assert "Traceback (most recent call last)" in err +@pytest.mark.parametrize("method", ["unset", "command_line", "env_var"]) +def test_enable(method, intercepted_build_args, monkeypatch): + if method == "command_line": + monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "pypy"]) + elif method == "env_var": + monkeypatch.setenv("CIBW_ENABLE", "pypy") + + main() + + enable_groups = intercepted_build_args.args[0].globals.build_selector.enable + + if method == "unset": + assert enable_groups == frozenset() + else: + assert enable_groups == frozenset([EnableGroup.PyPy]) + + +def test_enable_arg_inherits(intercepted_build_args, monkeypatch): + monkeypatch.setenv("CIBW_ENABLE", "pypy") + monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"]) + + main() + + enable_groups = intercepted_build_args.args[0].globals.build_selector.enable + + assert enable_groups == frozenset((EnableGroup.PyPy, EnableGroup.CPythonPrerelease)) + + +def test_enable_arg_error_message(monkeypatch, capsys): + monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "invalid_group"]) + + with pytest.raises(SystemExit) as ex: + main() + assert ex.value.code == 2 + + _, err = capsys.readouterr() + assert "Valid group names are:" in err + + def test_defaults(platform, intercepted_build_args): main() diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py index fbc861595..f98e5805f 100644 --- a/unit_test/main_tests/main_platform_test.py +++ b/unit_test/main_tests/main_platform_test.py @@ -6,7 +6,7 @@ from cibuildwheel.__main__ import main from cibuildwheel.architecture import Architecture -from cibuildwheel.util import EnableGroups +from cibuildwheel.util import EnableGroup from ..conftest import MOCK_PACKAGE_DIR @@ -217,7 +217,7 @@ def test_only_argument(intercepted_build_args, monkeypatch, only, plat): assert options.globals.build_selector.skip_config == "" assert options.platform == plat assert options.globals.architectures == Architecture.all_archs(plat) - assert EnableGroups.PyPy in options.globals.build_selector.enable + assert EnableGroup.PyPy in options.globals.build_selector.enable @pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing")) diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py index 9d6e2c430..deb0c6fba 100644 --- a/unit_test/option_prepare_test.py +++ b/unit_test/option_prepare_test.py @@ -14,20 +14,8 @@ from cibuildwheel.__main__ import main from cibuildwheel.oci_container import OCIPlatform -ALL_IDS = { - "cp36", - "cp37", - "cp38", - "cp39", - "cp310", - "cp311", - "cp312", - "cp313", - "pp37", - "pp38", - "pp39", - "pp310", -} +DEFAULT_IDS = {"cp36", "cp37", "cp38", "cp39", "cp310", "cp311", "cp312", "cp313"} +ALL_IDS = DEFAULT_IDS | {"cp313t", "pp37", "pp38", "pp39", "pp310"} @pytest.fixture @@ -77,7 +65,7 @@ def test_build_default_launches(monkeypatch): assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64 identifiers = {x.identifier for x in kwargs["platform_configs"]} - assert identifiers == {f"{x}-manylinux_x86_64" for x in ALL_IDS} + assert identifiers == {f"{x}-manylinux_x86_64" for x in DEFAULT_IDS} kwargs = build_in_container.call_args_list[1][1] assert "quay.io/pypa/manylinux2014_i686" in kwargs["container"]["image"] @@ -85,7 +73,7 @@ def test_build_default_launches(monkeypatch): assert kwargs["container"]["oci_platform"] == OCIPlatform.i386 identifiers = {x.identifier for x in kwargs["platform_configs"]} - assert identifiers == {f"{x}-manylinux_i686" for x in ALL_IDS} + assert identifiers == {f"{x}-manylinux_i686" for x in DEFAULT_IDS} kwargs = build_in_container.call_args_list[2][1] assert "quay.io/pypa/musllinux_1_2_x86_64" in kwargs["container"]["image"] @@ -93,9 +81,7 @@ def test_build_default_launches(monkeypatch): assert kwargs["container"]["oci_platform"] == OCIPlatform.AMD64 identifiers = {x.identifier for x in kwargs["platform_configs"]} - assert identifiers == { - f"{x}-musllinux_x86_64" for x in ALL_IDS for x in ALL_IDS if "pp" not in x - } + assert identifiers == {f"{x}-musllinux_x86_64" for x in DEFAULT_IDS} kwargs = build_in_container.call_args_list[3][1] assert "quay.io/pypa/musllinux_1_2_i686" in kwargs["container"]["image"] @@ -103,7 +89,7 @@ def test_build_default_launches(monkeypatch): assert kwargs["container"]["oci_platform"] == OCIPlatform.i386 identifiers = {x.identifier for x in kwargs["platform_configs"]} - assert identifiers == {f"{x}-musllinux_i686" for x in ALL_IDS if "pp" not in x} + assert identifiers == {f"{x}-musllinux_i686" for x in DEFAULT_IDS} @pytest.mark.usefixtures("mock_build_container") @@ -117,6 +103,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path): [tool.cibuildwheel] manylinux-x86_64-image = "manylinux_2_28" musllinux-x86_64-image = "musllinux_1_2" +enable = ["pypy", "cpython-freethreading"] # Before Python 3.10, use manylinux2014, musllinux_1_1 [[tool.cibuildwheel.overrides]] @@ -158,7 +145,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path): assert identifiers == { f"{x}-manylinux_x86_64" for x in ALL_IDS - - {"cp36", "cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310"} + - {"cp36", "cp310", "cp311", "cp312", "cp313", "cp313t", "pp37", "pp38", "pp39", "pp310"} } assert kwargs["options"].build_options("cp37-manylinux_x86_64").before_all == "" @@ -169,7 +156,7 @@ def test_build_with_override_launches(monkeypatch, tmp_path): identifiers = {x.identifier for x in kwargs["platform_configs"]} assert identifiers == { f"{x}-manylinux_x86_64" - for x in ["cp310", "cp311", "cp312", "cp313", "pp37", "pp38", "pp39", "pp310"] + for x in ["cp310", "cp311", "cp312", "cp313", "cp313t", "pp37", "pp38", "pp39", "pp310"] } kwargs = build_in_container.call_args_list[3][1] diff --git a/unit_test/options_test.py b/unit_test/options_test.py index fb6727b0c..1b9422c9e 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -15,7 +15,7 @@ Options, _get_pinned_container_images, ) -from cibuildwheel.util import EnableGroups +from cibuildwheel.util import EnableGroup PYPROJECT_1 = """ [tool.cibuildwheel] @@ -426,24 +426,32 @@ def test_override_inherit_environment_with_references(tmp_path: Path) -> None: @pytest.mark.parametrize( - ("toml_assignment", "env", "expected_result"), + ("toml_assignment", "env", "enable_args", "expected_result"), [ - ("", {}, False), - ("free-threaded-support = true", {}, True), - ("free-threaded-support = false", {}, False), - ("", {"CIBW_FREE_THREADED_SUPPORT": "0"}, False), - ("", {"CIBW_FREE_THREADED_SUPPORT": "1"}, True), - ("free-threaded-support = false", {"CIBW_FREE_THREADED_SUPPORT": "1"}, True), - ("free-threaded-support = true", {"CIBW_FREE_THREADED_SUPPORT": "0"}, False), - ("free-threaded-support = true", {"CIBW_FREE_THREADED_SUPPORT": ""}, True), - ("free-threaded-support = false", {"CIBW_FREE_THREADED_SUPPORT": ""}, False), + ("", {}, [], False), + ("enable = ['cpython-freethreading']", {}, [], True), + ("enable = []", {}, [], False), + ("", {}, ["cpython-freethreading"], True), + ("", {}, ["cpython-freethreading", "pypy"], True), + ("", {"CIBW_ENABLE": "pypy"}, [], False), + ("", {"CIBW_ENABLE": "cpython-freethreading"}, [], True), + ("enable = []", {"CIBW_ENABLE": "cpython-freethreading"}, [], True), + ("enable = ['cpython-freethreading']", {"CIBW_ENABLE": "pypy"}, [], True), + ("enable = ['cpython-freethreading']", {}, ["pypy"], True), + ("enable = ['cpython-freethreading']", {"CIBW_ENABLE": ""}, [], True), + ("enable = []", {"CIBW_ENABLE": ""}, [], False), ], ) def test_free_threaded_support( - tmp_path: Path, toml_assignment: str, env: dict[str, str], expected_result: bool + tmp_path: Path, + toml_assignment: str, + env: dict[str, str], + enable_args: list[str], + expected_result: bool, ) -> None: args = CommandLineArguments.defaults() args.package_dir = tmp_path + args.enable = enable_args pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text( @@ -456,6 +464,6 @@ def test_free_threaded_support( ) options = Options(platform="linux", command_line_arguments=args, env=env) if expected_result: - assert EnableGroups.CPythonFreeThreading in options.globals.build_selector.enable + assert EnableGroup.CPythonFreeThreading in options.globals.build_selector.enable else: - assert EnableGroups.CPythonFreeThreading not in options.globals.build_selector.enable + assert EnableGroup.CPythonFreeThreading not in options.globals.build_selector.enable diff --git a/unit_test/projectfiles_test.py b/unit_test/projectfiles_test.py index 179f648ef..77ea78cf1 100644 --- a/unit_test/projectfiles_test.py +++ b/unit_test/projectfiles_test.py @@ -1,10 +1,10 @@ from __future__ import annotations +import tomllib from textwrap import dedent import pytest -from cibuildwheel._compat import tomllib from cibuildwheel.projectfiles import ( get_requires_python_str, resolve_dependency_groups, diff --git a/unit_test/validate_schema_test.py b/unit_test/validate_schema_test.py index bb102bb25..c0dfad454 100644 --- a/unit_test/validate_schema_test.py +++ b/unit_test/validate_schema_test.py @@ -1,13 +1,12 @@ from __future__ import annotations import re +import tomllib from pathlib import Path import pytest import validate_pyproject.api -from cibuildwheel._compat import tomllib - DIR = Path(__file__).parent.resolve()