diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bb2ebe8a..c261259a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,6 +27,9 @@ jobs:
needs: [dist]
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
+ environment: pypi
+ permissions:
+ id-token: write
steps:
- uses: actions/download-artifact@v3
@@ -34,7 +37,4 @@ jobs:
name: artifact
path: dist
- - uses: pypa/gh-action-pypi-publish@v1.6.4
- with:
- user: __token__
- password: ${{ secrets.pypi_password }}
+ - uses: pypa/gh-action-pypi-publish@release/v1
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 36ed2511..aa07602e 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -39,6 +39,9 @@ jobs:
matrix:
os: [ubuntu-20.04, windows-latest, macos-11]
python_version: ['3.11']
+ include:
+ - os: ubuntu-22.04
+ python_version: '3.7'
timeout-minutes: 180
steps:
- uses: actions/checkout@v3
diff --git a/.github/workflows/update-dependencies.yml b/.github/workflows/update-dependencies.yml
index 414c460a..7ae7b9b9 100644
--- a/.github/workflows/update-dependencies.yml
+++ b/.github/workflows/update-dependencies.yml
@@ -22,9 +22,9 @@ jobs:
steps:
- uses: actions/checkout@v3
- - uses: wntrblm/nox@2022.11.21
+ - uses: wntrblm/nox@2023.04.22
with:
- python-versions: "3.6, 3.7, 3.8, 3.9, 3.10, 3.11"
+ python-versions: "3.7, 3.8, 3.9, 3.10, 3.11, 3.12-dev"
- name: "Run update: dependencies"
run: nox --force-color -s update_constraints
@@ -44,7 +44,7 @@ jobs:
- name: Create Pull Request
if: github.ref == 'refs/heads/main' && github.repository == 'pypa/cibuildwheel'
- uses: peter-evans/create-pull-request@v4
+ uses: peter-evans/create-pull-request@v5
with:
commit-message: Update dependencies
title: '[Bot] Update dependencies'
diff --git a/.github/workflows/update-major-minor-tag.yml b/.github/workflows/update-major-minor-tag.yml
new file mode 100644
index 00000000..d25466f0
--- /dev/null
+++ b/.github/workflows/update-major-minor-tag.yml
@@ -0,0 +1,30 @@
+name: Update the vX.Y tag
+
+on:
+ release:
+ types: [released]
+ workflow_dispatch:
+ inputs:
+ TAG_NAME:
+ description: 'Tag name that the major.minor tag will point to'
+ required: true
+
+env:
+ TAG_NAME: ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }}
+
+jobs:
+ update_tag:
+ name: Update the major.minor tag to include the ${{ github.event.inputs.TAG_NAME || github.event.release.tag_name }} changes
+ environment:
+ name: releaseNewActionVersion
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Update the ${{ env.TAG_NAME }} tag
+ id: update-major-minor-tag
+ uses: joerick/update-vX.Y-tag-action@v1.0
+ with:
+ source-tag: ${{ env.TAG_NAME }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c2acbdf4..7bbc80ec 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,24 +14,24 @@ repos:
- id: trailing-whitespace
- repo: https://github.com/psf/black
- rev: 23.1.0
+ rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: v0.0.243
+ rev: v0.0.272
hooks:
- id: ruff
- args: ["--fix"]
+ args: ["--fix", "--show-fixes"]
- repo: https://github.com/asottile/setup-cfg-fmt
- rev: v2.2.0
+ rev: v2.3.0
hooks:
- id: setup-cfg-fmt
args: [--include-version-classifiers, --max-py-version=3.11]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.0.0
+ rev: v1.3.0
hooks:
- id: mypy
name: mypy 3.7 on cibuildwheel/
@@ -46,20 +46,19 @@ repos:
- tomli_w
- types-certifi
- types-click
- - types-dataclasses
- types-jinja2
- types-pyyaml
- types-requests
- bracex
- - dataclasses
+ - markdown-it-py<3
- id: mypy
- name: mypy 3.10
+ name: mypy 3.11
exclude: ^cibuildwheel/resources/.*py$
- args: ["--python-version=3.10"]
+ args: ["--python-version=3.11"]
additional_dependencies: *mypy-dependencies
- repo: https://github.com/shellcheck-py/shellcheck-py
- rev: v0.9.0.2
+ rev: v0.9.0.5
hooks:
- id: shellcheck
@@ -79,7 +78,7 @@ repos:
files: ^docs/changelog.md$
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.2
+ rev: v2.2.5
hooks:
- id: codespell
args: ["-L", "sur"]
diff --git a/.travis.yml b/.travis.yml
index 4256a69b..cf45e94e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -37,8 +37,6 @@ jobs:
language: shell
before_install:
- choco upgrade python3 -y --version 3.7.9 --limit-output
- # Update root certificates to fix SSL error; see http://www.chawn.com/RootCerts.htm
- - powershell "md C:\temp\certs; CertUtil -generateSSTFromWU C:\temp\certs\RootStore.sst; Get-ChildItem -Path C:\\temp\certs\Rootstore.sst | Import-Certificate -CertStoreLocation Cert:\\LocalMachine\\Root\\ | out-null"
env:
- PYTHON=C:\\Python37\\python
diff --git a/CI.md b/CI.md
index 1fa1c510..98d36c73 100644
--- a/CI.md
+++ b/CI.md
@@ -1,11 +1,11 @@
This is a summary of the Python versions and platforms covered by the different CI platforms:
-| | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 |
-|---------|-----------------------|--------------------------|----------|-----------|----------------|
-| Linux | AppVeyor¹ / Travis CI | Azure Pipelines / GitLab | CircleCI | Cirrus CI | GitHub Actions |
-| macOS | AppVeyor¹ / Travis CI | Azure Pipelines | CircleCI | Cirrus CI | GitHub Actions |
-| Windows | AppVeyor¹ / Travis CI | Azure Pipelines | | Cirrus CI | GitHub Actions |
+| | 3.7 | 3.8 | 3.9 | 3.10 | 3.11 |
+|---------|-----------|---------------------------------------|-----------|-----------|----------------|
+| Linux | Travis CI | AppVeyor¹ / Azure Pipelines / GitLab¹ | CircleCI¹ | Cirrus CI | GitHub Actions |
+| macOS | Travis CI | AppVeyor¹ / Azure Pipelines | CircleCI¹ | Cirrus CI | GitHub Actions |
+| Windows | Travis CI | AppVeyor¹ / Azure Pipelines / GitLab¹ | | Cirrus CI | GitHub Actions |
-> ¹ AppVeyor only runs the "basic" test to reduce load.
+> ¹ Runs a reduced set of tests to reduce CI load
Non-x86 architectures are covered on Travis CI using Python 3.7.
diff --git a/README.md b/README.md
index c46327c0..5c28cc18 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ What does it do?
| CPython 3.9 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅³ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.10 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| CPython 3.11 | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
+| CPython 3.12⁵ | ✅ | ✅ | ✅ | ✅ | ✅² | ✅ | ✅ | ✅ | ✅ | ✅ |
| PyPy 3.7 v7.3 | ✅ | N/A | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
| PyPy 3.8 v7.3 | ✅ | ✅⁴ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
| PyPy 3.9 v7.3 | ✅ | ✅⁴ | ✅ | N/A | N/A | ✅¹ | ✅¹ | ✅¹ | N/A | N/A |
@@ -39,6 +40,7 @@ What does it do?
² Windows arm64 support is experimental.
³ Alpine 3.14 and very briefly 3.15's default python3 [was not able to load](https://github.com/pypa/cibuildwheel/issues/934) musllinux wheels. This has been fixed; please upgrade the python package if using Alpine from before the fix.
⁴ Cross-compilation not supported with PyPy - to build these wheels you need to run cibuildwheel on an Apple Silicon machine.
+⁵ CPython 3.12 is available using the [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons) option.
- Builds manylinux, musllinux, macOS 10.9+, and Windows wheels for CPython and PyPy
- Works on GitHub Actions, Azure Pipelines, Travis CI, AppVeyor, CircleCI, GitLab CI, and Cirrus CI
@@ -94,7 +96,7 @@ jobs:
- uses: actions/setup-python@v3
- name: Install cibuildwheel
- run: python -m pip install cibuildwheel==2.12.0
+ run: python -m pip install cibuildwheel==2.13.1
- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
@@ -158,26 +160,26 @@ Here are some repos that use cibuildwheel.
| Name | CI | OS | Notes |
|-----------------------------------|----|----|:------|
| [scikit-learn][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The machine learning library. A complex but clean config using many of cibuildwheel's features to build a large project with Cython and C++ extensions. |
+| [pytorch-fairseq][] | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
| [NumPy][] | ![github icon][] ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The fundamental package for scientific computing with Python. |
| [Tornado][] | ![travisci icon][] | ![apple icon][] ![linux icon][] | Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed. |
-| [pytorch-fairseq][] | ![github icon][] | ![apple icon][] ![linux icon][] | Facebook AI Research Sequence-to-Sequence Toolkit written in Python. |
| [Matplotlib][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The venerable Matplotlib, a Python library with C++ portions |
| [NCNN][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | ncnn is a high-performance neural network inference framework optimized for the mobile platform |
-| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
| [Prophet][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Tool for producing high quality forecasts for time series data that has multiple seasonality with linear or non-linear growth. |
+| [Kivy][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Open source UI framework written in Python, running on Windows, Linux, macOS, Android and iOS |
| [MyPy][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | The compiled version of MyPy using MyPyC. |
-| [pydantic][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Data parsing and validation using Python type hints |
+| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
[scikit-learn]: https://github.com/scikit-learn/scikit-learn
+[pytorch-fairseq]: https://github.com/pytorch/fairseq
[NumPy]: https://github.com/numpy/numpy
[Tornado]: https://github.com/tornadoweb/tornado
-[pytorch-fairseq]: https://github.com/pytorch/fairseq
[Matplotlib]: https://github.com/matplotlib/matplotlib
[NCNN]: https://github.com/Tencent/ncnn
-[Kivy]: https://github.com/kivy/kivy
[Prophet]: https://github.com/facebook/prophet
+[Kivy]: https://github.com/kivy/kivy
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
-[pydantic]: https://github.com/samuelcolvin/pydantic
+[MemRay]: https://github.com/bloomberg/memray
[appveyor icon]: docs/data/readme_icons/appveyor.svg
[github icon]: docs/data/readme_icons/github.svg
@@ -210,47 +212,42 @@ Changelog
-### v2.12.0
+### v2.13.1
-_16 Jan 2023_
+_10 June 2023_
-- ✨ Adds support for PyPy arm64 wheels. This means that you can build PyPy wheels for Apple Silicon machines. Cross-compilation is not supported for these wheels, so you'll have to build on an Apple Silicon machine. (#1372)
-- 🛠 Pinned version updates, including PyPy to v7.3.11 and setuptools to 66.0.0.
+- 🛠 Updates the prerelease CPython 3.12 version to 3.12.0b2. (#1516)
+- 🛠 Adds a moving `v.` tag for use in GitHub Actions workflow files. If you use this, you'll get the latest patch release within a minor version. Additionally, Dependabot won't send you PRs for patch releases. (#1517)
-### v2.11.4
+### v2.13.0
-_24 Dec 2022_
+_28 May 2023_
-- 🐛 Fix a bug that caused missing wheels on Windows when a test was skipped using CIBW_TEST_SKIP (#1377)
-- 🛠 Updates CPython 3.11 to 3.11.1 (#1371)
-- 🛠 Updates PyPy to 7.3.10, except on macOS which remains on 7.3.9 due to a bug on that platform. (#1371)
-- 📚 Added a reference to abi3audit to the docs (#1347)
+- ✨ Adds CPython 3.12 support, under the prerelease flag [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons). This version of cibuildwheel uses 3.12.0b1.
-### v2.11.3
+ While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.12 will be available in cibuildwheel without the flag. (#1507)
-_5 Dec 2022_
+- ✨ Adds the ability to pass arguments to the container engine when the container is created, using the [CIBW_CONTAINER_ENGINE](https://cibuildwheel.readthedocs.io/en/stable/options/#container-engine) option. (#1499)
-- ✨ Improves the 'build options' log output that's printed at the start of each run (#1352)
-- ✨ Added a friendly error message to a common misconfiguration of the `CIBW_TEST_COMMAND` option - not specifying path using the `{project}` placeholder (#1336)
-- 🛠 The GitHub Action now uses Powershell on Windows to avoid occasional incompabilities with bash (#1346)
+### v2.12.3
-### v2.11.2
+_19 April 2023_
-_26 October 2022_
+- 🐛 Fix an import error when running on Python 3.7. (#1479)
-- 🛠 Updates CPython 3.11 to 3.11.0 - final release (#1327)
-- 🛠 Simplify the default macOS repair command (#1322)
-- 🛠 Fix the default `MACOSX_DEPLOYMENT_TARGET` on arm64 (#1312)
-- 🛠 Hide irrelevant pip warnings on linux (#1311)
-- 🐛 Fix a bug that caused the stdout and stderr of commands in containers to be in the wrong order Previously, stdout could appear after stderr. (#1324)
-- 📚 Added [a FAQ entry](https://cibuildwheel.readthedocs.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64) describing how to perform native builds of CPython 3.8 wheels on Apple Silicon. (#1323)
-- 📚 Other docs improvements
+### v2.12.2
-### v2.11.1
+_18 April 2023_
-_13 October 2022_
+- 🐛 Fix a bug that caused an extra empty config-setting to be passed to the backend when CIBW_BUILD_FRONTEND is set to `build`. (#1474)
+- 🐛 Fix a crash that occurred when overwriting an existing wheel on Windows. (#1464)
+- 🛠 Pinned version updates, including CPython 3.10.11, 3.11.3, pip 23.1 and wheel 0.40.0.
-- 🛠 Updates to the latest manylinux images, and updates CPython 3.10 to 3.10.8.
+### v2.12.1
+
+_11 March 2023_
+
+- 🐛 Fix a bug that prevented the use of CIBW_CONFIG_SETTINGS with the 'pip' build backend. (#1430)
diff --git a/appveyor.yml b/appveyor.yml
index a832cc7c..4f12790d 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,18 +1,18 @@
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu
- APPVEYOR_JOB_NAME: "python37-x64-ubuntu"
+ APPVEYOR_JOB_NAME: "python38-x64-ubuntu"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- APPVEYOR_JOB_NAME: "python37-x64-vs2015"
+ APPVEYOR_JOB_NAME: "python38-x64-vs2015"
- APPVEYOR_BUILD_WORKER_IMAGE: macos
- APPVEYOR_JOB_NAME: "python37-x64-macos"
+ APPVEYOR_JOB_NAME: "python38-x64-macos"
-stack: python 3.7
+stack: python 3.8
build: off
init:
-- cmd: set PATH=C:\Python37;C:\Python37\Scripts;%PATH%
+- cmd: set PATH=C:\Python38;C:\Python38\Scripts;%PATH%
- ps: |
$BRANCH = if ($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH) { $env:APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH } else { $env:APPVEYOR_REPO_BRANCH }
if (-not ($BRANCH -eq 'main' -or $BRANCH.ToLower().StartsWith('appveyor-'))) {
diff --git a/bin/inspect_all_known_projects.py b/bin/inspect_all_known_projects.py
index 3f91b2b5..a2c68a6b 100755
--- a/bin/inspect_all_known_projects.py
+++ b/bin/inspect_all_known_projects.py
@@ -15,8 +15,8 @@ the results without the `--online` setting.
from __future__ import annotations
import ast
+from collections.abc import Iterable, Iterator
from pathlib import Path
-from typing import Iterator
import click
import yaml
@@ -97,7 +97,7 @@ class MaybeRemote:
with open(filename, "w") as f:
yaml.safe_dump(self.contents, f, default_flow_style=False)
- def on_each(self, repos: list[str]) -> Iterator[tuple[str, str, str | None]]:
+ def on_each(self, repos: Iterable[str]) -> Iterator[tuple[str, str, str | None]]:
for repo in repos:
print(f"[bold]{repo}:")
for filename in sorted(self.contents, reverse=True):
diff --git a/bin/projects.py b/bin/projects.py
old mode 100644
new mode 100755
index 6b577189..546a10c8
--- a/bin/projects.py
+++ b/bin/projects.py
@@ -16,6 +16,7 @@ import functools
import textwrap
import urllib.request
import xml.dom.minidom
+from collections.abc import Iterable, Mapping, Sequence
from datetime import datetime
from io import StringIO
from pathlib import Path
@@ -42,7 +43,7 @@ ICONS = (
class Project:
NAME: int = 0
- def __init__(self, config: dict[str, Any], github: Github | None = None):
+ def __init__(self, config: Mapping[str, Any], github: Github | None = None):
try:
self.name: str = config["name"]
self.gh: str = config["gh"]
@@ -149,7 +150,7 @@ def path_for_icon(icon_name: str, relative_to: Path | None = None) -> Path:
def get_projects(
- config: list[dict[str, Any]],
+ config: Iterable[Mapping[str, Any]],
*,
online: bool = True,
auth: str | None = None,
@@ -163,7 +164,7 @@ def get_projects(
return sorted((Project(item, github) for item in config), reverse=online)
-def render_projects(projects: list[Project], *, dest_path: Path, include_info: bool = True):
+def render_projects(projects: Sequence[Project], *, dest_path: Path, include_info: bool = True):
io = StringIO()
print = functools.partial(builtins.print, file=io)
@@ -191,7 +192,7 @@ def render_projects(projects: list[Project], *, dest_path: Path, include_info: b
def insert_projects_table(
file: Path,
*,
- projects: list[Project],
+ projects: Sequence[Project],
input_filename: str,
include_info: bool = True,
):
diff --git a/bin/update_how_it_works_image.py b/bin/update_how_it_works_image.py
old mode 100644
new mode 100755
diff --git a/bin/update_pythons.py b/bin/update_pythons.py
index 930b13df..e57a7173 100755
--- a/bin/update_pythons.py
+++ b/bin/update_pythons.py
@@ -5,26 +5,21 @@ from __future__ import annotations
import copy
import difflib
import logging
-import sys
+from collections.abc import Mapping, MutableMapping
from pathlib import Path
from typing import Any, Union
import click
import requests
import rich
-
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
-
from packaging.specifiers import Specifier
from packaging.version import Version
from rich.logging import RichHandler
from rich.syntax import Syntax
+from cibuildwheel._compat import tomllib
+from cibuildwheel._compat.typing import Final, Literal, TypedDict
from cibuildwheel.extra import dump_python_configurations
-from cibuildwheel.typing import Final, Literal, TypedDict
log = logging.getLogger("cibw")
@@ -93,7 +88,7 @@ class WindowsVersions:
unsorted_versions = spec.filter(self.version_dict)
versions = sorted(unsorted_versions, reverse=True)
- log.debug(f"Windows {self.arch} {spec} has {', '.join(str(v) for v in versions)}")
+ log.debug("Windows %s %s has %s", self.arch, spec, ", ".join(str(v) for v in versions))
if not versions:
return None
@@ -124,7 +119,7 @@ class PyPyVersions:
]
self.arch = arch_str
- def get_arch_file(self, release: dict[str, Any]) -> str:
+ def get_arch_file(self, release: Mapping[str, Any]) -> str:
urls: list[str] = [
rf["download_url"]
for rf in release["files"]
@@ -250,11 +245,11 @@ class AllVersions:
self.macos_pypy = PyPyVersions("64")
self.macos_pypy_arm64 = PyPyVersions("ARM64")
- def update_config(self, config: dict[str, str]) -> None:
+ def update_config(self, config: MutableMapping[str, str]) -> None:
identifier = config["identifier"]
version = Version(config["version"])
spec = Specifier(f"=={version.major}.{version.minor}.*")
- log.info(f"Reading in '{identifier}' -> {spec} @ {version}")
+ log.info("Reading in %r -> %s @ %s", str(identifier), spec, version)
orig_config = copy.copy(config)
config_update: AnyConfig | None = None
@@ -282,7 +277,7 @@ class AllVersions:
config.update(**config_update)
if config != orig_config:
- log.info(f" Updated {orig_config} to {config}")
+ log.info(" Updated %s to %s", orig_config, config)
@click.command()
diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py
index ef1fec80..c4c70f29 100755
--- a/bin/update_virtualenv.py
+++ b/bin/update_virtualenv.py
@@ -5,23 +5,17 @@ from __future__ import annotations
import difflib
import logging
import subprocess
-import sys
from dataclasses import dataclass
from pathlib import Path
import click
import rich
-
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
-
from packaging.version import InvalidVersion, Version
from rich.logging import RichHandler
from rich.syntax import Syntax
-from cibuildwheel.typing import Final
+from cibuildwheel._compat import tomllib
+from cibuildwheel._compat.typing import Final
log = logging.getLogger("cibw")
@@ -54,14 +48,18 @@ def git_ls_remote_versions(url) -> list[VersionTuple]:
try:
version = Version(version_string)
if version.is_devrelease:
- log.info(f"Ignoring development release '{version}'")
+ log.info("Ignoring development release %r", str(version))
continue
if version.is_prerelease:
- log.info(f"Ignoring pre-release '{version}'")
+ log.info("Ignoring pre-release %r", str(version))
+ continue
+ # Do not upgrade past 20.22.0 to keep python 3.6 compat
+ if version >= Version("20.22.0"):
+ log.info("Ignoring %r which is not compatible with python 3.6", str(version))
continue
versions.append(VersionTuple(version, version_string))
except InvalidVersion:
- log.warning(f"Ignoring ref '{ref}'")
+ log.warning("Ignoring ref %r", ref)
versions.sort(reverse=True)
return versions
diff --git a/cibuildwheel/__init__.py b/cibuildwheel/__init__.py
index 4a09ed48..33607129 100644
--- a/cibuildwheel/__init__.py
+++ b/cibuildwheel/__init__.py
@@ -1,3 +1,3 @@
from __future__ import annotations
-__version__ = "2.12.0"
+__version__ = "2.13.1"
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index 1ff447aa..0575f4e1 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -7,6 +7,7 @@ import sys
import tarfile
import textwrap
import typing
+from collections.abc import Iterable, Sequence, Set
from pathlib import Path
from tempfile import mkdtemp
@@ -15,10 +16,11 @@ import cibuildwheel.linux
import cibuildwheel.macos
import cibuildwheel.util
import cibuildwheel.windows
+from cibuildwheel._compat.typing import Protocol, assert_never
from cibuildwheel.architecture import Architecture, allowed_architectures_check
from cibuildwheel.logger import log
from cibuildwheel.options import CommandLineArguments, Options, compute_options
-from cibuildwheel.typing import PLATFORMS, PlatformName, assert_never
+from cibuildwheel.typing import PLATFORMS, GenericPythonConfiguration, PlatformName
from cibuildwheel.util import (
CIBW_CACHE_PATH,
BuildSelector,
@@ -167,70 +169,102 @@ def main() -> None:
log.warning(f"Can't delete temporary folder '{temp_dir}'")
-def build_in_directory(args: CommandLineArguments) -> None:
+def _compute_platform_only(only: str) -> PlatformName:
+ if "linux_" in only:
+ return "linux"
+ if "macosx_" in only:
+ return "macos"
+ if "win_" in only or "win32" in only:
+ return "windows"
+ print(
+ f"Invalid --only='{only}', must be a build selector with a known platform",
+ file=sys.stderr,
+ )
+ sys.exit(2)
+
+
+def _compute_platform_ci() -> PlatformName:
+ if detect_ci_provider() is None:
+ print(
+ textwrap.dedent(
+ """
+ cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
+ Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, Gitlab, and Cirrus CI
+ are supported. You can run on your development machine or other CI providers
+ using the --platform argument. Check --help output for more information.
+ """
+ ),
+ file=sys.stderr,
+ )
+ sys.exit(2)
+ if sys.platform.startswith("linux"):
+ return "linux"
+ elif sys.platform == "darwin":
+ return "macos"
+ elif sys.platform == "win32":
+ return "windows"
+ else:
+ print(
+ 'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
+ "cibuildwheel using the --platform argument. Check --help output for more information.",
+ file=sys.stderr,
+ )
+ sys.exit(2)
+
+
+def _compute_platform(args: CommandLineArguments) -> PlatformName:
platform_option_value = args.platform or os.environ.get("CIBW_PLATFORM", "auto")
- platform: PlatformName
+
+ if args.only and args.platform is not None:
+ print(
+ "--platform cannot be specified with --only, it is computed from --only",
+ file=sys.stderr,
+ )
+ sys.exit(2)
+ if args.only and args.archs is not None:
+ print(
+ "--arch cannot be specified with --only, it is computed from --only",
+ file=sys.stderr,
+ )
+ sys.exit(2)
+
+ if platform_option_value not in PLATFORMS | {"auto"}:
+ print(f"cibuildwheel: Unsupported platform: {platform_option_value}", file=sys.stderr)
+ sys.exit(2)
if args.only:
- if "linux_" in args.only:
- platform = "linux"
- elif "macosx_" in args.only:
- platform = "macos"
- elif "win_" in args.only or "win32" in args.only:
- platform = "windows"
- else:
- print(
- f"Invalid --only='{args.only}', must be a build selector with a known platform",
- file=sys.stderr,
- )
- sys.exit(2)
- if args.platform is not None:
- print(
- "--platform cannot be specified with --only, it is computed from --only",
- file=sys.stderr,
- )
- sys.exit(2)
- if args.archs is not None:
- print(
- "--arch cannot be specified with --only, it is computed from --only",
- file=sys.stderr,
- )
- sys.exit(2)
+ return _compute_platform_only(args.only)
elif platform_option_value != "auto":
- if platform_option_value not in PLATFORMS:
- print(f"cibuildwheel: Unsupported platform: {platform_option_value}", file=sys.stderr)
- sys.exit(2)
+ return typing.cast(PlatformName, platform_option_value)
- platform = typing.cast(PlatformName, platform_option_value)
- else:
- ci_provider = detect_ci_provider()
- if ci_provider is None:
- print(
- textwrap.dedent(
- """
- cibuildwheel: Unable to detect platform. cibuildwheel should run on your CI server;
- Travis CI, AppVeyor, Azure Pipelines, GitHub Actions, CircleCI, Gitlab, and Cirrus CI
- are supported. You can run on your development machine or other CI providers
- using the --platform argument. Check --help output for more information.
- """
- ),
- file=sys.stderr,
- )
- sys.exit(2)
- if sys.platform.startswith("linux"):
- platform = "linux"
- elif sys.platform == "darwin":
- platform = "macos"
- elif sys.platform == "win32":
- platform = "windows"
- else:
- print(
- 'cibuildwheel: Unable to detect platform from "sys.platform" in a CI environment. You can run '
- "cibuildwheel using the --platform argument. Check --help output for more information.",
- file=sys.stderr,
- )
- sys.exit(2)
+ return _compute_platform_ci()
+
+class PlatformModule(Protocol):
+ # note that as per PEP544, the self argument is ignored when the protocol
+ # is applied to a module
+ def get_python_configurations(
+ self, build_selector: BuildSelector, architectures: Set[Architecture]
+ ) -> Sequence[GenericPythonConfiguration]:
+ ...
+
+ def build(self, options: Options, tmp_path: Path) -> None:
+ ...
+
+
+# pylint: disable-next=inconsistent-return-statements
+def get_platform_module(platform: PlatformName) -> PlatformModule:
+ if platform == "linux":
+ return cibuildwheel.linux
+ if platform == "windows":
+ return cibuildwheel.windows
+ if platform == "macos":
+ return cibuildwheel.macos
+ assert_never(platform)
+
+
+def build_in_directory(args: CommandLineArguments) -> None:
+ platform: PlatformName = _compute_platform(args)
options = compute_options(platform=platform, command_line_arguments=args, env=os.environ)
package_dir = options.globals.package_dir
@@ -242,8 +276,9 @@ def build_in_directory(args: CommandLineArguments) -> None:
print(msg, file=sys.stderr)
sys.exit(2)
+ platform_module = get_platform_module(platform)
identifiers = get_build_identifiers(
- platform=platform,
+ platform_module=platform_module,
build_selector=options.globals.build_selector,
architectures=options.globals.architectures,
)
@@ -289,14 +324,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
with cibuildwheel.util.print_new_wheels(
"\n{n} wheels produced in {m:.0f} minutes:", output_dir
):
- if platform == "linux":
- cibuildwheel.linux.build(options, tmp_path)
- elif platform == "windows":
- cibuildwheel.windows.build(options, tmp_path)
- elif platform == "macos":
- cibuildwheel.macos.build(options, tmp_path)
- else:
- assert_never(platform)
+ platform_module.build(options, tmp_path)
finally:
# avoid https://github.com/python/cpython/issues/86962 by performing
# cleanup manually
@@ -305,7 +333,7 @@ def build_in_directory(args: CommandLineArguments) -> None:
log.warning(f"Can't delete temporary folder '{tmp_path}'")
-def print_preamble(platform: str, options: Options, identifiers: list[str]) -> None:
+def print_preamble(platform: str, options: Options, identifiers: Sequence[str]) -> None:
print(
textwrap.dedent(
"""
@@ -339,33 +367,13 @@ def print_preamble(platform: str, options: Options, identifiers: list[str]) -> N
def get_build_identifiers(
- platform: PlatformName, build_selector: BuildSelector, architectures: set[Architecture]
+ platform_module: PlatformModule, build_selector: BuildSelector, architectures: Set[Architecture]
) -> list[str]:
- python_configurations: (
- list[cibuildwheel.linux.PythonConfiguration]
- | list[cibuildwheel.windows.PythonConfiguration]
- | list[cibuildwheel.macos.PythonConfiguration]
- )
-
- if platform == "linux":
- python_configurations = cibuildwheel.linux.get_python_configurations(
- build_selector, architectures
- )
- elif platform == "windows":
- python_configurations = cibuildwheel.windows.get_python_configurations(
- build_selector, architectures
- )
- elif platform == "macos":
- python_configurations = cibuildwheel.macos.get_python_configurations(
- build_selector, architectures
- )
- else:
- assert_never(platform)
-
+ python_configurations = platform_module.get_python_configurations(build_selector, architectures)
return [config.identifier for config in python_configurations]
-def detect_warnings(*, options: Options, identifiers: list[str]) -> list[str]:
+def detect_warnings(*, options: Options, identifiers: Iterable[str]) -> list[str]:
warnings = []
# warn about deprecated {python} and {pip}
diff --git a/cibuildwheel/_compat/__init__.py b/cibuildwheel/_compat/__init__.py
new file mode 100644
index 00000000..9d48db4f
--- /dev/null
+++ b/cibuildwheel/_compat/__init__.py
@@ -0,0 +1 @@
+from __future__ import annotations
diff --git a/cibuildwheel/functools_cached_property_38.py b/cibuildwheel/_compat/_functools_cached_property_38.py
similarity index 94%
rename from cibuildwheel/functools_cached_property_38.py
rename to cibuildwheel/_compat/_functools_cached_property_38.py
index 879cdb62..134564b5 100644
--- a/cibuildwheel/functools_cached_property_38.py
+++ b/cibuildwheel/_compat/_functools_cached_property_38.py
@@ -1,7 +1,9 @@
from __future__ import annotations
+import typing
+from collections.abc import Callable
from threading import RLock
-from typing import Any, Callable, Generic, TypeVar, overload
+from typing import Any, Generic, TypeVar
__all__ = ["cached_property"]
@@ -24,11 +26,11 @@ class cached_property(Generic[_T]):
msg = f"Cannot assign the same cached_property to two different names ({self.attrname!r} and {name!r})."
raise TypeError(msg)
- @overload
+ @typing.overload
def __get__(self, instance: None, owner: type[Any] | None = ...) -> cached_property[_T]:
...
- @overload
+ @typing.overload
def __get__(self, instance: object, owner: type[Any] | None = ...) -> _T:
...
diff --git a/cibuildwheel/_compat/functools.py b/cibuildwheel/_compat/functools.py
new file mode 100644
index 00000000..8fe86a3e
--- /dev/null
+++ b/cibuildwheel/_compat/functools.py
@@ -0,0 +1,10 @@
+from __future__ import annotations
+
+import sys
+
+if sys.version_info >= (3, 8):
+ from functools import cached_property
+else:
+ from ._functools_cached_property_38 import cached_property
+
+__all__ = ("cached_property",)
diff --git a/cibuildwheel/_compat/tomllib.py b/cibuildwheel/_compat/tomllib.py
new file mode 100644
index 00000000..f3b30fe6
--- /dev/null
+++ b/cibuildwheel/_compat/tomllib.py
@@ -0,0 +1,10 @@
+from __future__ import annotations
+
+import sys
+
+if sys.version_info >= (3, 11):
+ from tomllib import load # noqa: TID251
+else:
+ from tomli import load # noqa: TID251
+
+__all__ = ("load",)
diff --git a/cibuildwheel/_compat/typing.py b/cibuildwheel/_compat/typing.py
new file mode 100644
index 00000000..62495adf
--- /dev/null
+++ b/cibuildwheel/_compat/typing.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+import sys
+
+if sys.version_info < (3, 8):
+ from typing_extensions import Final, Literal, OrderedDict, Protocol, TypedDict
+else:
+ from typing import Final, Literal, OrderedDict, Protocol, TypedDict # noqa: TID251
+
+if sys.version_info < (3, 11):
+ from typing_extensions import NotRequired, assert_never
+else:
+ from typing import NotRequired, assert_never # noqa: TID251
+
+__all__ = (
+ "Final",
+ "Literal",
+ "Protocol",
+ "Protocol",
+ "TypedDict",
+ "OrderedDict",
+ "assert_never",
+ "NotRequired",
+)
diff --git a/cibuildwheel/architecture.py b/cibuildwheel/architecture.py
index e1c8482a..a891ca2a 100644
--- a/cibuildwheel/architecture.py
+++ b/cibuildwheel/architecture.py
@@ -4,11 +4,17 @@ import functools
import platform as platform_module
import re
import sys
+from collections.abc import Set
from enum import Enum
-from .typing import Final, Literal, PlatformName, assert_never
+from ._compat.typing import Final, Literal, assert_never
+from .typing import PlatformName
-PRETTY_NAMES: Final = {"linux": "Linux", "macos": "macOS", "windows": "Windows"}
+PRETTY_NAMES: Final[dict[PlatformName, str]] = {
+ "linux": "Linux",
+ "macos": "macOS",
+ "windows": "Windows",
+}
ARCH_SYNONYMS: Final[list[dict[PlatformName, str | None]]] = [
{"linux": "x86_64", "macos": "x86_64", "windows": "AMD64"},
@@ -132,7 +138,7 @@ class Architecture(Enum):
def allowed_architectures_check(
platform: PlatformName,
- architectures: set[Architecture],
+ architectures: Set[Architecture],
) -> None:
allowed_architectures = Architecture.all_archs(platform)
diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py
index 01093a62..c5f80537 100644
--- a/cibuildwheel/bashlex_eval.py
+++ b/cibuildwheel/bashlex_eval.py
@@ -1,8 +1,9 @@
from __future__ import annotations
import subprocess
+from collections.abc import Iterable, Mapping, Sequence
from dataclasses import dataclass
-from typing import Callable, Dict, List, Sequence
+from typing import Callable, Dict, List # noqa: TID251
import bashlex
@@ -10,7 +11,7 @@ import bashlex
EnvironmentExecutor = Callable[[List[str], Dict[str, str]], str]
-def local_environment_executor(command: list[str], env: dict[str, str]) -> str:
+def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) -> str:
return subprocess.run(command, env=env, text=True, stdout=subprocess.PIPE, check=True).stdout
@@ -22,7 +23,7 @@ class NodeExecutionContext:
def evaluate(
- value: str, environment: dict[str, str], executor: EnvironmentExecutor | None = None
+ value: str, environment: Mapping[str, str], executor: EnvironmentExecutor | None = None
) -> str:
if not value:
# empty string evaluates to empty string
@@ -40,7 +41,9 @@ def evaluate(
return evaluate_node(
value_word_node,
context=NodeExecutionContext(
- environment=environment, input=value, executor=executor or local_environment_executor
+ environment=dict(environment),
+ input=value,
+ executor=executor or local_environment_executor,
),
)
@@ -105,7 +108,7 @@ def evaluate_nodes_as_compound_command(
def evaluate_nodes_as_simple_command(
- nodes: list[bashlex.ast.node], context: NodeExecutionContext
+ nodes: Iterable[bashlex.ast.node], context: NodeExecutionContext
) -> str:
command = [evaluate_node(part, context=context) for part in nodes]
return context.executor(command, context.environment)
diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py
index fd17175d..3117b54e 100644
--- a/cibuildwheel/environment.py
+++ b/cibuildwheel/environment.py
@@ -1,14 +1,14 @@
from __future__ import annotations
import dataclasses
-from typing import Any, Mapping, Sequence
+from collections.abc import Mapping, Sequence
+from typing import Any
import bashlex
import bashlex.errors
-from cibuildwheel.typing import Protocol
-
from . import bashlex_eval
+from ._compat.typing import Protocol
class EnvironmentParseError(Exception):
@@ -54,7 +54,7 @@ class EnvironmentAssignment(Protocol):
def evaluated_value(
self,
*,
- environment: dict[str, str],
+ environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
) -> str:
"""Returns the value of this assignment, as evaluated in the environment"""
@@ -91,7 +91,7 @@ class EnvironmentAssignmentBash:
def evaluated_value(
self,
- environment: dict[str, str],
+ environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
) -> str:
return bashlex_eval.evaluate(self.value, environment=environment, executor=executor)
@@ -117,7 +117,7 @@ class ParsedEnvironment:
prev_environment: Mapping[str, str],
executor: bashlex_eval.EnvironmentExecutor | None = None,
) -> dict[str, str]:
- environment = dict(**prev_environment)
+ environment = {**prev_environment}
for assignment in self.assignments:
value = assignment.evaluated_value(environment=environment, executor=executor)
diff --git a/cibuildwheel/extra.py b/cibuildwheel/extra.py
index 36389e6f..5ac9ea1f 100644
--- a/cibuildwheel/extra.py
+++ b/cibuildwheel/extra.py
@@ -4,9 +4,10 @@ These are utilities for the `/bin` scripts, not for the `cibuildwheel` program.
from __future__ import annotations
+from collections.abc import Mapping, Sequence
from io import StringIO
-from .typing import Protocol
+from ._compat.typing import Protocol
__all__ = ("Printable", "dump_python_configurations")
@@ -16,7 +17,9 @@ class Printable(Protocol):
...
-def dump_python_configurations(inp: dict[str, dict[str, list[dict[str, Printable]]]]) -> str:
+def dump_python_configurations(
+ inp: Mapping[str, Mapping[str, Sequence[Mapping[str, Printable]]]]
+) -> str:
output = StringIO()
for header, values in inp.items():
output.write(f"[{header}]\n")
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index acf00667..44bafc55 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -3,19 +3,22 @@ from __future__ import annotations
import subprocess
import sys
import textwrap
+from collections.abc import Iterable, Iterator, Sequence, Set
from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath
-from typing import Iterator, Tuple
+from typing import Tuple
+from ._compat.typing import OrderedDict, assert_never
from .architecture import Architecture
from .logger import log
from .oci_container import OCIContainer
from .options import Options
-from .typing import OrderedDict, PathOrStr, assert_never
+from .typing import PathOrStr
from .util import (
AlreadyBuiltWheelError,
BuildSelector,
NonPlatformWheelError,
+ build_frontend_or_default,
find_compatible_wheel,
get_build_verbosity_extra_flags,
prepare_command,
@@ -46,7 +49,7 @@ class BuildStep:
def get_python_configurations(
build_selector: BuildSelector,
- architectures: set[Architecture],
+ architectures: Set[Architecture],
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("linux")
@@ -111,7 +114,7 @@ def get_build_steps(
def check_all_python_exist(
- *, platform_configs: list[PythonConfiguration], container: OCIContainer
+ *, platform_configs: Iterable[PythonConfiguration], container: OCIContainer
) -> None:
exist = True
messages = []
@@ -136,7 +139,7 @@ def check_all_python_exist(
def build_in_container(
*,
options: Options,
- platform_configs: list[PythonConfiguration],
+ platform_configs: Sequence[PythonConfiguration],
container: OCIContainer,
container_project_path: PurePath,
container_package_dir: PurePath,
@@ -174,6 +177,7 @@ def build_in_container(
for config in platform_configs:
log.build_start(config.identifier)
build_options = options.build_options(config.identifier)
+ build_frontend = build_frontend_or_default(build_options.build_frontend)
dependency_constraint_flags: list[PathOrStr] = []
@@ -239,11 +243,10 @@ def build_in_container(
container.call(["rm", "-rf", built_wheel_dir])
container.call(["mkdir", "-p", built_wheel_dir])
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
- extra_flags = split_config_settings(build_options.config_settings)
+ extra_flags = split_config_settings(build_options.config_settings, build_frontend)
- if build_options.build_frontend == "pip":
- extra_flags += verbosity_flags
+ if build_frontend == "pip":
+ extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
container.call(
[
"python",
@@ -257,9 +260,10 @@ def build_in_container(
],
env=env,
)
- elif build_options.build_frontend == "build":
- verbosity_setting = " ".join(verbosity_flags)
- extra_flags += (f"--config-setting={verbosity_setting}",)
+ elif build_frontend == "build":
+ if not 0 <= build_options.build_verbosity < 2:
+ msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
+ log.warning(msg)
container.call(
[
"python",
@@ -273,7 +277,7 @@ def build_in_container(
env=env,
)
else:
- assert_never(build_options.build_frontend)
+ assert_never(build_frontend)
built_wheel = container.glob(built_wheel_dir, "*.whl")[0]
@@ -375,7 +379,9 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
try:
# check the container engine is installed
subprocess.run(
- [options.globals.container_engine, "--version"], check=True, stdout=subprocess.DEVNULL
+ [options.globals.container_engine.name, "--version"],
+ check=True,
+ stdout=subprocess.DEVNULL,
)
except subprocess.CalledProcessError:
print(
@@ -435,7 +441,7 @@ def build(options: Options, tmp_path: Path) -> None: # noqa: ARG001
sys.exit(1)
-def _matches_prepared_command(error_cmd: list[str], command_template: str) -> bool:
+def _matches_prepared_command(error_cmd: Sequence[str], command_template: str) -> bool:
if len(error_cmd) < 3 or error_cmd[0:2] != ["sh", "-c"]:
return False
command_prefix = command_template.split("{", maxsplit=1)[0].strip()
diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py
index 014f3b00..9a546c97 100644
--- a/cibuildwheel/logger.py
+++ b/cibuildwheel/logger.py
@@ -5,19 +5,20 @@ import os
import re
import sys
import time
-from typing import IO, AnyStr
+from typing import IO, AnyStr, Tuple
-from cibuildwheel.typing import Final
-from cibuildwheel.util import CIProvider, detect_ci_provider
+from ._compat.typing import Final
+from .util import CIProvider, detect_ci_provider
-DEFAULT_FOLD_PATTERN: Final = ("{name}", "")
-FOLD_PATTERNS: Final = {
+FoldPattern = Tuple[str, str]
+DEFAULT_FOLD_PATTERN: Final[FoldPattern] = ("{name}", "")
+FOLD_PATTERNS: Final[dict[str, FoldPattern]] = {
"azure": ("##[group]{name}", "##[endgroup]"),
"travis": ("travis_fold:start:{identifier}\n{name}", "travis_fold:end:{identifier}"),
"github": ("::group::{name}", "::endgroup::{name}"),
}
-PLATFORM_IDENTIFIER_DESCRIPTIONS: Final = {
+PLATFORM_IDENTIFIER_DESCRIPTIONS: Final[dict[str, str]] = {
"manylinux_x86_64": "manylinux x86_64",
"manylinux_i686": "manylinux i686",
"manylinux_aarch64": "manylinux aarch64",
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index a94caa66..1c103629 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -8,23 +8,27 @@ import re
import shutil
import subprocess
import sys
+import typing
+from collections.abc import Sequence, Set
from dataclasses import dataclass
from pathlib import Path
-from typing import Sequence, Tuple, cast
+from typing import Tuple
from filelock import FileLock
+from ._compat.typing import Literal, assert_never
from .architecture import Architecture
from .environment import ParsedEnvironment
from .logger import log
from .options import Options
-from .typing import Literal, PathOrStr, assert_never
+from .typing import PathOrStr
from .util import (
CIBW_CACHE_PATH,
AlreadyBuiltWheelError,
BuildFrontend,
BuildSelector,
NonPlatformWheelError,
+ build_frontend_or_default,
call,
detect_ci_provider,
download,
@@ -53,7 +57,7 @@ def get_macos_version() -> tuple[int, int]:
"""
version_str, _, _ = platform.mac_ver()
version = tuple(map(int, version_str.split(".")[:2]))
- return cast(Tuple[int, int], version)
+ return typing.cast(Tuple[int, int], version)
def get_macos_sdks() -> list[str]:
@@ -69,7 +73,7 @@ class PythonConfiguration:
def get_python_configurations(
- build_selector: BuildSelector, architectures: set[Architecture]
+ build_selector: BuildSelector, architectures: Set[Architecture]
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("macos")
@@ -330,6 +334,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
+ build_frontend = build_frontend_or_default(build_options.build_frontend)
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
@@ -352,7 +357,7 @@ def build(options: Options, tmp_path: Path) -> None:
config,
dependency_constraint_flags,
build_options.environment,
- build_options.build_frontend,
+ build_frontend,
)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
@@ -373,11 +378,10 @@ def build(options: Options, tmp_path: Path) -> None:
log.step("Building wheel...")
built_wheel_dir.mkdir()
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
- extra_flags = split_config_settings(build_options.config_settings)
+ extra_flags = split_config_settings(build_options.config_settings, build_frontend)
- if build_options.build_frontend == "pip":
- extra_flags += verbosity_flags
+ if build_frontend == "pip":
+ extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
@@ -391,9 +395,10 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags,
env=env,
)
- elif build_options.build_frontend == "build":
- verbosity_setting = " ".join(verbosity_flags)
- extra_flags += (f"--config-setting={verbosity_setting}",)
+ elif build_frontend == "build":
+ if not 0 <= build_options.build_verbosity < 2:
+ msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
+ log.warning(msg)
build_env = env.copy()
if build_options.dependency_constraints:
constraint_path = (
@@ -414,7 +419,7 @@ def build(options: Options, tmp_path: Path) -> None:
env=build_env,
)
else:
- assert_never(build_options.build_frontend)
+ assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl"))
diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py
index b92acceb..e0700126 100644
--- a/cibuildwheel/oci_container.py
+++ b/cibuildwheel/oci_container.py
@@ -8,16 +8,47 @@ import shlex
import shutil
import subprocess
import sys
+import typing
import uuid
+from collections.abc import Mapping, Sequence
+from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath
from types import TracebackType
-from typing import IO, Dict, Sequence, cast
+from typing import IO, Dict
-from cibuildwheel.util import CIProvider, detect_ci_provider
+from ._compat.typing import Literal
+from .typing import PathOrStr, PopenBytes
+from .util import CIProvider, detect_ci_provider, parse_key_value_string
-from .typing import Literal, PathOrStr, PopenBytes
+ContainerEngineName = Literal["docker", "podman"]
-ContainerEngine = Literal["docker", "podman"]
+
+@dataclass(frozen=True)
+class OCIContainerEngineConfig:
+ name: ContainerEngineName
+ create_args: Sequence[str] = ()
+
+ @staticmethod
+ def from_config_string(config_string: str) -> OCIContainerEngineConfig:
+ config_dict = parse_key_value_string(config_string, ["name"])
+ name = " ".join(config_dict["name"])
+ if name not in {"docker", "podman"}:
+ msg = f"unknown container engine {name}"
+ raise ValueError(msg)
+
+ name = typing.cast(ContainerEngineName, name)
+ # some flexibility in the option name to cope with TOML conventions
+ create_args = config_dict.get("create_args") or config_dict.get("create-args") or []
+ return OCIContainerEngineConfig(name=name, create_args=create_args)
+
+ def options_summary(self) -> str | dict[str, str]:
+ if not self.create_args:
+ return self.name
+ else:
+ return {"name": self.name, "create_args": repr(self.create_args)}
+
+
+DEFAULT_ENGINE = OCIContainerEngineConfig("docker")
class OCIContainer:
@@ -55,7 +86,7 @@ class OCIContainer:
image: str,
simulate_32_bit: bool = False,
cwd: PathOrStr | None = None,
- engine: ContainerEngine = "docker",
+ engine: OCIContainerEngineConfig = DEFAULT_ENGINE,
):
if not image:
msg = "Must have a non-empty image to run."
@@ -82,13 +113,14 @@ class OCIContainer:
subprocess.run(
[
- self.engine,
+ self.engine.name,
"create",
"--env=CIBUILDWHEEL",
f"--name={self.name}",
"--interactive",
"--volume=/:/host", # ignored on CircleCI
*network_args,
+ *self.engine.create_args,
self.image,
*shell_args,
],
@@ -97,7 +129,7 @@ class OCIContainer:
self.process = subprocess.Popen(
[
- self.engine,
+ self.engine.name,
"start",
"--attach",
"--interactive",
@@ -135,7 +167,7 @@ class OCIContainer:
self.bash_stdin.close()
self.bash_stdout.close()
- if self.engine == "podman":
+ if self.engine.name == "podman":
# This works around what seems to be a race condition in the podman
# backend. The full reason is not understood. See PR #966 for a
# discussion on possible causes and attempts to remove this line.
@@ -145,7 +177,7 @@ class OCIContainer:
assert isinstance(self.name, str)
subprocess.run(
- [self.engine, "rm", "--force", "-v", self.name],
+ [self.engine.name, "rm", "--force", "-v", self.name],
stdout=subprocess.DEVNULL,
check=False,
)
@@ -160,15 +192,16 @@ class OCIContainer:
if from_path.is_dir():
self.call(["mkdir", "-p", to_path])
subprocess.run(
- f"tar cf - . | {self.engine} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -",
+ f"tar cf - . | {self.engine.name} exec -i {self.name} tar --no-same-owner -xC {shell_quote(to_path)} -f -",
shell=True,
check=True,
cwd=from_path,
)
else:
+ exec_process: subprocess.Popen[bytes]
with subprocess.Popen(
[
- self.engine,
+ self.engine.name,
"exec",
"-i",
str(self.name),
@@ -178,10 +211,10 @@ class OCIContainer:
],
stdin=subprocess.PIPE,
) as exec_process:
- exec_process.stdin = cast(IO[bytes], exec_process.stdin)
-
+ assert exec_process.stdin
with open(from_path, "rb") as from_file:
- shutil.copyfileobj(from_file, exec_process.stdin)
+ # Bug in mypy, https://github.com/python/mypy/issues/15031
+ shutil.copyfileobj(from_file, exec_process.stdin) # type: ignore[misc]
exec_process.stdin.close()
exec_process.wait()
@@ -195,10 +228,10 @@ class OCIContainer:
# note: we assume from_path is a dir
to_path.mkdir(parents=True, exist_ok=True)
- if self.engine == "podman":
+ if self.engine.name == "podman":
subprocess.run(
[
- self.engine,
+ self.engine.name,
"cp",
f"{self.name}:{from_path}/.",
str(to_path),
@@ -206,10 +239,10 @@ class OCIContainer:
check=True,
cwd=to_path,
)
- elif self.engine == "docker":
+ elif self.engine.name == "docker":
# There is a bug in docker that prevents a simple 'cp' invocation
# from working https://github.com/moby/moby/issues/38995
- command = f"{self.engine} exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -"
+ command = f"{self.engine.name} exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -"
subprocess.run(
command,
shell=True,
@@ -217,7 +250,7 @@ class OCIContainer:
cwd=to_path,
)
else:
- raise KeyError(self.engine)
+ raise KeyError(self.engine.name)
def glob(self, path: PurePosixPath, pattern: str) -> list[PurePosixPath]:
glob_pattern = path.joinpath(pattern)
@@ -238,7 +271,7 @@ class OCIContainer:
def call(
self,
args: Sequence[PathOrStr],
- env: dict[str, str] | None = None,
+ env: Mapping[str, str] | None = None,
capture_output: bool = False,
cwd: PathOrStr | None = None,
) -> str:
@@ -328,17 +361,17 @@ class OCIContainer:
capture_output=True,
)
)
- return cast(Dict[str, str], env)
+ return typing.cast(Dict[str, str], env)
- def environment_executor(self, command: list[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
return self.call(command, env=environment, capture_output=True)
def debug_info(self) -> str:
- if self.engine == "podman":
- command = f"{self.engine} info --debug"
+ if self.engine.name == "podman":
+ command = f"{self.engine.name} info --debug"
else:
- command = f"{self.engine} info"
+ command = f"{self.engine.name} info"
completed = subprocess.run(
command,
shell=True,
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 6b7b335f..f9d96d0f 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -10,22 +10,20 @@ import shlex
import sys
import textwrap
import traceback
+from collections.abc import Callable, Generator, Iterable, Iterator, Mapping, Set
from pathlib import Path
-from typing import Any, Callable, Dict, Generator, Iterator, List, Mapping, Union, cast
-
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
+from typing import Any, Dict, List, Union
from packaging.specifiers import SpecifierSet
+from ._compat import tomllib
+from ._compat.typing import Literal, NotRequired, TypedDict
from .architecture import Architecture
from .environment import EnvironmentParseError, ParsedEnvironment, parse_environment
from .logger import log
-from .oci_container import ContainerEngine
+from .oci_container import OCIContainerEngineConfig
from .projectfiles import get_requires_python_str
-from .typing import PLATFORMS, Literal, NotRequired, PlatformName, TypedDict
+from .typing import PLATFORMS, PlatformName
from .util import (
MANYLINUX_ARCHS,
MUSLLINUX_ARCHS,
@@ -76,7 +74,7 @@ class GlobalOptions:
build_selector: BuildSelector
test_selector: TestSelector
architectures: set[Architecture]
- container_engine: ContainerEngine
+ container_engine: OCIContainerEngineConfig
@dataclasses.dataclass(frozen=True)
@@ -94,7 +92,7 @@ class BuildOptions:
test_requires: list[str]
test_extras: str
build_verbosity: int
- build_frontend: BuildFrontend
+ build_frontend: BuildFrontend | Literal["default"]
config_settings: str
@property
@@ -137,8 +135,14 @@ DISALLOWED_OPTIONS = {
class TableFmt(TypedDict):
+ # a format string, used with '.format', with `k` and `v` parameters
+ # e.g. "{k}={v}"
item: str
+ # the string that is inserted between items
+ # e.g. " "
sep: str
+ # a quoting function that, if supplied, is called to quote each value
+ # e.g. shlex.quote
quote: NotRequired[Callable[[str], str]]
@@ -161,7 +165,7 @@ def _dig_first(*pairs: tuple[Mapping[str, Setting], str], ignore_empty: bool = F
if key in dict_like:
value = dict_like[key]
- if ignore_empty and value == "":
+ if ignore_empty and value == "": # noqa: PLC1901
continue
return value
@@ -193,7 +197,7 @@ class OptionsReader:
*,
platform: PlatformName,
env: Mapping[str, str],
- disallow: dict[str, set[str]] | None = None,
+ disallow: Mapping[str, Set[str]] | None = None,
) -> None:
self.platform = platform
self.env = env
@@ -455,15 +459,17 @@ class Options:
)
test_selector = TestSelector(skip_config=test_skip)
- container_engine_str = self.reader.get("container-engine")
+ container_engine_str = self.reader.get(
+ "container-engine", table={"item": "{k}:{v}", "sep": "; ", "quote": shlex.quote}
+ )
- if container_engine_str not in ["docker", "podman"]:
- msg = f"cibuildwheel: Unrecognised container_engine {container_engine_str!r}, only 'docker' and 'podman' are supported"
+ try:
+ container_engine = OCIContainerEngineConfig.from_config_string(container_engine_str)
+ except ValueError as e:
+ msg = f"cibuildwheel: Failed to parse container config. {e}"
print(msg, file=sys.stderr)
sys.exit(2)
- container_engine = cast(ContainerEngine, container_engine_str)
-
return GlobalOptions(
package_dir=package_dir,
output_dir=output_dir,
@@ -499,11 +505,13 @@ class Options:
test_extras = self.reader.get("test-extras", sep=",")
build_verbosity_str = self.reader.get("build-verbosity")
- build_frontend: BuildFrontend
+ build_frontend: BuildFrontend | Literal["default"]
if build_frontend_str == "build":
build_frontend = "build"
elif build_frontend_str == "pip":
build_frontend = "pip"
+ elif build_frontend_str == "default":
+ build_frontend = "default"
else:
msg = f"cibuildwheel: Unrecognised build frontend {build_frontend_str!r}, only 'pip' and 'build' are supported"
print(msg, file=sys.stderr)
@@ -597,7 +605,7 @@ class Options:
config_settings=config_settings,
)
- def check_for_invalid_configuration(self, identifiers: list[str]) -> None:
+ def check_for_invalid_configuration(self, identifiers: Iterable[str]) -> None:
if self.platform in {"macos", "windows"}:
before_all_values = {self.build_options(i).before_all for i in identifiers}
@@ -629,7 +637,7 @@ class Options:
read_config_file=False,
)
- def summary(self, identifiers: list[str]) -> str:
+ def summary(self, identifiers: Iterable[str]) -> str:
lines = []
global_option_names = sorted(f.name for f in dataclasses.fields(self.globals))
@@ -667,7 +675,7 @@ class Options:
option_name: str,
option_value: Any,
default_value: Any,
- overrides: dict[str, Any] | None = None,
+ overrides: Mapping[str, Any] | None = None,
) -> str:
"""
Return a summary of the option value, including any overrides, with
diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py
index 0b87415b..95dc150d 100644
--- a/cibuildwheel/projectfiles.py
+++ b/cibuildwheel/projectfiles.py
@@ -7,10 +7,7 @@ import sys
from pathlib import Path
from typing import Any
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
+from ._compat import tomllib
if sys.version_info < (3, 8):
Constant = ast.Str
diff --git a/cibuildwheel/resources/build-platforms.toml b/cibuildwheel/resources/build-platforms.toml
index 601d8d46..1b605eaf 100644
--- a/cibuildwheel/resources/build-platforms.toml
+++ b/cibuildwheel/resources/build-platforms.toml
@@ -6,12 +6,14 @@ python_configurations = [
{ identifier = "cp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_x86_64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-manylinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-manylinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_i686", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-manylinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "pp37-manylinux_x86_64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_x86_64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_x86_64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
@@ -21,18 +23,21 @@ python_configurations = [
{ identifier = "cp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_aarch64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-manylinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-manylinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_ppc64le", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-manylinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-manylinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-manylinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-manylinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-manylinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-manylinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-manylinux_s390x", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-manylinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "pp37-manylinux_aarch64", version = "3.7", path_str = "/opt/python/pp37-pypy37_pp73" },
{ identifier = "pp38-manylinux_aarch64", version = "3.8", path_str = "/opt/python/pp38-pypy38_pp73" },
{ identifier = "pp39-manylinux_aarch64", version = "3.9", path_str = "/opt/python/pp39-pypy39_pp73" },
@@ -45,30 +50,35 @@ python_configurations = [
{ identifier = "cp39-musllinux_x86_64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_x86_64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_x86_64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-musllinux_x86_64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-musllinux_i686", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_i686", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_i686", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_i686", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_i686", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_i686", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-musllinux_i686", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-musllinux_aarch64", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_aarch64", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_aarch64", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_aarch64", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_aarch64", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_aarch64", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-musllinux_aarch64", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-musllinux_ppc64le", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_ppc64le", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_ppc64le", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_ppc64le", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_ppc64le", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_ppc64le", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-musllinux_ppc64le", version = "3.12", path_str = "/opt/python/cp312-cp312" },
{ identifier = "cp36-musllinux_s390x", version = "3.6", path_str = "/opt/python/cp36-cp36m" },
{ identifier = "cp37-musllinux_s390x", version = "3.7", path_str = "/opt/python/cp37-cp37m" },
{ identifier = "cp38-musllinux_s390x", version = "3.8", path_str = "/opt/python/cp38-cp38" },
{ identifier = "cp39-musllinux_s390x", version = "3.9", path_str = "/opt/python/cp39-cp39" },
{ identifier = "cp310-musllinux_s390x", version = "3.10", path_str = "/opt/python/cp310-cp310" },
{ identifier = "cp311-musllinux_s390x", version = "3.11", path_str = "/opt/python/cp311-cp311" },
+ { identifier = "cp312-musllinux_s390x", version = "3.12", path_str = "/opt/python/cp312-cp312" },
]
[macos]
@@ -81,12 +91,15 @@ python_configurations = [
{ identifier = "cp39-macosx_x86_64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
{ identifier = "cp39-macosx_arm64", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
{ identifier = "cp39-macosx_universal2", version = "3.9", url = "https://www.python.org/ftp/python/3.9.13/python-3.9.13-macos11.pkg" },
- { identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.9/python-3.10.9-macos11.pkg" },
- { identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.9/python-3.10.9-macos11.pkg" },
- { identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.9/python-3.10.9-macos11.pkg" },
- { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.1/python-3.11.1-macos11.pkg" },
- { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.1/python-3.11.1-macos11.pkg" },
- { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.1/python-3.11.1-macos11.pkg" },
+ { identifier = "cp310-macosx_x86_64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
+ { identifier = "cp310-macosx_arm64", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
+ { identifier = "cp310-macosx_universal2", version = "3.10", url = "https://www.python.org/ftp/python/3.10.11/python-3.10.11-macos11.pkg" },
+ { identifier = "cp311-macosx_x86_64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.4/python-3.11.4-macos11.pkg" },
+ { identifier = "cp311-macosx_arm64", version = "3.11", url = "https://www.python.org/ftp/python/3.11.4/python-3.11.4-macos11.pkg" },
+ { identifier = "cp311-macosx_universal2", version = "3.11", url = "https://www.python.org/ftp/python/3.11.4/python-3.11.4-macos11.pkg" },
+ { identifier = "cp312-macosx_x86_64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.0/python-3.12.0b2-macos11.pkg" },
+ { identifier = "cp312-macosx_arm64", version = "3.12", url = "https://www.python.org/ftp/python/3.12.0/python-3.12.0b2-macos11.pkg" },
+ { identifier = "cp312-macosx_universal2", version = "3.12", url = "https://www.python.org/ftp/python/3.12.0/python-3.12.0b2-macos11.pkg" },
{ identifier = "pp37-macosx_x86_64", version = "3.7", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-osx64.tar.bz2" },
{ identifier = "pp38-macosx_x86_64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_x86_64.tar.bz2" },
{ identifier = "pp38-macosx_arm64", version = "3.8", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-macos_arm64.tar.bz2" },
@@ -104,13 +117,16 @@ python_configurations = [
{ identifier = "cp38-win_amd64", version = "3.8.10", arch = "64" },
{ identifier = "cp39-win32", version = "3.9.13", arch = "32" },
{ identifier = "cp39-win_amd64", version = "3.9.13", arch = "64" },
- { identifier = "cp310-win32", version = "3.10.9", arch = "32" },
- { identifier = "cp310-win_amd64", version = "3.10.9", arch = "64" },
- { identifier = "cp311-win32", version = "3.11.1", arch = "32" },
- { identifier = "cp311-win_amd64", version = "3.11.1", arch = "64" },
+ { identifier = "cp310-win32", version = "3.10.11", arch = "32" },
+ { identifier = "cp310-win_amd64", version = "3.10.11", arch = "64" },
+ { identifier = "cp311-win32", version = "3.11.4", arch = "32" },
+ { identifier = "cp311-win_amd64", version = "3.11.4", arch = "64" },
+ { identifier = "cp312-win32", version = "3.12.0-b2", arch = "32" },
+ { identifier = "cp312-win_amd64", version = "3.12.0-b2", arch = "64" },
{ identifier = "cp39-win_arm64", version = "3.9.10", arch = "ARM64" },
- { identifier = "cp310-win_arm64", version = "3.10.8", arch = "ARM64" },
- { identifier = "cp311-win_arm64", version = "3.11.1", arch = "ARM64" },
+ { identifier = "cp310-win_arm64", version = "3.10.11", arch = "ARM64" },
+ { identifier = "cp311-win_arm64", version = "3.11.4", arch = "ARM64" },
+ { identifier = "cp312-win_arm64", version = "3.12.0-b2", arch = "ARM64" },
{ identifier = "pp37-win_amd64", version = "3.7", arch = "64", url = "https://downloads.python.org/pypy/pypy3.7-v7.3.9-win64.zip" },
{ identifier = "pp38-win_amd64", version = "3.8", arch = "64", url = "https://downloads.python.org/pypy/pypy3.8-v7.3.11-win64.zip" },
{ identifier = "pp39-win_amd64", version = "3.9", arch = "64", url = "https://downloads.python.org/pypy/pypy3.9-v7.3.11-win64.zip" },
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index c6f6cbe3..3d88a90b 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -8,21 +8,21 @@ delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via delocate
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index c2fa590f..b05133bf 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -8,21 +8,21 @@ delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via delocate
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python312.txt b/cibuildwheel/resources/constraints-python312.txt
new file mode 100644
index 00000000..6e8f761c
--- /dev/null
+++ b/cibuildwheel/resources/constraints-python312.txt
@@ -0,0 +1,28 @@
+#
+# This file is autogenerated by pip-compile with Python 3.12
+# by the following command:
+#
+# nox -s update_constraints-3.12
+#
+delocate==0.10.4
+ # via -r cibuildwheel/resources/constraints.in
+distlib==0.3.6
+ # via virtualenv
+filelock==3.12.1
+ # via virtualenv
+packaging==23.1
+ # via delocate
+platformdirs==3.5.3
+ # via virtualenv
+typing-extensions==4.6.3
+ # via delocate
+virtualenv==20.23.0
+ # via -r cibuildwheel/resources/constraints.in
+wheel==0.40.0
+ # via -r cibuildwheel/resources/constraints.in
+
+# The following packages are considered to be unsafe in a requirements file:
+pip==23.1.2
+ # via -r cibuildwheel/resources/constraints.in
+setuptools==67.8.0
+ # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt
index 2ff0390f..5f80dc5e 100644
--- a/cibuildwheel/resources/constraints-python37.txt
+++ b/cibuildwheel/resources/constraints-python37.txt
@@ -8,28 +8,28 @@ delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-importlib-metadata==6.0.0
+importlib-metadata==6.6.0
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via
# delocate
# importlib-metadata
# platformdirs
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
-zipp==3.12.1
+zipp==3.15.0
# via importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 2cd59960..beeb3aa6 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -8,21 +8,21 @@ delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via delocate
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index e853692d..a3655063 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -8,21 +8,21 @@ delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via delocate
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index c2fa590f..6e8f761c 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -1,28 +1,28 @@
#
-# This file is autogenerated by pip-compile with Python 3.11
+# This file is autogenerated by pip-compile with Python 3.12
# by the following command:
#
-# nox -s update_constraints-3.11
+# nox -s update_constraints-3.12
#
delocate==0.10.4
# via -r cibuildwheel/resources/constraints.in
distlib==0.3.6
# via virtualenv
-filelock==3.9.0
+filelock==3.12.1
# via virtualenv
-packaging==23.0
+packaging==23.1
# via delocate
-platformdirs==2.6.2
+platformdirs==3.5.3
# via virtualenv
-typing-extensions==4.4.0
+typing-extensions==4.6.3
# via delocate
-virtualenv==20.17.1
+virtualenv==20.23.0
# via -r cibuildwheel/resources/constraints.in
-wheel==0.38.4
+wheel==0.40.0
# via -r cibuildwheel/resources/constraints.in
# The following packages are considered to be unsafe in a requirements file:
-pip==23.0
+pip==23.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==67.1.0
+setuptools==67.8.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/defaults.toml b/cibuildwheel/resources/defaults.toml
index d16a3147..40b42f6e 100644
--- a/cibuildwheel/resources/defaults.toml
+++ b/cibuildwheel/resources/defaults.toml
@@ -4,7 +4,7 @@ skip = ""
test-skip = ""
archs = ["auto"]
-build-frontend = "pip"
+build-frontend = "default"
config-settings = {}
dependency-versions = "pinned"
environment = {}
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 85b4b4dd..65635fed 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,49 +1,49 @@
[x86_64]
-manylinux1 = quay.io/pypa/manylinux1_x86_64:2023-02-05-7c3bb1c
+manylinux1 = quay.io/pypa/manylinux1_x86_64:2023-06-07-3623bb5
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2023-02-05-56647d4
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2023-06-11-02cacaf
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2023-06-11-02cacaf
[i686]
-manylinux1 = quay.io/pypa/manylinux1_i686:2023-02-05-7c3bb1c
+manylinux1 = quay.io/pypa/manylinux1_i686:2023-06-07-3623bb5
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2023-02-05-56647d4
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2023-06-11-02cacaf
[pypy_x86_64]
manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2023-06-11-02cacaf
[pypy_i686]
manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-08-05-4535177
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-12-26-0d38463
[aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2023-02-05-56647d4
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2023-06-11-02cacaf
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2023-06-11-02cacaf
[ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2023-01-03-129be5e
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2023-06-11-02cacaf
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2023-06-11-02cacaf
[s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2023-02-05-56647d4
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_s390x:2023-06-11-02cacaf
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2023-06-11-02cacaf
[pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2023-02-05-56647d4
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2023-06-11-02cacaf
manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-12-26-0d38463
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2023-02-05-56647d4
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2023-06-11-02cacaf
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index feb0327e..446d0540 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1,2 +1,2 @@
-version = "20.17.1"
-url = "https://github.com/pypa/get-virtualenv/blob/20.17.1/public/virtualenv.pyz?raw=true"
+version = "20.21.1"
+url = "https://github.com/pypa/get-virtualenv/blob/20.21.1/public/virtualenv.pyz?raw=true"
diff --git a/cibuildwheel/typing.py b/cibuildwheel/typing.py
index 6c8fe9fc..7abd9e3c 100644
--- a/cibuildwheel/typing.py
+++ b/cibuildwheel/typing.py
@@ -2,37 +2,21 @@ from __future__ import annotations
import os
import subprocess
-import sys
-from typing import TYPE_CHECKING, Union
+import typing
+from typing import Union
-if sys.version_info < (3, 8):
- from typing_extensions import Final, Literal, OrderedDict, Protocol, TypedDict
-else:
- from typing import Final, Literal, OrderedDict, Protocol, TypedDict
-
-if sys.version_info < (3, 11):
- from typing_extensions import NotRequired, assert_never
-else:
- from typing import NotRequired, assert_never
+from ._compat.typing import Final, Literal, Protocol
__all__ = (
- "Final",
- "Literal",
"PLATFORMS",
"PathOrStr",
"PlatformName",
- "Protocol",
"PLATFORMS",
"PopenBytes",
- "Protocol",
- "TypedDict",
- "OrderedDict",
- "assert_never",
- "NotRequired",
)
-if TYPE_CHECKING:
+if typing.TYPE_CHECKING:
PopenBytes = subprocess.Popen[bytes]
PathOrStr = Union[str, os.PathLike[str]]
else:
@@ -42,3 +26,9 @@ else:
PlatformName = Literal["linux", "macos", "windows"]
PLATFORMS: Final[set[PlatformName]] = {"linux", "macos", "windows"}
+
+
+class GenericPythonConfiguration(Protocol):
+ @property
+ def identifier(self) -> str:
+ ...
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index fe9e3487..dfc6e65c 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -11,32 +11,19 @@ import subprocess
import sys
import textwrap
import time
+import typing
import urllib.request
+from collections import defaultdict
+from collections.abc import Generator, Iterable, Mapping, Sequence
from dataclasses import dataclass
from enum import Enum
from functools import lru_cache
from pathlib import Path, PurePath
from time import sleep
-from typing import (
- Any,
- ClassVar,
- Generator,
- Iterable,
- Sequence,
- TextIO,
- TypeVar,
- cast,
- overload,
-)
+from typing import Any, ClassVar, TextIO, TypeVar
import bracex
import certifi
-
-if sys.version_info >= (3, 11):
- import tomllib
-else:
- import tomli as tomllib
-
from filelock import FileLock
from packaging.requirements import InvalidRequirement, Requirement
from packaging.specifiers import SpecifierSet
@@ -44,7 +31,10 @@ from packaging.utils import parse_wheel_filename
from packaging.version import Version
from platformdirs import user_cache_path
-from cibuildwheel.typing import Final, Literal, PathOrStr, PlatformName
+from ._compat import tomllib
+from ._compat.functools import cached_property
+from ._compat.typing import Final, Literal
+from .typing import PathOrStr, PlatformName
__all__ = [
"resources_dir",
@@ -71,6 +61,15 @@ test_fail_cwd_file: Final[Path] = resources_dir / "testing_temp_dir_file.py"
BuildFrontend = Literal["pip", "build"]
+
+def build_frontend_or_default(
+ setting: BuildFrontend | Literal["default"], default: BuildFrontend = "pip"
+) -> BuildFrontend:
+ if setting == "default":
+ return default
+ return setting
+
+
MANYLINUX_ARCHS: Final[tuple[str, ...]] = (
"x86_64",
"i686",
@@ -98,20 +97,20 @@ CIBW_CACHE_PATH: Final[Path] = Path(
IS_WIN: Final[bool] = sys.platform.startswith("win")
-@overload
+@typing.overload
def call(
*args: PathOrStr,
- env: dict[str, str] | None = None,
+ env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[False] = ...,
) -> None:
...
-@overload
+@typing.overload
def call(
*args: PathOrStr,
- env: dict[str, str] | None = None,
+ env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: Literal[True],
) -> str:
@@ -120,7 +119,7 @@ def call(
def call(
*args: PathOrStr,
- env: dict[str, str] | None = None,
+ env: Mapping[str, str] | None = None,
cwd: PathOrStr | None = None,
capture_stdout: bool = False,
) -> str | None:
@@ -140,10 +139,12 @@ def call(
result = subprocess.run(args_, check=True, shell=IS_WIN, env=env, cwd=cwd, **kwargs)
if not capture_stdout:
return None
- return cast(str, result.stdout)
+ return typing.cast(str, result.stdout)
-def shell(*commands: str, env: dict[str, str] | None = None, cwd: PathOrStr | None = None) -> None:
+def shell(
+ *commands: str, env: Mapping[str, str] | None = None, cwd: PathOrStr | None = None
+) -> None:
command = " ".join(commands)
print(f"+ {command}")
subprocess.run(command, env=env, cwd=cwd, shell=True, check=True)
@@ -208,9 +209,10 @@ def get_build_verbosity_extra_flags(level: int) -> list[str]:
return []
-def split_config_settings(config_settings: str) -> list[str]:
+def split_config_settings(config_settings: str, frontend: Literal["pip", "build"]) -> list[str]:
config_settings_list = shlex.split(config_settings)
- return [f"--config-setting={setting}" for setting in config_settings_list]
+ s = "s" if frontend == "pip" else ""
+ return [f"--config-setting{s}={setting}" for setting in config_settings_list]
def read_python_configs(config: PlatformName) -> list[dict[str, str]]:
@@ -250,7 +252,7 @@ class BuildSelector:
requires_python: SpecifierSet | None = None
# a pattern that skips prerelease versions, when include_prereleases is False.
- PRERELEASE_SKIP: ClassVar[str] = ""
+ PRERELEASE_SKIP: ClassVar[str] = "cp312-*"
prerelease_pythons: bool = False
def __call__(self, build_id: str) -> bool:
@@ -497,7 +499,7 @@ def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
)
-def get_pip_version(env: dict[str, str]) -> str:
+def get_pip_version(env: Mapping[str, str]) -> str:
versions_output_text = call(
"python", "-m", "pip", "freeze", "--all", capture_stdout=True, env=env
)
@@ -543,8 +545,8 @@ def _parse_constraints_for_virtualenv(
constraint_path = Path(dependency_constraint_flags[1])
assert constraint_path.exists()
with constraint_path.open(encoding="utf-8") as constraint_file:
- for line in constraint_file:
- line = line.strip()
+ for line_ in constraint_file:
+ line = line_.strip()
if not line:
continue
if line.startswith("#"):
@@ -657,12 +659,6 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> T | None:
return None
-if sys.version_info >= (3, 8):
- from functools import cached_property
-else:
- from .functools_cached_property_38 import cached_property
-
-
# Can be replaced by contextlib.chdir in Python 3.11
@contextlib.contextmanager
def chdir(new_path: Path | str) -> Generator[None, None, None]:
@@ -702,3 +698,40 @@ def fix_ansi_codes_for_github_actions(text: str) -> str:
ansi_codes.append(code)
return output
+
+
+def parse_key_value_string(
+ key_value_string: str, positional_arg_names: list[str] | None = None
+) -> dict[str, list[str]]:
+ """
+ Parses a string like "docker; create_args: --some-option=value another-option"
+ """
+ if positional_arg_names is None:
+ positional_arg_names = []
+
+ shlexer = shlex.shlex(key_value_string, posix=True, punctuation_chars=";:")
+ shlexer.commenters = ""
+ parts = list(shlexer)
+ # parts now looks like
+ # ['docker', ';', 'create_args',':', '--some-option=value', 'another-option']
+
+ # split by semicolon
+ fields = [list(group) for k, group in itertools.groupby(parts, lambda x: x == ";") if not k]
+
+ result: dict[str, list[str]] = defaultdict(list)
+ for field_i, field in enumerate(fields):
+ if len(field) > 1 and field[1] == ":":
+ field_name = field[0]
+ values = field[2:]
+ else:
+ try:
+ field_name = positional_arg_names[field_i]
+ except IndexError:
+ msg = f"Failed to parse {key_value_string!r}. Too many positional arguments - expected a maximum of {len(positional_arg_names)}"
+ raise ValueError(msg) from None
+
+ values = field
+
+ result[field_name] += values
+
+ return result
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index 9182f046..d3e58786 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -6,26 +6,29 @@ import shutil
import subprocess
import sys
import textwrap
+from collections.abc import MutableMapping, Sequence, Set
+from contextlib import suppress
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
-from typing import Sequence
from zipfile import ZipFile
from filelock import FileLock
from packaging.version import Version
+from ._compat.typing import assert_never
from .architecture import Architecture
from .environment import ParsedEnvironment
from .logger import log
from .options import Options
-from .typing import PathOrStr, assert_never
+from .typing import PathOrStr
from .util import (
CIBW_CACHE_PATH,
AlreadyBuiltWheelError,
BuildFrontend,
BuildSelector,
NonPlatformWheelError,
+ build_frontend_or_default,
call,
download,
find_compatible_wheel,
@@ -71,7 +74,7 @@ class PythonConfiguration:
def get_python_configurations(
build_selector: BuildSelector,
- architectures: set[Architecture],
+ architectures: Set[Architecture],
) -> list[PythonConfiguration]:
full_python_configs = read_python_configs("windows")
@@ -135,7 +138,7 @@ def setup_setuptools_cross_compile(
tmp: Path,
python_configuration: PythonConfiguration,
python_libs_base: Path,
- env: dict[str, str],
+ env: MutableMapping[str, str],
) -> None:
distutils_cfg = tmp / "extra-setup.cfg"
env["DIST_EXTRA_CONFIG"] = str(distutils_cfg)
@@ -183,7 +186,7 @@ def setup_rust_cross_compile(
tmp: Path, # noqa: ARG001
python_configuration: PythonConfiguration,
python_libs_base: Path, # noqa: ARG001
- env: dict[str, str],
+ env: MutableMapping[str, str],
) -> None:
# Assume that MSVC will be used, because we already know that we are
# cross-compiling. MinGW users can set CARGO_BUILD_TARGET themselves
@@ -366,6 +369,7 @@ def build(options: Options, tmp_path: Path) -> None:
for config in python_configurations:
build_options = options.build_options(config.identifier)
+ build_frontend = build_frontend_or_default(build_options.build_frontend)
log.build_start(config.identifier)
identifier_tmp_dir = tmp_path / config.identifier
@@ -386,7 +390,7 @@ def build(options: Options, tmp_path: Path) -> None:
config,
dependency_constraint_flags,
build_options.environment,
- build_options.build_frontend,
+ build_frontend,
)
compatible_wheel = find_compatible_wheel(built_wheels, config.identifier)
@@ -410,11 +414,10 @@ def build(options: Options, tmp_path: Path) -> None:
log.step("Building wheel...")
built_wheel_dir.mkdir()
- verbosity_flags = get_build_verbosity_extra_flags(build_options.build_verbosity)
- extra_flags = split_config_settings(build_options.config_settings)
+ extra_flags = split_config_settings(build_options.config_settings, build_frontend)
- if build_options.build_frontend == "pip":
- extra_flags += verbosity_flags
+ if build_frontend == "pip":
+ extra_flags += get_build_verbosity_extra_flags(build_options.build_verbosity)
# Path.resolve() is needed. Without it pip wheel may try to fetch package from pypi.org
# see https://github.com/pypa/cibuildwheel/pull/369
call(
@@ -428,9 +431,10 @@ def build(options: Options, tmp_path: Path) -> None:
*extra_flags,
env=env,
)
- elif build_options.build_frontend == "build":
- verbosity_setting = " ".join(verbosity_flags)
- extra_flags += (f"--config-setting={verbosity_setting}",)
+ elif build_frontend == "build":
+ if not 0 <= build_options.build_verbosity < 2:
+ msg = f"build_verbosity {build_options.build_verbosity} is not supported for build frontend. Ignoring."
+ log.warning(msg)
build_env = env.copy()
if build_options.dependency_constraints:
constraints_path = (
@@ -461,7 +465,7 @@ def build(options: Options, tmp_path: Path) -> None:
env=build_env,
)
else:
- assert_never(build_options.build_frontend)
+ assert_never(build_frontend)
built_wheel = next(built_wheel_dir.glob("*.whl"))
@@ -557,6 +561,9 @@ def build(options: Options, tmp_path: Path) -> None:
# we're all done here; move it to output (remove if already exists)
if compatible_wheel is None:
+ with suppress(FileNotFoundError):
+ (build_options.output_dir / repaired_wheel.name).unlink()
+
shutil.move(str(repaired_wheel), build_options.output_dir)
built_wheels.append(build_options.output_dir / repaired_wheel.name)
diff --git a/docs/changelog.md b/docs/changelog.md
index a4a64013..126b0f60 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,43 @@ title: Changelog
# Changelog
+### v2.13.1
+
+_10 June 2023_
+
+- 🛠 Updates the prerelease CPython 3.12 version to 3.12.0b2. (#1516)
+- 🛠 Adds a moving `v.` tag for use in GitHub Actions workflow files. If you use this, you'll get the latest patch release within a minor version. Additionally, Dependabot won't send you PRs for patch releases. (#1517)
+
+### v2.13.0
+
+_28 May 2023_
+
+- ✨ Adds CPython 3.12 support, under the prerelease flag [CIBW_PRERELEASE_PYTHONS](https://cibuildwheel.readthedocs.io/en/stable/options/#prerelease-pythons). This version of cibuildwheel uses 3.12.0b1.
+
+ While CPython is in beta, the ABI can change, so your wheels might not be compatible with the final release. For this reason, we don't recommend distributing wheels until RC1, at which point 3.12 will be available in cibuildwheel without the flag. (#1507)
+
+- ✨ Adds the ability to pass arguments to the container engine when the container is created, using the [CIBW_CONTAINER_ENGINE](https://cibuildwheel.readthedocs.io/en/stable/options/#container-engine) option. (#1499)
+
+### v2.12.3
+
+_19 April 2023_
+
+- 🐛 Fix an import error when running on Python 3.7. (#1479)
+
+### v2.12.2
+
+_18 April 2023_
+
+- 🐛 Fix a bug that caused an extra empty config-setting to be passed to the backend when CIBW_BUILD_FRONTEND is set to `build`. (#1474)
+- 🐛 Fix a crash that occurred when overwriting an existing wheel on Windows. (#1464)
+- 🛠 Pinned version updates, including CPython 3.10.11, 3.11.3, pip 23.1 and wheel 0.40.0.
+
+### v2.12.1
+
+_11 March 2023_
+
+- 🐛 Fix a bug that prevented the use of CIBW_CONFIG_SETTINGS with the 'pip' build backend. (#1430)
+
### v2.12.0
_16 Jan 2023_
diff --git a/docs/data/projects.yml b/docs/data/projects.yml
index 82abe91a..cf993735 100644
--- a/docs/data/projects.yml
+++ b/docs/data/projects.yml
@@ -431,11 +431,6 @@
ci: [github]
os: [apple, linux, windows]
-- name: pydantic
- gh: samuelcolvin/pydantic
- ci: [github]
- os: [apple, linux, windows]
-
- name: vaex
gh: vaexio/vaex
ci: [github]
@@ -628,3 +623,10 @@
os: [windows, apple, linux]
pypi: picologging
notes: A high-performance logging library for Python.
+
+- name: aalink
+ gh: artfwo/aalink
+ ci: [github]
+ os: [windows, apple, linux]
+ pypi: aalink
+ notes: Async Python interface for Ableton Link.
diff --git a/docs/faq.md b/docs/faq.md
index f7256b89..f3497474 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -161,7 +161,7 @@ There are two suggested methods for keeping cibuildwheel up to date that instead
If you use GitHub Actions for builds, you can use cibuildwheel as an action:
```yaml
-uses: pypa/cibuildwheel@v2.12.0
+uses: pypa/cibuildwheel@v2.13.1
```
This is a composite step that just runs cibuildwheel using pipx. You can set command-line options as `with:` parameters, and use `env:` as normal.
@@ -183,7 +183,7 @@ The second option, and the only one that supports other CI systems, is using a `
```bash
# requirements-cibw.txt
-cibuildwheel==2.12.0
+cibuildwheel==2.13.1
```
Then your install step would have `python -m pip install -r requirements-cibw.txt` in it. Your `.github/dependabot.yml` file could look like this:
@@ -327,7 +327,7 @@ Solutions to this vary, but the simplest is to use pipx:
# most runners have pipx preinstalled, but in case you don't
python3 -m pip install pipx
-pipx run cibuildwheel==2.12.0 --output-dir wheelhouse
+pipx run cibuildwheel==2.13.1 --output-dir wheelhouse
pipx run twine upload wheelhouse/*.whl
```
diff --git a/docs/options.md b/docs/options.md
index 257cd3bd..9d6da2b3 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -92,8 +92,8 @@ Options have the same names as the environment variable overrides, but are
placed in `[tool.cibuildwheel]` and are lower case, with dashes, following
common [TOML][] practice. Anything placed in subsections `linux`, `windows`,
or `macos` will only affect those platforms. Lists can be used instead of
-strings for items that are natually a list. Multiline strings also work just
-like in in the environment variables. Environment variables will take
+strings for items that are naturally a list. Multiline strings also work just
+like in the environment variables. Environment variables will take
precedence if defined.
The example above using environment variables could have been written like this:
@@ -226,6 +226,7 @@ When setting the options, you can use shell-style globbing syntax, as per [fnmat
| Python 3.9 | cp39-macosx_x86_64
cp39-macosx_universal2
cp39-macosx_arm64 | cp39-win_amd64
cp39-win32
cp39-win_arm64 | cp39-manylinux_x86_64
cp39-manylinux_i686
cp39-musllinux_x86_64
cp39-musllinux_i686 | cp39-manylinux_aarch64
cp39-manylinux_ppc64le
cp39-manylinux_s390x
cp39-musllinux_aarch64
cp39-musllinux_ppc64le
cp39-musllinux_s390x |
| Python 3.10 | cp310-macosx_x86_64
cp310-macosx_universal2
cp310-macosx_arm64 | cp310-win_amd64
cp310-win32
cp310-win_arm64 | cp310-manylinux_x86_64
cp310-manylinux_i686
cp310-musllinux_x86_64
cp310-musllinux_i686 | cp310-manylinux_aarch64
cp310-manylinux_ppc64le
cp310-manylinux_s390x
cp310-musllinux_aarch64
cp310-musllinux_ppc64le
cp310-musllinux_s390x |
| Python 3.11 | cp311-macosx_x86_64
cp311-macosx_universal2
cp311-macosx_arm64 | cp311-win_amd64
cp311-win32
cp311-win_arm64 | cp311-manylinux_x86_64
cp311-manylinux_i686
cp311-musllinux_x86_64
cp311-musllinux_i686 | cp311-manylinux_aarch64
cp311-manylinux_ppc64le
cp311-manylinux_s390x
cp311-musllinux_aarch64
cp311-musllinux_ppc64le
cp311-musllinux_s390x |
+| Python 3.12 | cp312-macosx_x86_64
cp312-macosx_universal2
cp312-macosx_arm64 | cp312-win_amd64
cp312-win32
cp312-win_arm64 | cp312-manylinux_x86_64
cp312-manylinux_i686
cp312-musllinux_x86_64
cp312-musllinux_i686 | cp312-manylinux_aarch64
cp312-manylinux_ppc64le
cp312-manylinux_s390x
cp312-musllinux_aarch64
cp312-musllinux_ppc64le
cp312-musllinux_s390x |
| PyPy3.7 v7.3 | pp37-macosx_x86_64 | pp37-win_amd64 | pp37-manylinux_x86_64
pp37-manylinux_i686 | pp37-manylinux_aarch64 |
| PyPy3.8 v7.3 | pp38-macosx_x86_64
pp38-macosx_arm64 | pp38-win_amd64 | pp38-manylinux_x86_64
pp38-manylinux_i686 | pp38-manylinux_aarch64 |
| PyPy3.9 v7.3 | pp39-macosx_x86_64
pp39-macosx_arm64 | pp39-win_amd64 | pp39-manylinux_x86_64
pp39-manylinux_i686 | pp39-manylinux_aarch64 |
@@ -237,7 +238,7 @@ For CPython, the minimally supported macOS version is 10.9; for PyPy 3.7, macOS
Windows arm64 platform support is experimental.
-See the [cibuildwheel 1 documentation](https://cibuildwheel.readthedocs.io/en/1.x/) for past end of life versions of Python, and PyPy2.7.
+See the [cibuildwheel 1 documentation](https://cibuildwheel.readthedocs.io/en/1.x/) for past end-of-life versions of Python, and PyPy2.7.
#### Examples
@@ -314,7 +315,7 @@ See the [cibuildwheel 1 documentation](https://cibuildwheel.readthedocs.io/en/1.
It is generally recommended to set `CIBW_BUILD` as an environment variable, though `skip`
tends to be useful in a config file; you can statically declare that you don't
- support pypy, for example.
+ support PyPy, for example.