feat: use python3.11+ for cibuildwheel driver (#1912)

* chore: use SPEC 0 schedule for cibuildwheel

* remove support for {python} and {pip} in commands

* Remove specific Python versions from the update-dependencies job

The requirements pinning is done by uv now, so we don't need to
run the versions of Python to do the pinning anymore.

---------

Co-authored-by: Joe Rickerby <joerick@mac.com>
This commit is contained in:
Matthieu Darbois
2025-01-16 10:45:18 -05:00
committed by GitHub
co-authored by Joe Rickerby
parent 1608f7567e
commit a96545b729
33 changed files with 117 additions and 166 deletions
+23 -12
View File
@@ -7,7 +7,6 @@ run_tests: &RUN_TESTS
run_cibuildwheel_tests_script: run_cibuildwheel_tests_script:
- python ./bin/run_tests.py - python ./bin/run_tests.py
linux_x86_task: linux_x86_task:
timeout_in: 120m timeout_in: 120m
compute_engine_instance: compute_engine_instance:
@@ -16,10 +15,15 @@ linux_x86_task:
platform: linux platform: linux
cpu: 8 cpu: 8
memory: 8G memory: 8G
env:
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
install_pre_requirements_script: install_pre_requirements_script:
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all - 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 <<: *RUN_TESTS
linux_aarch64_task: linux_aarch64_task:
@@ -30,10 +34,15 @@ linux_aarch64_task:
platform: linux platform: linux
cpu: 4 cpu: 4
memory: 4G memory: 4G
env:
VENV_ROOT: ${HOME}/venv-cibuildwheel
PATH: ${VENV_ROOT}/bin:${PATH}
install_pre_requirements_script: install_pre_requirements_script:
- docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all - 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 <<: *RUN_TESTS
windows_x86_task: windows_x86_task:
@@ -46,7 +55,7 @@ windows_x86_task:
memory: 8G memory: 8G
install_pre_requirements_script: 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 - refreshenv
- echo PATH=%PATH% >> "%CIRRUS_ENV%" - echo PATH=%PATH% >> "%CIRRUS_ENV%"
<<: *RUN_TESTS <<: *RUN_TESTS
@@ -54,22 +63,24 @@ windows_x86_task:
macos_arm64_task: macos_arm64_task:
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-runner:sonoma image: ghcr.io/cirruslabs/macos-runner:sonoma
env: 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: install_pre_requirements_script:
- brew install python@3.10 - brew install python@3.12
- python3.12 -m venv ${VENV_ROOT}
<<: *RUN_TESTS <<: *RUN_TESTS
macos_arm64_cp38_task: macos_arm64_cp38_task:
macos_instance: macos_instance:
image: ghcr.io/cirruslabs/macos-runner:sonoma image: ghcr.io/cirruslabs/macos-runner:sonoma
env: 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' PYTEST_ADDOPTS: --run-cp38-universal2 -k 'test_cp38_arm64_testing_universal2_installer or test_arch_auto'
install_pre_requirements_script: 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 - 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 / - sudo installer -pkg python-3.8.10-macos11.pkg -target /
- rm python-3.8.10-macos11.pkg - rm python-3.8.10-macos11.pkg
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
python_version: ['3.13'] python_version: ['3.13']
include: include:
- os: ubuntu-latest - os: ubuntu-latest
python_version: '3.8' python_version: '3.11'
timeout-minutes: 180 timeout-minutes: 180
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -33,8 +33,6 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: wntrblm/nox@2024.10.09 - uses: wntrblm/nox@2024.10.09
with:
python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13"
- name: "Run update: dependencies" - name: "Run update: dependencies"
run: nox --force-color -s update_constraints run: nox --force-color -s update_constraints
+4 -4
View File
@@ -24,9 +24,9 @@ repos:
rev: v1.14.1 rev: v1.14.1
hooks: hooks:
- id: mypy - id: mypy
name: mypy 3.8 on cibuildwheel/ name: mypy 3.11 on cibuildwheel/
exclude: ^cibuildwheel/resources/.*py|bin/generate_schema.py$ exclude: ^cibuildwheel/resources/.*py|bin/generate_schema.py$
args: ["--python-version=3.8"] args: ["--python-version=3.11"]
additional_dependencies: &mypy-dependencies additional_dependencies: &mypy-dependencies
- bracex - bracex
- dependency-groups>=1.2 - dependency-groups>=1.2
@@ -47,9 +47,9 @@ repos:
- uv - uv
- validate-pyproject - validate-pyproject
- id: mypy - id: mypy
name: mypy 3.12 name: mypy 3.13
exclude: ^cibuildwheel/resources/.*py$ exclude: ^cibuildwheel/resources/.*py$
args: ["--python-version=3.12"] args: ["--python-version=3.13"]
additional_dependencies: *mypy-dependencies additional_dependencies: *mypy-dependencies
- repo: https://github.com/shellcheck-py/shellcheck-py - repo: https://github.com/shellcheck-py/shellcheck-py
+11 -11
View File
@@ -8,13 +8,13 @@ branches:
jobs: jobs:
include: include:
- name: Linux | x86_64 + i686 | Python 3.9 - name: Linux | x86_64 + i686 | Python 3.11
python: 3.9 python: 3.11
services: docker services: docker
env: PYTHON=python env: PYTHON=python
- name: Linux | arm64 | Python 3.9 - name: Linux | arm64 | Python 3.11
python: 3.9 python: 3.11
services: docker services: docker
arch: arm64-graviton2 arch: arm64-graviton2
group: edge group: edge
@@ -29,8 +29,8 @@ jobs:
packages: packages:
- docker-ce docker-ce-cli containerd.io - docker-ce docker-ce-cli containerd.io
- name: Linux | ppc64le | Python 3.9 - name: Linux | ppc64le | Python 3.11
python: 3.9 python: 3.11
services: docker services: docker
arch: ppc64le arch: ppc64le
allow_failure: True 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 # 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"' - PYTEST_ADDOPTS='-k "not test_manylinuxXXXX_only"'
- name: Windows | x86_64 | Python 3.9 - name: Windows | x86_64 | Python 3.11
os: windows os: windows
language: shell language: shell
before_install: 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: env:
- PYTHON=C:\\Python39\\python - PYTHON=C:\\Python311\\python
- name: Linux | s390x | Python 3.9 - name: Linux | s390x | Python 3.11
python: 3.9 python: 3.11
services: docker services: docker
arch: s390x arch: s390x
allow_failure: True allow_failure: True
+6 -6
View File
@@ -1,11 +1,11 @@
This is a summary of the host Python versions and platforms covered by the different CI platforms: 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 | | | 3.11 | 3.12 | 3.13 |
|---------|----------------------------------|-----------|-----------|---------|--------------------------------------------------| |---------|----------------------------------------------|---------------------------------------------|----------------|
| Linux | Azure Pipelines / GitHub Actions | Travis CI | Cirrus CI | | AppVeyor¹ / CircleCI¹ / GitHub Actions / GitLab¹ | | Linux | Azure Pipelines / GitHub Actions / Travis CI | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions |
| macOS | Azure Pipelines | | Cirrus CI | GitLab¹ | AppVeyor¹ /CircleCI¹ / GitHub Actions | | macOS | Azure Pipelines / GitLab¹ | AppVeyor¹ / CircleCI¹ / Cirrus CI / GitLab¹ | GitHub Actions |
| Windows | Azure Pipelines | Travis CI | Cirrus CI | | AppVeyor¹ / GitHub Actions / GitLab¹ | | Windows | Azure Pipelines / Travis CI | AppVeyor¹ / Cirrus CI / GitLab¹ | GitHub Actions |
> ¹ Runs a reduced set of tests to reduce CI load > ¹ 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.
+6 -6
View File
@@ -5,37 +5,37 @@ pr:
- .pre-commit-config.yaml - .pre-commit-config.yaml
jobs: jobs:
- job: linux_38 - job: linux_311
timeoutInMinutes: 120 timeoutInMinutes: 120
pool: {vmImage: 'Ubuntu-20.04'} pool: {vmImage: 'Ubuntu-20.04'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.11'
- bash: | - bash: |
docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all docker run --rm --privileged docker.io/tonistiigi/binfmt:latest --install all
python -m pip install dependency-groups python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e. python -m dependency_groups test | xargs python -m pip install -e.
python ./bin/run_tests.py python ./bin/run_tests.py
- job: macos_38 - job: macos_311
pool: {vmImage: 'macOS-13'} pool: {vmImage: 'macOS-13'}
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.11'
- bash: | - bash: |
python -m pip install dependency-groups python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e. python -m dependency_groups test | xargs python -m pip install -e.
python ./bin/run_tests.py --num-processes 2 python ./bin/run_tests.py --num-processes 2
- job: windows_38 - job: windows_311
pool: {vmImage: 'windows-2019'} pool: {vmImage: 'windows-2019'}
timeoutInMinutes: 180 timeoutInMinutes: 180
steps: steps:
- task: UsePythonVersion@0 - task: UsePythonVersion@0
inputs: inputs:
versionSpec: '3.8' versionSpec: '3.11'
- bash: | - bash: |
python -m pip install dependency-groups python -m pip install dependency-groups
python -m dependency_groups test | xargs python -m pip install -e. python -m dependency_groups test | xargs python -m pip install -e.
+1 -5
View File
@@ -11,17 +11,13 @@ import glob
import os import os
import subprocess import subprocess
import sys import sys
import tomllib
import urllib.parse import urllib.parse
from pathlib import Path from pathlib import Path
import click import click
from packaging.version import InvalidVersion, Version from packaging.version import InvalidVersion, Version
if sys.version_info < (3, 11):
import tomli as tomllib
else:
import tomllib
config = [ config = [
# file path, version find/replace format # file path, version find/replace format
("pyproject.toml", 'version = "{}"'), ("pyproject.toml", 'version = "{}"'),
+1 -2
View File
@@ -4,6 +4,7 @@ from __future__ import annotations
import difflib import difflib
import logging import logging
import tomllib
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Final from typing import Final
@@ -16,8 +17,6 @@ from packaging.version import InvalidVersion, Version
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.syntax import Syntax from rich.syntax import Syntax
from cibuildwheel._compat import tomllib
log = logging.getLogger("cibw") log = logging.getLogger("cibw")
# Looking up the dir instead of using utils.resources_dir # Looking up the dir instead of using utils.resources_dir
+3 -3
View File
@@ -5,9 +5,10 @@ from __future__ import annotations
import copy import copy
import difflib import difflib
import logging import logging
import tomllib
from collections.abc import Mapping, MutableMapping from collections.abc import Mapping, MutableMapping
from pathlib import Path from pathlib import Path
from typing import Any, Final, Literal, TypedDict, Union from typing import Any, Final, Literal, TypedDict
import click import click
import requests import requests
@@ -17,7 +18,6 @@ from packaging.version import Version
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.syntax import Syntax from rich.syntax import Syntax
from cibuildwheel._compat import tomllib
from cibuildwheel.extra import dump_python_configurations from cibuildwheel.extra import dump_python_configurations
log = logging.getLogger("cibw") log = logging.getLogger("cibw")
@@ -50,7 +50,7 @@ class ConfigMacOS(TypedDict):
url: str 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 # The following set of "Versions" classes allow the initial call to the APIs to
+1 -2
View File
@@ -5,6 +5,7 @@ from __future__ import annotations
import difflib import difflib
import logging import logging
import subprocess import subprocess
import tomllib
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Final from typing import Final
@@ -15,8 +16,6 @@ from packaging.version import InvalidVersion, Version
from rich.logging import RichHandler from rich.logging import RichHandler
from rich.syntax import Syntax from rich.syntax import Syntax
from cibuildwheel._compat import tomllib
log = logging.getLogger("cibw") log = logging.getLogger("cibw")
# Looking up the dir instead of using utils.resources_dir # Looking up the dir instead of using utils.resources_dir
+6 -7
View File
@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import contextlib
import dataclasses import dataclasses
import os import os
import shutil import shutil
@@ -12,7 +13,7 @@ import typing
from collections.abc import Iterable, Sequence, Set from collections.abc import Iterable, Sequence, Set
from pathlib import Path from pathlib import Path
from tempfile import mkdtemp from tempfile import mkdtemp
from typing import Protocol from typing import Protocol, assert_never
import cibuildwheel import cibuildwheel
import cibuildwheel.linux import cibuildwheel.linux
@@ -21,7 +22,6 @@ import cibuildwheel.pyodide
import cibuildwheel.util import cibuildwheel.util
import cibuildwheel.windows import cibuildwheel.windows
from cibuildwheel import errors from cibuildwheel import errors
from cibuildwheel._compat.typing import assert_never
from cibuildwheel.architecture import Architecture, allowed_architectures_check from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.logger import log from cibuildwheel.logger import log
from cibuildwheel.options import CommandLineArguments, Options, compute_options from cibuildwheel.options import CommandLineArguments, Options, compute_options
@@ -31,7 +31,6 @@ from cibuildwheel.util import (
BuildSelector, BuildSelector,
CIProvider, CIProvider,
Unbuffered, Unbuffered,
chdir,
detect_ci_provider, detect_ci_provider,
fix_ansi_codes_for_github_actions, fix_ansi_codes_for_github_actions,
strtobool, strtobool,
@@ -200,7 +199,7 @@ def main_inner(global_options: GlobalOptions) -> None:
# This is now the new package dir # This is now the new package dir
args.package_dir = project_dir.resolve() args.package_dir = project_dir.resolve()
with chdir(project_dir): with contextlib.chdir(project_dir):
build_in_directory(args) build_in_directory(args)
finally: finally:
# avoid https://github.com/python/cpython/issues/86962 by performing # avoid https://github.com/python/cpython/issues/86962 by performing
@@ -418,10 +417,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): 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 # Reminder: in an f-string, double braces means literal single brace
msg = ( msg = (
f"{option_name}: '{{python}}' and '{{pip}}' are no longer needed, " f"{option_name}: '{{python}}' and '{{pip}}' are no longer supported "
"and will be removed in cibuildwheel 3. Simply use 'python' or 'pip' instead." "and have been removed in cibuildwheel 3. Simply use 'python' or 'pip' instead."
) )
warnings.append(msg) raise errors.ConfigurationError(msg)
return warnings return warnings
-1
View File
@@ -1 +0,0 @@
from __future__ import annotations
-10
View File
@@ -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"]
-14
View File
@@ -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",
)
+1 -2
View File
@@ -6,9 +6,8 @@ import re
import sys import sys
from collections.abc import Set from collections.abc import Set
from enum import Enum 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 from .typing import PlatformName
PRETTY_NAMES: Final[dict[PlatformName, str]] = { PRETTY_NAMES: Final[dict[PlatformName, str]] = {
+7 -3
View File
@@ -1,14 +1,18 @@
from __future__ import annotations from __future__ import annotations
import subprocess import subprocess
from collections.abc import Iterable, Mapping, Sequence from collections.abc import (
Callable,
Iterable,
Mapping,
Sequence,
)
from dataclasses import dataclass from dataclasses import dataclass
from typing import Callable, Dict, List # noqa: TID251
import bashlex import bashlex
# a function that takes a command and the environment, and returns the result # 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: def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) -> str:
+3 -3
View File
@@ -4,15 +4,15 @@ import contextlib
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
from collections import OrderedDict
from collections.abc import Iterable, Iterator, Sequence, Set from collections.abc import Iterable, Iterator, Sequence, Set
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath from pathlib import Path, PurePath, PurePosixPath
from typing import OrderedDict, Tuple from typing import assert_never
from packaging.version import Version from packaging.version import Version
from . import errors from . import errors
from ._compat.typing import assert_never
from .architecture import Architecture from .architecture import Architecture
from .logger import log from .logger import log
from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform from .oci_container import OCIContainer, OCIContainerEngineConfig, OCIPlatform
@@ -104,7 +104,7 @@ def get_build_steps(
Groups PythonConfigurations into BuildSteps. Each BuildStep represents a Groups PythonConfigurations into BuildSteps. Each BuildStep represents a
separate container instance. separate container instance.
""" """
steps = OrderedDict[Tuple[str, str, str, OCIContainerEngineConfig], BuildStep]() steps = OrderedDict[tuple[str, str, str, OCIContainerEngineConfig], BuildStep]()
for config in python_configurations: for config in python_configurations:
_, platform_tag = config.identifier.split("-", 1) _, platform_tag = config.identifier.split("-", 1)
+2 -2
View File
@@ -5,11 +5,11 @@ import os
import re import re
import sys import sys
import time import time
from typing import IO, AnyStr, Final, Tuple from typing import IO, AnyStr, Final
from .util import CIProvider, detect_ci_provider from .util import CIProvider, detect_ci_provider
FoldPattern = Tuple[str, str] FoldPattern = tuple[str, str]
DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "") DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "")
FOLD_PATTERNS: Final[dict[str, FoldPattern]] = { FOLD_PATTERNS: Final[dict[str, FoldPattern]] = {
"azure": ("##[group]{name}", "##[endgroup]"), "azure": ("##[group]{name}", "##[endgroup]"),
+4 -5
View File
@@ -12,13 +12,12 @@ import typing
from collections.abc import Sequence, Set from collections.abc import Sequence, Set
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Literal, Tuple from typing import Literal, assert_never
from filelock import FileLock from filelock import FileLock
from packaging.version import Version from packaging.version import Version
from . import errors from . import errors
from ._compat.typing import assert_never
from .architecture import Architecture from .architecture import Architecture
from .environment import ParsedEnvironment from .environment import ParsedEnvironment
from .logger import log from .logger import log
@@ -50,7 +49,7 @@ from .util import (
) )
@functools.lru_cache(maxsize=None) @functools.cache
def get_macos_version() -> tuple[int, int]: def get_macos_version() -> tuple[int, int]:
""" """
Returns the macOS major/minor version, as a tuple, e.g. (10, 15) or (11, 0) 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, capture_stdout=True,
) )
version = tuple(map(int, version_str.split(".")[:2])) 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: def get_test_macosx_deployment_target() -> str:
version = get_macos_version() version = get_macos_version()
if version >= (11, 0): if version >= (11, 0):
+2 -3
View File
@@ -16,9 +16,8 @@ from dataclasses import dataclass, field
from enum import Enum from enum import Enum
from pathlib import Path, PurePath, PurePosixPath from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType 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 .errors import OCIEngineTooOldError
from .logger import log from .logger import log
from .typing import PathOrStr, PopenBytes from .typing import PathOrStr, PopenBytes
@@ -489,7 +488,7 @@ class OCIContainer:
capture_output=True, 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: def environment_executor(self, command: Sequence[str], environment: dict[str, str]) -> str:
# used as an EnvironmentExecutor to evaluate commands and capture output # used as an EnvironmentExecutor to evaluate commands and capture output
+8 -9
View File
@@ -9,15 +9,14 @@ import enum
import functools import functools
import shlex import shlex
import textwrap import textwrap
from collections.abc import Callable, Generator, Iterable, Set import tomllib
from collections.abc import Callable, Generator, Iterable, Mapping, Sequence, Set
from pathlib import Path 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 packaging.specifiers import SpecifierSet
from . import errors from . import errors
from ._compat import tomllib
from ._compat.typing import assert_never
from .architecture import Architecture from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .logger import log from .logger import log
@@ -122,10 +121,10 @@ class BuildOptions:
return self.globals.architectures return self.globals.architectures
SettingLeaf = Union[str, int, bool] SettingLeaf = str | int | bool
SettingList = Sequence[SettingLeaf] SettingList = Sequence[SettingLeaf]
SettingTable = Mapping[str, Union[SettingLeaf, SettingList]] SettingTable = Mapping[str, SettingLeaf | SettingList]
SettingValue = Union[SettingTable, SettingList, SettingLeaf] SettingValue = SettingTable | SettingList | SettingLeaf
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
@@ -361,7 +360,7 @@ def _stringify_setting(
msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list" msg = f"Error converting {setting!r} to a string: this setting doesn't accept a list"
raise OptionsReaderError(msg) from None raise OptionsReaderError(msg) from None
if isinstance(setting, (bool, int)): if isinstance(setting, bool | int):
return str(setting) return str(setting)
return setting return setting
@@ -984,7 +983,7 @@ def compute_options(
return options return options
@functools.lru_cache(maxsize=None) @functools.cache
def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]: def _get_pinned_container_images() -> Mapping[str, Mapping[str, str]]:
""" """
This looks like a dict of dicts, e.g. This looks like a dict of dicts, e.g.
+1 -1
View File
@@ -15,7 +15,7 @@ __all__ = (
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
PopenBytes = subprocess.Popen[bytes] PopenBytes = subprocess.Popen[bytes]
PathOrStr = Union[str, os.PathLike[str]] PathOrStr = str | os.PathLike[str]
else: else:
PopenBytes = subprocess.Popen PopenBytes = subprocess.Popen
PathOrStr = Union[str, "os.PathLike[str]"] PathOrStr = Union[str, "os.PathLike[str]"]
+6 -20
View File
@@ -14,13 +14,14 @@ import sys
import tarfile import tarfile
import textwrap import textwrap
import time import time
import tomllib
import typing import typing
import urllib.request import urllib.request
from collections import defaultdict from collections import defaultdict
from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping, Sequence from collections.abc import Callable, Generator, Iterable, Mapping, MutableMapping, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from functools import lru_cache, total_ordering from functools import cache, total_ordering
from pathlib import Path, PurePath from pathlib import Path, PurePath
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
from time import sleep from time import sleep
@@ -37,7 +38,6 @@ from packaging.version import Version
from platformdirs import user_cache_path from platformdirs import user_cache_path
from . import errors from . import errors
from ._compat import tomllib
from .architecture import Architecture from .architecture import Architecture
from .errors import FatalError from .errors import FatalError
from .typing import PathOrStr, PlatformName from .typing import PathOrStr, PlatformName
@@ -46,7 +46,6 @@ __all__ = [
"MANYLINUX_ARCHS", "MANYLINUX_ARCHS",
"EnableGroups", "EnableGroups",
"call", "call",
"chdir",
"combine_constraints", "combine_constraints",
"find_compatible_wheel", "find_compatible_wheel",
"find_uv", "find_uv",
@@ -219,12 +218,12 @@ def format_safe(template: str, **kwargs: str | os.PathLike[str]) -> str:
def prepare_command(command: str, **kwargs: PathOrStr) -> 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 For example, used in the test_command option to specify the path to the
project's root. Unmatched syntax will mostly be allowed through. 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]: def get_build_verbosity_extra_flags(level: int) -> list[str]:
@@ -631,7 +630,7 @@ def get_pip_version(env: Mapping[str, str]) -> str:
return pip_version return pip_version
@lru_cache(maxsize=None) @cache
def ensure_node(major_version: str) -> Path: def ensure_node(major_version: str) -> Path:
input_file = resources_dir / "nodejs.toml" input_file = resources_dir / "nodejs.toml"
with input_file.open("rb") as f: with input_file.open("rb") as f:
@@ -663,7 +662,7 @@ def ensure_node(major_version: str) -> Path:
return path return path
@lru_cache(maxsize=None) @cache
def _ensure_virtualenv(version: str) -> Path: def _ensure_virtualenv(version: str) -> Path:
version_parts = version.split(".") version_parts = version.split(".")
key = f"py{version_parts[0]}{version_parts[1]}" key = f"py{version_parts[0]}{version_parts[1]}"
@@ -839,19 +838,6 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
return 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: def fix_ansi_codes_for_github_actions(text: str) -> str:
""" """
Github Actions forgets the current ANSI style on every new line. This Github Actions forgets the current ANSI style on every new line. This
+3 -3
View File
@@ -7,14 +7,14 @@ import subprocess
import textwrap import textwrap
from collections.abc import MutableMapping, Sequence, Set from collections.abc import MutableMapping, Sequence, Set
from dataclasses import dataclass from dataclasses import dataclass
from functools import lru_cache from functools import cache
from pathlib import Path from pathlib import Path
from typing import assert_never
from filelock import FileLock from filelock import FileLock
from packaging.version import Version from packaging.version import Version
from . import errors from . import errors
from ._compat.typing import assert_never
from .architecture import Architecture from .architecture import Architecture
from .environment import ParsedEnvironment from .environment import ParsedEnvironment
from .logger import log from .logger import log
@@ -96,7 +96,7 @@ def get_python_configurations(
return python_configurations return python_configurations
@lru_cache(maxsize=None) @cache
def _ensure_nuget() -> Path: def _ensure_nuget() -> Path:
nuget = CIBW_CACHE_PATH / "nuget.exe" nuget = CIBW_CACHE_PATH / "nuget.exe"
with FileLock(str(nuget) + ".lock"): with FileLock(str(nuget) + ".lock"):
+4 -15
View File
@@ -8,7 +8,7 @@ version = "2.22.0"
description = "Build Python wheels on CI with minimal configuration." description = "Build Python wheels on CI with minimal configuration."
readme = "README.md" readme = "README.md"
license = "BSD-2-Clause" license = "BSD-2-Clause"
requires-python = ">=3.8" requires-python = ">=3.11"
authors = [ authors = [
{ name = "Joe Rickerby", email = "joerick@mac.com" }, { name = "Joe Rickerby", email = "joerick@mac.com" },
] ]
@@ -30,9 +30,6 @@ classifiers = [
"Natural Language :: English", "Natural Language :: English",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only", "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.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
@@ -46,9 +43,7 @@ dependencies = [
"dependency-groups>=1.2", "dependency-groups>=1.2",
"filelock", "filelock",
"packaging>=20.9", "packaging>=20.9",
"platformdirs", "platformdirs"
"tomli;python_version < '3.11'",
"typing-extensions>=4.1.0;python_version < '3.11'",
] ]
[project.optional-dependencies] [project.optional-dependencies]
@@ -110,7 +105,7 @@ log_cli_level = "info"
[tool.mypy] [tool.mypy]
python_version = "3.8" python_version = "3.11"
files = [ files = [
"cibuildwheel/*.py", "cibuildwheel/*.py",
"test/**/*.py", "test/**/*.py",
@@ -142,7 +137,7 @@ ignore_missing_imports = true
[tool.pylint] [tool.pylint]
py-version = "3.8" py-version = "3.11"
jobs = "0" jobs = "0"
fail-on = ["E", "F"] fail-on = ["E", "F"]
fail-under = "9.8" fail-under = "9.8"
@@ -212,7 +207,6 @@ ignore = [
"PYI025", # Set as AbstractSet "PYI025", # Set as AbstractSet
"ISC001", # Conflicts with formatter "ISC001", # Conflicts with formatter
] ]
typing-modules = ["cibuildwheel._compat.typing"]
flake8-unused-arguments.ignore-variadic-names = true flake8-unused-arguments.ignore-variadic-names = true
[tool.ruff.lint.flake8-tidy-imports.banned-api] [tool.ruff.lint.flake8-tidy-imports.banned-api]
@@ -221,14 +215,9 @@ flake8-unused-arguments.ignore-variadic-names = true
"typing.Iterator".msg = "Use collections.abc.Iterator instead." "typing.Iterator".msg = "Use collections.abc.Iterator instead."
"typing.Sequence".msg = "Use collections.abc.Sequence instead." "typing.Sequence".msg = "Use collections.abc.Sequence instead."
"typing.Set".msg = "Use collections.abc.Set 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] [tool.ruff.lint.per-file-ignores]
"unit_test/*" = ["PLC1901"] "unit_test/*" = ["PLC1901"]
"cibuildwheel/_compat/**.py" = ["TID251"]
"bin/*" = ["TID251"] "bin/*" = ["TID251"]
[tool.repo-review] [tool.repo-review]
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import json import json
import subprocess import subprocess
from typing import Generator from collections.abc import Generator
import pytest import pytest
from filelock import FileLock from filelock import FileLock
+3 -3
View File
@@ -1,12 +1,12 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Union from typing import Any
import jinja2 import jinja2
FilesDict = Dict[str, Union[str, jinja2.Template]] FilesDict = dict[str, str | jinja2.Template]
TemplateContext = Dict[str, Any] TemplateContext = dict[str, Any]
class TestProject: class TestProject:
+2 -1
View File
@@ -1,8 +1,9 @@
from __future__ import annotations from __future__ import annotations
import tomllib
from packaging.version import Version from packaging.version import Version
from cibuildwheel._compat import tomllib
from cibuildwheel.extra import Printable, dump_python_configurations from cibuildwheel.extra import Printable, dump_python_configurations
from cibuildwheel.util import resources_dir from cibuildwheel.util import resources_dir
+4 -5
View File
@@ -1,7 +1,6 @@
import contextlib import contextlib
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Dict
import pytest import pytest
import setuptools._distutils.util import setuptools._distutils.util
@@ -16,7 +15,7 @@ if not sys.platform.startswith("win"):
@contextlib.contextmanager @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: with monkeypatch.context() as mp:
for envvar, val in environment.items(): for envvar, val in environment.items():
mp.setenv(name=envvar, value=val) 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): def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
arch = "32" arch = "32"
environment: Dict[str, str] = {} environment: dict[str, str] = {}
configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) 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): def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
arch = "64" arch = "64"
environment: Dict[str, str] = {} environment: dict[str, str] = {}
configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) 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): def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
arch = "ARM64" arch = "ARM64"
environment: Dict[str, str] = {} environment: dict[str, str] = {}
configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None)
+1 -1
View File
@@ -1,13 +1,13 @@
from __future__ import annotations from __future__ import annotations
import sys import sys
import tomllib
from fnmatch import fnmatch from fnmatch import fnmatch
from pathlib import Path from pathlib import Path
import pytest import pytest
from cibuildwheel.__main__ import main from cibuildwheel.__main__ import main
from cibuildwheel._compat import tomllib
from cibuildwheel.environment import ParsedEnvironment from cibuildwheel.environment import ParsedEnvironment
from cibuildwheel.options import BuildOptions, _get_pinned_container_images from cibuildwheel.options import BuildOptions, _get_pinned_container_images
from cibuildwheel.util import BuildSelector, resources_dir, split_config_settings from cibuildwheel.util import BuildSelector, resources_dir, split_config_settings
+1 -1
View File
@@ -1,10 +1,10 @@
from __future__ import annotations from __future__ import annotations
import tomllib
from textwrap import dedent from textwrap import dedent
import pytest import pytest
from cibuildwheel._compat import tomllib
from cibuildwheel.projectfiles import ( from cibuildwheel.projectfiles import (
get_requires_python_str, get_requires_python_str,
resolve_dependency_groups, resolve_dependency_groups,
+1 -2
View File
@@ -1,13 +1,12 @@
from __future__ import annotations from __future__ import annotations
import re import re
import tomllib
from pathlib import Path from pathlib import Path
import pytest import pytest
import validate_pyproject.api import validate_pyproject.api
from cibuildwheel._compat import tomllib
DIR = Path(__file__).parent.resolve() DIR = Path(__file__).parent.resolve()