diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index ce05ac5c..6fddca0d 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -5,6 +5,3 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- ignore:
- - dependency-name: "actions/*"
- update-types: ["version-update:semver-minor", "version-update:semver-patch"]
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 679254fd..89126ed2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -27,7 +27,7 @@ repos:
# Autoremoves unused imports
- repo: https://github.com/hadialqattan/pycln
- rev: v1.3.3
+ rev: v1.3.5
hooks:
- id: pycln
args: [--all]
diff --git a/bin/bump_version.py b/bin/bump_version.py
index 7e2b1b30..cab036ca 100755
--- a/bin/bump_version.py
+++ b/bin/bump_version.py
@@ -184,7 +184,7 @@ def bump_version() -> None:
print()
release_url = "https://github.com/pypa/cibuildwheel/releases/new?" + urllib.parse.urlencode(
- {"tag": new_version}
+ {"tag": f"v{new_version}"}
)
print("Then create a release at the URL:")
print(f" {release_url}")
diff --git a/bin/update_docker.py b/bin/update_docker.py
index 32d98e75..4d1af209 100755
--- a/bin/update_docker.py
+++ b/bin/update_docker.py
@@ -2,8 +2,8 @@
from __future__ import annotations
import configparser
+from dataclasses import dataclass
from pathlib import Path
-from typing import NamedTuple
import requests
@@ -11,7 +11,8 @@ DIR = Path(__file__).parent.resolve()
RESOURCES = DIR.parent / "cibuildwheel/resources"
-class Image(NamedTuple):
+@dataclass(frozen=True)
+class Image:
manylinux_version: str
platform: str
image_name: str
diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py
index 2e67007e..8dd9c5ef 100755
--- a/bin/update_virtualenv.py
+++ b/bin/update_virtualenv.py
@@ -6,8 +6,8 @@ import difflib
import logging
import subprocess
import sys
+from dataclasses import dataclass
from pathlib import Path
-from typing import NamedTuple
import click
import rich
@@ -36,7 +36,8 @@ GET_VIRTUALENV_URL_TEMPLATE: Final[
] = f"{GET_VIRTUALENV_GITHUB}/blob/{{version}}/public/virtualenv.pyz?raw=true"
-class VersionTuple(NamedTuple):
+@dataclass(frozen=True)
+class VersionTuple:
version: Version
version_string: str
diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py
index f8158b31..73077735 100644
--- a/cibuildwheel/__main__.py
+++ b/cibuildwheel/__main__.py
@@ -116,7 +116,7 @@ def main() -> None:
help="Enable pre-release Python versions if available.",
)
- args = parser.parse_args(namespace=CommandLineArguments())
+ args = CommandLineArguments(**vars(parser.parse_args()))
args.package_dir = args.package_dir.resolve()
diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py
index 9eb5eac3..a8b4691c 100644
--- a/cibuildwheel/bashlex_eval.py
+++ b/cibuildwheel/bashlex_eval.py
@@ -1,5 +1,6 @@
import subprocess
-from typing import Callable, Dict, List, NamedTuple, Optional, Sequence
+from dataclasses import dataclass
+from typing import Callable, Dict, List, Optional, Sequence
import bashlex
@@ -13,7 +14,8 @@ def local_environment_executor(command: List[str], env: Dict[str, str]) -> str:
).stdout
-class NodeExecutionContext(NamedTuple):
+@dataclass(frozen=True)
+class NodeExecutionContext:
environment: Dict[str, str]
input: str
executor: EnvironmentExecutor
diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py
index d6b39e74..56fc01a5 100644
--- a/cibuildwheel/linux.py
+++ b/cibuildwheel/linux.py
@@ -1,8 +1,9 @@
import subprocess
import sys
import textwrap
+from dataclasses import dataclass
from pathlib import Path, PurePath, PurePosixPath
-from typing import Iterator, List, NamedTuple, Set, Tuple
+from typing import Iterator, List, Set, Tuple
from .architecture import Architecture
from .docker_container import DockerContainer
@@ -20,7 +21,8 @@ from .util import (
)
-class PythonConfiguration(NamedTuple):
+@dataclass(frozen=True)
+class PythonConfiguration:
version: str
identifier: str
path_str: str
@@ -30,7 +32,8 @@ class PythonConfiguration(NamedTuple):
return PurePosixPath(self.path_str)
-class BuildStep(NamedTuple):
+@dataclass(frozen=True)
+class BuildStep:
platform_configs: List[PythonConfiguration]
platform_tag: str
docker_image: str
diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py
index d5fff5ed..8cc2e80d 100644
--- a/cibuildwheel/macos.py
+++ b/cibuildwheel/macos.py
@@ -5,8 +5,9 @@ import re
import shutil
import subprocess
import sys
+from dataclasses import dataclass
from pathlib import Path
-from typing import Dict, List, NamedTuple, Sequence, Set, Tuple, cast
+from typing import Dict, List, Sequence, Set, Tuple, cast
from filelock import FileLock
@@ -55,7 +56,8 @@ def get_macos_sdks() -> List[str]:
return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)]
-class PythonConfiguration(NamedTuple):
+@dataclass(frozen=True)
+class PythonConfiguration:
version: str
identifier: str
url: str
diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py
index 5d9744af..1863469f 100644
--- a/cibuildwheel/options.py
+++ b/cibuildwheel/options.py
@@ -4,19 +4,9 @@ import sys
import traceback
from configparser import ConfigParser
from contextlib import contextmanager
+from dataclasses import asdict, dataclass
from pathlib import Path
-from typing import (
- Any,
- Dict,
- Generator,
- List,
- Mapping,
- NamedTuple,
- Optional,
- Set,
- Tuple,
- Union,
-)
+from typing import Any, Dict, Generator, List, Mapping, Optional, Set, Tuple, Union
if sys.version_info >= (3, 11):
import tomllib
@@ -45,6 +35,7 @@ from .util import (
)
+@dataclass
class CommandLineArguments:
platform: Literal["auto", "linux", "macos", "windows"]
archs: Optional[str]
@@ -56,7 +47,8 @@ class CommandLineArguments:
prerelease_pythons: bool
-class GlobalOptions(NamedTuple):
+@dataclass(frozen=True)
+class GlobalOptions:
package_dir: Path
output_dir: Path
build_selector: BuildSelector
@@ -64,7 +56,8 @@ class GlobalOptions(NamedTuple):
architectures: Set[Architecture]
-class BuildOptions(NamedTuple):
+@dataclass(frozen=True)
+class BuildOptions:
globals: GlobalOptions
environment: ParsedEnvironment
before_all: str
@@ -104,7 +97,8 @@ class BuildOptions(NamedTuple):
Setting = Union[Dict[str, str], List[str], str, int]
-class Override(NamedTuple):
+@dataclass(frozen=True)
+class Override:
select_pattern: str
options: Dict[str, Setting]
@@ -548,12 +542,12 @@ class Options:
def summary(self, identifiers: List[str]) -> str:
lines = [
f"{option_name}: {option_value!r}"
- for option_name, option_value in sorted(self.globals._asdict().items())
+ for option_name, option_value in sorted(asdict(self.globals).items())
]
build_option_defaults = self.build_options(identifier=None)
- for option_name, default_value in sorted(build_option_defaults._asdict().items()):
+ for option_name, default_value in sorted(asdict(build_option_defaults).items()):
if option_name == "globals":
continue
@@ -561,7 +555,7 @@ class Options:
# if any identifiers have an overridden value, print that too
for identifier in identifiers:
- option_value = self.build_options(identifier=identifier)._asdict()[option_name]
+ option_value = getattr(self.build_options(identifier=identifier), option_name)
if option_value != default_value:
lines.append(f" {identifier}: {option_value!r}")
diff --git a/cibuildwheel/resources/constraints-python310.txt b/cibuildwheel/resources/constraints-python310.txt
index 9c96aa12..6f02809e 100644
--- a/cibuildwheel/resources/constraints-python310.txt
+++ b/cibuildwheel/resources/constraints-python310.txt
@@ -16,7 +16,7 @@ six==1.16.0
# via virtualenv
typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -26,5 +26,5 @@ wheel==0.37.1
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python311.txt b/cibuildwheel/resources/constraints-python311.txt
index 82e6c46d..5d69e081 100644
--- a/cibuildwheel/resources/constraints-python311.txt
+++ b/cibuildwheel/resources/constraints-python311.txt
@@ -16,7 +16,7 @@ six==1.16.0
# via virtualenv
typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -26,5 +26,5 @@ wheel==0.37.1
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python36.txt b/cibuildwheel/resources/constraints-python36.txt
index 9ca0fd11..e5bc034c 100644
--- a/cibuildwheel/resources/constraints-python36.txt
+++ b/cibuildwheel/resources/constraints-python36.txt
@@ -22,7 +22,7 @@ typing-extensions==4.1.1
# via
# delocate
# importlib-metadata
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt
index e0a25472..93118618 100644
--- a/cibuildwheel/resources/constraints-python37.txt
+++ b/cibuildwheel/resources/constraints-python37.txt
@@ -10,7 +10,7 @@ distlib==0.3.4
# via virtualenv
filelock==3.7.1
# via virtualenv
-importlib-metadata==4.11.4
+importlib-metadata==4.12.0
# via virtualenv
platformdirs==2.5.2
# via virtualenv
@@ -20,7 +20,7 @@ typing-extensions==4.2.0
# via
# delocate
# importlib-metadata
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -32,5 +32,5 @@ zipp==3.8.0
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 971a8e42..3de94015 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -16,7 +16,7 @@ six==1.16.0
# via virtualenv
typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -26,5 +26,5 @@ wheel==0.37.1
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index 8f6d1c00..8c407bb0 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -16,7 +16,7 @@ six==1.16.0
# via virtualenv
typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -26,5 +26,5 @@ wheel==0.37.1
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index 8f6d1c00..8c407bb0 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -16,7 +16,7 @@ six==1.16.0
# via virtualenv
typing-extensions==4.2.0
# via delocate
-virtualenv==20.14.1
+virtualenv==20.15.0
# via -r cibuildwheel/resources/constraints.in
wheel==0.37.1
# via
@@ -26,5 +26,5 @@ wheel==0.37.1
# The following packages are considered to be unsafe in a requirements file:
pip==22.1.2
# via -r cibuildwheel/resources/constraints.in
-setuptools==62.4.0
+setuptools==62.6.0
# via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 0a7c6592..9d8e6297 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,48 +1,48 @@
[x86_64]
-manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-06-12-a846b05
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-13-c365205
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-13-c365205
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-13-c365205
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-06-13-c365205
+manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-06-26-ddecca8
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-26-9a2ca4b
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-26-9a2ca4b
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-26-9a2ca4b
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-06-26-9a2ca4b
[i686]
-manylinux1 = quay.io/pypa/manylinux1_i686:2022-06-12-a846b05
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-13-c365205
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-13-c365205
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-06-13-c365205
+manylinux1 = quay.io/pypa/manylinux1_i686:2022-06-26-ddecca8
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-26-9a2ca4b
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-26-9a2ca4b
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-06-26-9a2ca4b
[pypy_x86_64]
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-13-c365205
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-13-c365205
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-13-c365205
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-06-26-9a2ca4b
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-06-26-9a2ca4b
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_x86_64:2022-06-26-9a2ca4b
[pypy_i686]
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-13-c365205
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-13-c365205
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-06-26-9a2ca4b
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-06-26-9a2ca4b
[aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-13-c365205
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-13-c365205
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-26-9a2ca4b
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-26-9a2ca4b
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-06-26-9a2ca4b
[ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-06-13-c365205
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2022-06-13-c365205
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-06-26-9a2ca4b
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_ppc64le:2022-06-26-9a2ca4b
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-06-26-9a2ca4b
[s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-06-13-c365205
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-06-26-9a2ca4b
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-06-26-9a2ca4b
[pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-13-c365205
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-13-c365205
-manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-13-c365205
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-06-26-9a2ca4b
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-06-26-9a2ca4b
+manylinux_2_28 = quay.io/pypa/manylinux_2_28_aarch64:2022-06-26-9a2ca4b
diff --git a/cibuildwheel/resources/virtualenv.toml b/cibuildwheel/resources/virtualenv.toml
index 2296e59f..01489be2 100644
--- a/cibuildwheel/resources/virtualenv.toml
+++ b/cibuildwheel/resources/virtualenv.toml
@@ -1,2 +1,2 @@
-version = "20.14.1"
-url = "https://github.com/pypa/get-virtualenv/blob/20.14.1/public/virtualenv.pyz?raw=true"
+version = "20.15.0"
+url = "https://github.com/pypa/get-virtualenv/blob/20.15.0/public/virtualenv.pyz?raw=true"
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index 268e7ce4..f164b91a 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -1,5 +1,4 @@
import contextlib
-import dataclasses
import fnmatch
import itertools
import os
@@ -11,6 +10,7 @@ import sys
import textwrap
import time
import urllib.request
+from dataclasses import dataclass
from enum import Enum
from functools import lru_cache
from pathlib import Path, PurePath
@@ -22,7 +22,6 @@ from typing import (
Generator,
Iterable,
List,
- NamedTuple,
Optional,
Sequence,
TextIO,
@@ -233,7 +232,7 @@ def selector_matches(patterns: str, string: str) -> bool:
# Once we require Python 3.10+, we can add kw_only=True
-@dataclasses.dataclass
+@dataclass(frozen=True)
class BuildSelector:
"""
This class holds a set of build/skip patterns. You call an instance with a
@@ -270,7 +269,7 @@ class BuildSelector:
return should_build and not should_skip
-@dataclasses.dataclass
+@dataclass(frozen=True)
class TestSelector:
"""
A build selector that can only skip tests according to a skip pattern.
@@ -432,6 +431,12 @@ def unwrap(text: str) -> str:
return re.sub(r"\s+", " ", text)
+@dataclass(frozen=True)
+class FileReport:
+ name: str
+ size: str
+
+
@contextlib.contextmanager
def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
"""
@@ -446,10 +451,6 @@ def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
yield
final_contents = set(output_dir.iterdir())
- class FileReport(NamedTuple):
- name: str
- size: str
-
new_contents = [
FileReport(wheel.name, f"{(wheel.stat().st_size + 1023) // 1024:,d}")
for wheel in final_contents - existing_contents
@@ -591,7 +592,7 @@ T = TypeVar("T", bound=PurePath)
def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
"""
Finds a wheel with an abi3 or a none ABI tag in `wheels` compatible with the Python interpreter
- specified by `identifier`.
+ specified by `identifier` that is previously built.
"""
interpreter, platform = identifier.split("-")
@@ -599,27 +600,36 @@ def find_compatible_wheel(wheels: Sequence[T], identifier: str) -> Optional[T]:
_, _, _, tags = parse_wheel_filename(wheel.name)
for tag in tags:
if tag.abi == "abi3":
+ # ABI3 wheels must start with cp3 for impl and tag
if not (interpreter.startswith("cp3") and tag.interpreter.startswith("cp3")):
continue
elif tag.abi == "none":
+ # CPythonless wheels must include py3 tag
if tag.interpreter[:3] != "py3":
continue
else:
+ # Other types of wheels are not detected, this is looking for previously built wheels.
continue
+
if tag.interpreter != "py3" and int(tag.interpreter[3:]) > int(interpreter[3:]):
+ # If a minor version number is given, it has to be lower than the current one.
continue
+
if platform.startswith(("manylinux", "musllinux", "macosx")):
- # Linux, macOS
+ # Linux, macOS require the beginning and ending match (macos/manylinux version doesn't need to)
os_, arch = platform.split("_", 1)
if not tag.platform.startswith(os_):
continue
- if not tag.platform.endswith("_" + arch):
+ if not tag.platform.endswith(f"_{arch}"):
continue
else:
- # Windows
+ # Windows should exactly match
if not tag.platform == platform:
continue
+
+ # If all the filters above pass, then the wheel is a previously built compatible wheel.
return wheel
+
return None
diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py
index b4ae6e07..82309a23 100644
--- a/cibuildwheel/windows.py
+++ b/cibuildwheel/windows.py
@@ -2,9 +2,10 @@ import os
import shutil
import subprocess
import sys
+from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
-from typing import Dict, List, NamedTuple, Optional, Sequence, Set
+from typing import Dict, List, Optional, Sequence, Set
from zipfile import ZipFile
from filelock import FileLock
@@ -47,7 +48,8 @@ def get_nuget_args(version: str, arch: str, output_directory: Path) -> List[str]
]
-class PythonConfiguration(NamedTuple):
+@dataclass(frozen=True)
+class PythonConfiguration:
version: str
arch: str
identifier: str
diff --git a/docs/faq.md b/docs/faq.md
index 66c05618..b66bd0e4 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -147,10 +147,6 @@ updates:
directory: "/"
schedule:
interval: "weekly"
- ignore:
- # Optional: Official actions have moving tags like v1;
- # if you use those, you don't need updates.
- - dependency-name: "actions/*"
```
#### Option 2: Requirement files
diff --git a/docs/options.md b/docs/options.md
index f72b388a..3f12ed8e 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -553,6 +553,10 @@ Platform-specific environment variables are also available:
# Supply options to `pip` to affect how it downloads dependencies
CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.myorg.com/simple
+ # Any pip command-line options can be set using the PIP_ prefix
+ # https://pip.pypa.io/en/stable/topics/configuration/#environment-variables
+ CIBW_ENVIRONMENT: PIP_GLOBAL_OPTION="build_ext -j4"
+
# Set two flags on linux only
CIBW_ENVIRONMENT_LINUX: BUILD_TIME="$(date)" SAMPLE_TEXT="sample text"
```
@@ -582,6 +586,10 @@ Platform-specific environment variables are also available:
# Supply options to `pip` to affect how it downloads dependencies
environment = { PIP_EXTRA_INDEX_URL="https://pypi.myorg.com/simple" }
+ # Any pip command-line option can be set using the PIP_ prefix
+ # https://pip.pypa.io/en/stable/topics/configuration/#environment-variables
+ environment = { PIP_GLOBAL_OPTION="build_ext -j4" }
+
# Set two flags on linux only
[tool.cibuildwheel.linux]
environment = { BUILD_TIME="$(date)", SAMPLE_TEXT="sample text" }
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 2845ebec..a478ee7f 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -18,8 +18,8 @@ title: Working examples
| [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. |
| [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 |
-| [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. |
| [MemRay][] | ![github icon][] | ![linux icon][] | Memray is a memory profiler for Python |
+| [uvloop][] | ![github icon][] | ![apple icon][] ![linux icon][] | Ultra fast asyncio event loop. |
| [psutil][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Cross-platform lib for process and system monitoring in Python |
| [vaex][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Out-of-Core hybrid Apache Arrow/NumPy DataFrame for Python, ML, visualization and exploration of big tabular data at a billion rows per second 🚀 |
| [Google Benchmark][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A microbenchmark support library |
@@ -52,8 +52,8 @@ title: Working examples
| [Wrapt][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python module for decorators, wrappers and monkey patching. |
| [PyAV][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Pythonic bindings for FFmpeg's libraries. |
| [SimpleJSON][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | simplejson is a simple, fast, extensible JSON encoder/decoder for Python |
-| [OpenColorIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
| [pikepdf][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python library for reading and writing PDF, powered by qpdf |
+| [OpenColorIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A color management framework for visual effects and animation. |
| [Line Profiler][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Line-by-line profiling for Python |
| [PyTables][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python package to manage extremely large amounts of data |
| [OpenTimelineIO][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Open Source API and interchange format for editorial timeline information. |
@@ -75,8 +75,8 @@ title: Working examples
| [pybind11 python_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Example pybind11 module built with a Python-based build system |
| [dd-trace-py][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Uses custom alternate arch emulation on GitHub |
| [sourmash][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
-| [time-machine][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. |
| [CTranslate2][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
+| [time-machine][] | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. |
| [cyvcf2][] | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
| [matrixprofile][] | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
| [abess][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A fast best-subset selection library. It uses cibuildwheel to build a large project with C++ extensions. |
@@ -118,8 +118,8 @@ title: Working examples
[Prophet]: https://github.com/facebook/prophet
[MyPy]: https://github.com/mypyc/mypy_mypyc-wheels
[pydantic]: https://github.com/samuelcolvin/pydantic
-[uvloop]: https://github.com/MagicStack/uvloop
[MemRay]: https://github.com/bloomberg/memray
+[uvloop]: https://github.com/MagicStack/uvloop
[psutil]: https://github.com/giampaolo/psutil
[vaex]: https://github.com/vaexio/vaex
[Google Benchmark]: https://github.com/google/benchmark
@@ -152,8 +152,8 @@ title: Working examples
[Wrapt]: https://github.com/GrahamDumpleton/wrapt
[PyAV]: https://github.com/PyAV-Org/PyAV
[SimpleJSON]: https://github.com/simplejson/simplejson
-[OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
[pikepdf]: https://github.com/pikepdf/pikepdf
+[OpenColorIO]: https://github.com/AcademySoftwareFoundation/OpenColorIO
[Line Profiler]: https://github.com/pyutils/line_profiler
[PyTables]: https://github.com/PyTables/PyTables
[OpenTimelineIO]: https://github.com/PixarAnimationStudios/OpenTimelineIO
@@ -175,8 +175,8 @@ title: Working examples
[pybind11 python_example]: https://github.com/pybind/python_example
[dd-trace-py]: https://github.com/DataDog/dd-trace-py
[sourmash]: https://github.com/dib-lab/sourmash
-[time-machine]: https://github.com/adamchainz/time-machine
[CTranslate2]: https://github.com/OpenNMT/CTranslate2
+[time-machine]: https://github.com/adamchainz/time-machine
[cyvcf2]: https://github.com/brentp/cyvcf2
[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
[abess]: https://github.com/abess-team/abess
@@ -218,105 +218,105 @@ title: Working examples
[apple icon]: data/readme_icons/apple.svg
[linux icon]: data/readme_icons/linux.svg
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setup.cfg b/setup.cfg
index 4f91bf92..838631e0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -56,8 +56,8 @@ console_scripts =
cibuildwheel = resources/*
[flake8]
-extend-ignore = E203,E501,B950
-extend-select = B,B9
+extend-ignore = E203,E501,B950,B023
+extend-select = B9
application-import-names = cibuildwheel
exclude =
cibuildwheel/resources/,
diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py
new file mode 100644
index 00000000..932a2ec0
--- /dev/null
+++ b/test/test_abi_variants.py
@@ -0,0 +1,179 @@
+import textwrap
+
+from . import test_projects, utils
+
+limited_api_project = test_projects.new_c_project(
+ setup_py_add=textwrap.dedent(
+ r"""
+ cmdclass = {}
+ extension_kwargs = {}
+ if sys.version_info[:2] >= (3, 8):
+ from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
+
+ class bdist_wheel_abi3(_bdist_wheel):
+ def finalize_options(self):
+ _bdist_wheel.finalize_options(self)
+ self.root_is_pure = False
+
+ def get_tag(self):
+ python, abi, plat = _bdist_wheel.get_tag(self)
+ return python, "abi3", plat
+
+ cmdclass["bdist_wheel"] = bdist_wheel_abi3
+ extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")]
+ extension_kwargs["py_limited_api"] = True
+ """
+ ),
+ setup_py_extension_args_add="**extension_kwargs",
+ setup_py_setup_args_add="cmdclass=cmdclass",
+)
+
+
+def test_abi3(tmp_path):
+ project_dir = tmp_path / "project"
+ limited_api_project.generate(project_dir)
+
+ # build the wheels
+ actual_wheels = utils.cibuildwheel_run(
+ project_dir,
+ add_env={
+ "CIBW_SKIP": "pp* ", # PyPy does not have a Py_LIMITED_API equivalent
+ },
+ )
+
+ # check that the expected wheels are produced
+ expected_wheels = [
+ w.replace("cp38-cp38", "cp38-abi3")
+ for w in utils.expected_wheels("spam", "0.1.0")
+ if "-pp" not in w and "-cp39" not in w and "-cp310" not in w and "-cp311" not in w
+ ]
+ assert set(actual_wheels) == set(expected_wheels)
+
+
+ctypes_project = test_projects.TestProject()
+ctypes_project.files["setup.py"] = textwrap.dedent(
+ """
+ from setuptools import setup, Extension
+
+ from distutils.command.build_ext import build_ext as _build_ext
+ class CTypesExtension(Extension): pass
+ class build_ext(_build_ext):
+ def build_extension(self, ext):
+ self._ctypes = isinstance(ext, CTypesExtension)
+ return super().build_extension(ext)
+
+ def get_export_symbols(self, ext):
+ if self._ctypes:
+ return ext.export_symbols
+ return super().get_export_symbols(ext)
+
+ def get_ext_filename(self, ext_name):
+ if self._ctypes:
+ return ext_name + '.so'
+ return super().get_ext_filename(ext_name)
+
+ from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
+ class bdist_wheel_abi_none(_bdist_wheel):
+ def finalize_options(self):
+ _bdist_wheel.finalize_options(self)
+ self.root_is_pure = False
+
+ def get_tag(self):
+ python, abi, plat = _bdist_wheel.get_tag(self)
+ return "py3", "none", plat
+
+ setup(
+ name="ctypesexample",
+ version="1.0.0",
+ py_modules = ["ctypesexample.summing"],
+ ext_modules=[
+ CTypesExtension(
+ "ctypesexample.csumlib",
+ ["ctypesexample/csumlib.c"],
+ ),
+ ],
+ cmdclass={'build_ext': build_ext, 'bdist_wheel': bdist_wheel_abi_none},
+ )
+ """
+)
+ctypes_project.files["ctypesexample/csumlib.c"] = textwrap.dedent(
+ """
+ #ifdef _WIN32
+ #define LIBRARY_API __declspec(dllexport)
+ #else
+ #define LIBRARY_API
+ #endif
+
+ #include
+
+
+ LIBRARY_API double *add_vec3(double *a, double *b)
+ {
+ double *res = malloc(sizeof(double) * 3);
+
+ for (int i = 0; i < 3; ++i)
+ {
+ res[i] = a[i] + b[i];
+ }
+
+ return res;
+ }
+ """
+)
+ctypes_project.files["ctypesexample/summing.py"] = textwrap.dedent(
+ """
+ import ctypes
+ import pathlib
+
+ # path of the shared library
+ libfile = pathlib.Path(__file__).parent / "csumlib.so"
+ csumlib = ctypes.CDLL(str(libfile))
+
+ type_vec3 = ctypes.POINTER(ctypes.c_double * 3)
+
+ csumlib.add_vec3.restype = type_vec3
+ csumlib.add_vec3.argtypes = [type_vec3, type_vec3]
+ def add(a: list, b: list) -> list:
+ a_p = (ctypes.c_double * 3)(*a)
+ b_p = (ctypes.c_double * 3)(*b)
+ r_p = csumlib.add_vec3(a_p,b_p)
+
+ return [l for l in r_p.contents]
+ """
+)
+
+ctypes_project.files["test/add_test.py"] = textwrap.dedent(
+ """
+ import ctypesexample.summing
+
+ def test():
+ a = [1, 2, 3]
+ b = [4, 5, 6]
+ assert ctypesexample.summing.add(a, b) == [5, 7, 9]
+ """
+)
+
+
+def test_abi_none(tmp_path, capfd):
+ project_dir = tmp_path / "project"
+ ctypes_project.generate(project_dir)
+
+ # build the wheels
+ actual_wheels = utils.cibuildwheel_run(
+ project_dir,
+ add_env={
+ "CIBW_TEST_REQUIRES": "pytest",
+ "CIBW_TEST_COMMAND": "pytest {project}/test",
+ # limit the number of builds for test performance reasons
+ "CIBW_BUILD": "cp38-* cp310-* pp39-*",
+ },
+ )
+
+ # check that the expected wheels are produced
+ expected_wheels = utils.expected_wheels("ctypesexample", "1.0.0", python_abi_tags=["py3-none"])
+ assert set(actual_wheels) == set(expected_wheels)
+
+ # check that each wheel was built once, and reused
+ captured = capfd.readouterr()
+ assert "Building wheel..." in captured.out
+ assert "Found previously built wheel" in captured.out
diff --git a/test/test_limited_api.py b/test/test_limited_api.py
deleted file mode 100644
index ea173bf5..00000000
--- a/test/test_limited_api.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import textwrap
-
-from . import test_projects, utils
-
-limited_api_project = test_projects.new_c_project(
- setup_py_add=textwrap.dedent(
- r"""
- cmdclass = {}
- extension_kwargs = {}
- if sys.version_info[:2] >= (3, 8):
- from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
-
- class bdist_wheel_abi3(_bdist_wheel):
- def finalize_options(self):
- _bdist_wheel.finalize_options(self)
- self.root_is_pure = False
-
- def get_tag(self):
- python, abi, plat = _bdist_wheel.get_tag(self)
- return python, "abi3", plat
-
- cmdclass["bdist_wheel"] = bdist_wheel_abi3
- extension_kwargs["define_macros"] = [("Py_LIMITED_API", "0x03080000")]
- extension_kwargs["py_limited_api"] = True
- """
- ),
- setup_py_extension_args_add="**extension_kwargs",
- setup_py_setup_args_add="cmdclass=cmdclass",
-)
-
-
-def test(tmp_path):
- project_dir = tmp_path / "project"
- limited_api_project.generate(project_dir)
-
- # build the wheels
- actual_wheels = utils.cibuildwheel_run(
- project_dir,
- add_env={
- "CIBW_SKIP": "pp* ", # PyPy does not have a Py_LIMITED_API equivalent
- },
- )
-
- # check that the expected wheels are produced
- expected_wheels = [
- w.replace("cp38-cp38", "cp38-abi3")
- for w in utils.expected_wheels("spam", "0.1.0")
- if "-pp" not in w and "-cp39" not in w and "-cp310" not in w and "-cp311" not in w
- ]
- assert set(actual_wheels) == set(expected_wheels)
diff --git a/test/utils.py b/test/utils.py
index b6212021..d325be68 100644
--- a/test/utils.py
+++ b/test/utils.py
@@ -113,6 +113,7 @@ def expected_wheels(
musllinux_versions=None,
macosx_deployment_target="10.9",
machine_arch=None,
+ python_abi_tags=None,
):
"""
Returns a list of expected wheels from a run of cibuildwheel.
@@ -139,21 +140,22 @@ def expected_wheels(
if musllinux_versions is None:
musllinux_versions = ["musllinux_1_1"]
- python_abi_tags = [
- "cp36-cp36m",
- "cp37-cp37m",
- "cp38-cp38",
- "cp39-cp39",
- "cp310-cp310",
- "cp311-cp311",
- ]
+ if python_abi_tags is None:
+ python_abi_tags = [
+ "cp36-cp36m",
+ "cp37-cp37m",
+ "cp38-cp38",
+ "cp39-cp39",
+ "cp310-cp310",
+ "cp311-cp311",
+ ]
- if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
- python_abi_tags += ["pp37-pypy37_pp73", "pp38-pypy38_pp73", "pp39-pypy39_pp73"]
+ if machine_arch in ["x86_64", "AMD64", "x86", "aarch64"]:
+ python_abi_tags += ["pp37-pypy37_pp73", "pp38-pypy38_pp73", "pp39-pypy39_pp73"]
- if platform == "macos" and machine_arch == "arm64":
- # currently, arm64 macs are only supported by cp39, cp310 & cp311
- python_abi_tags = ["cp39-cp39", "cp310-cp310", "cp311-cp311"]
+ if platform == "macos" and machine_arch == "arm64":
+ # currently, arm64 macs are only supported by cp39, cp310 & cp311
+ python_abi_tags = ["cp39-cp39", "cp310-cp310", "cp311-cp311"]
wheels = []
@@ -185,10 +187,10 @@ def expected_wheels(
)
elif platform == "windows":
- if python_abi_tag.startswith("cp"):
- platform_tags = ["win32", "win_amd64"]
- else:
+ if python_abi_tag.startswith("pp"):
platform_tags = ["win_amd64"]
+ else:
+ platform_tags = ["win32", "win_amd64"]
elif platform == "macos":
if python_abi_tag == "cp39-cp39" and machine_arch == "arm64":
diff --git a/unit_test/utils.py b/unit_test/utils.py
index ef158d55..2b312eca 100644
--- a/unit_test/utils.py
+++ b/unit_test/utils.py
@@ -4,15 +4,15 @@ from cibuildwheel.options import CommandLineArguments
def get_default_command_line_arguments() -> CommandLineArguments:
- defaults = CommandLineArguments()
-
- defaults.platform = "auto"
- defaults.allow_empty = False
- defaults.archs = None
- defaults.config_file = ""
- defaults.output_dir = Path("wheelhouse")
- defaults.package_dir = Path(".")
- defaults.prerelease_pythons = False
- defaults.print_build_identifiers = False
+ defaults = CommandLineArguments(
+ platform="auto",
+ allow_empty=False,
+ archs=None,
+ config_file="",
+ output_dir=Path("wheelhouse"),
+ package_dir=Path("."),
+ prerelease_pythons=False,
+ print_build_identifiers=False,
+ )
return defaults