diff --git a/bin/update_docker.py b/bin/update_docker.py index efb90154..3c6ef79e 100755 --- a/bin/update_docker.py +++ b/bin/update_docker.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import configparser -from dataclasses import dataclass +import dataclasses from pathlib import Path import requests @@ -11,7 +11,7 @@ DIR = Path(__file__).parent.resolve() RESOURCES = DIR.parent / "cibuildwheel/resources" -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class Image: manylinux_version: str platforms: list[str] diff --git a/bin/update_nodejs.py b/bin/update_nodejs.py index 3f9eb526..a564fad0 100755 --- a/bin/update_nodejs.py +++ b/bin/update_nodejs.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 +import dataclasses import difflib import logging import tomllib -from dataclasses import dataclass from pathlib import Path from typing import Final @@ -27,7 +27,7 @@ NODEJS_DIST: Final[str] = "https://nodejs.org/dist/" NODEJS_INDEX: Final[str] = f"{NODEJS_DIST}index.json" -@dataclass(frozen=True, order=True) +@dataclasses.dataclass(frozen=True, order=True) class VersionTuple: version: Version version_string: str diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index c6cd1b0c..6e38eadb 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 +import dataclasses import difflib import logging import subprocess import tomllib -from dataclasses import dataclass from pathlib import Path from typing import Final @@ -28,7 +28,7 @@ GET_VIRTUALENV_URL_TEMPLATE: Final[str] = ( ) -@dataclass(frozen=True, order=True) +@dataclasses.dataclass(frozen=True, order=True) class VersionTuple: version: Version version_string: str diff --git a/cibuildwheel/bashlex_eval.py b/cibuildwheel/bashlex_eval.py index 6656d0a4..3d1ac118 100644 --- a/cibuildwheel/bashlex_eval.py +++ b/cibuildwheel/bashlex_eval.py @@ -1,3 +1,4 @@ +import dataclasses import subprocess from collections.abc import ( Callable, @@ -5,7 +6,6 @@ from collections.abc import ( Mapping, Sequence, ) -from dataclasses import dataclass import bashlex @@ -17,7 +17,7 @@ def local_environment_executor(command: Sequence[str], env: Mapping[str, str]) - return subprocess.run(command, env=env, text=True, stdout=subprocess.PIPE, check=True).stdout -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class NodeExecutionContext: environment: dict[str, str] input: str diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py index d5a0796d..a9a3f862 100644 --- a/cibuildwheel/environment.py +++ b/cibuildwheel/environment.py @@ -102,7 +102,7 @@ class EnvironmentAssignmentBash: return False -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class ParsedEnvironment: assignments: list[EnvironmentAssignment] diff --git a/cibuildwheel/frontend.py b/cibuildwheel/frontend.py index 79796a1b..18157cdd 100644 --- a/cibuildwheel/frontend.py +++ b/cibuildwheel/frontend.py @@ -1,7 +1,7 @@ +import dataclasses import shlex import typing from collections.abc import Sequence -from dataclasses import dataclass from typing import Literal, Self, get_args from .logger import log @@ -10,7 +10,7 @@ from .util.helpers import parse_key_value_string BuildFrontendName = Literal["pip", "build", "build[uv]"] -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class BuildFrontendConfig: name: BuildFrontendName args: Sequence[str] = () diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py index ebfe38fc..59a1ca7e 100644 --- a/cibuildwheel/oci_container.py +++ b/cibuildwheel/oci_container.py @@ -1,3 +1,4 @@ +import dataclasses import io import json import os @@ -10,7 +11,6 @@ import textwrap import typing import uuid from collections.abc import Mapping, Sequence -from dataclasses import dataclass, field from enum import Enum from pathlib import Path, PurePath, PurePosixPath from types import TracebackType @@ -37,10 +37,11 @@ class OCIPlatform(Enum): S390X = "linux/s390x" -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True) class OCIContainerEngineConfig: name: ContainerEngineName - create_args: tuple[str, ...] = field(default_factory=tuple) + _: dataclasses.KW_ONLY + create_args: tuple[str, ...] = dataclasses.field(default_factory=tuple) disable_host_mount: bool = False @classmethod diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index 5a9c96c2..7c98f1e2 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -51,7 +51,7 @@ MUSLLINUX_ARCHS: Final[tuple[str, ...]] = ( ) -@dataclasses.dataclass +@dataclasses.dataclass(kw_only=True) class CommandLineArguments: platform: Literal["auto", "linux", "macos", "windows"] | None archs: str | None @@ -80,7 +80,7 @@ class CommandLineArguments: ) -@dataclasses.dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class GlobalOptions: package_dir: Path output_dir: Path @@ -90,7 +90,7 @@ class GlobalOptions: allow_empty: bool -@dataclasses.dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class BuildOptions: globals: GlobalOptions environment: ParsedEnvironment diff --git a/cibuildwheel/platforms/ios.py b/cibuildwheel/platforms/ios.py index dc4911f7..1235d92d 100644 --- a/cibuildwheel/platforms/ios.py +++ b/cibuildwheel/platforms/ios.py @@ -1,5 +1,6 @@ from __future__ import annotations +import dataclasses import os import shlex import shutil @@ -7,7 +8,6 @@ import subprocess import sys import textwrap from collections.abc import Sequence, Set -from dataclasses import dataclass from pathlib import Path from typing import assert_never @@ -42,7 +42,7 @@ from ..venv import constraint_flags, virtualenv from .macos import install_cpython as install_build_cpython -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class PythonConfiguration: version: str identifier: str diff --git a/cibuildwheel/platforms/linux.py b/cibuildwheel/platforms/linux.py index 5ffd5a2e..f31529cb 100644 --- a/cibuildwheel/platforms/linux.py +++ b/cibuildwheel/platforms/linux.py @@ -1,10 +1,10 @@ import contextlib +import dataclasses import subprocess import sys import textwrap from collections import OrderedDict from collections.abc import Iterable, Iterator, Sequence, Set -from dataclasses import dataclass from pathlib import Path, PurePath, PurePosixPath from typing import assert_never @@ -32,7 +32,7 @@ ARCHITECTURE_OCI_PLATFORM_MAP = { } -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class PythonConfiguration: version: str identifier: str @@ -43,7 +43,7 @@ class PythonConfiguration: return PurePosixPath(self.path_str) -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class BuildStep: platform_configs: list[PythonConfiguration] platform_tag: str diff --git a/cibuildwheel/platforms/macos.py b/cibuildwheel/platforms/macos.py index d6491736..1975c663 100644 --- a/cibuildwheel/platforms/macos.py +++ b/cibuildwheel/platforms/macos.py @@ -1,3 +1,4 @@ +import dataclasses import functools import inspect import os @@ -8,7 +9,6 @@ import subprocess import sys import typing from collections.abc import Set -from dataclasses import dataclass from pathlib import Path from typing import Literal, assert_never @@ -76,7 +76,7 @@ def get_macos_sdks() -> list[str]: return [m.group(1) for m in re.finditer(r"-sdk (macosx\S+)", output)] -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class PythonConfiguration: version: str identifier: str diff --git a/cibuildwheel/platforms/pyodide.py b/cibuildwheel/platforms/pyodide.py index d4ee9a92..3159acd6 100644 --- a/cibuildwheel/platforms/pyodide.py +++ b/cibuildwheel/platforms/pyodide.py @@ -1,3 +1,4 @@ +import dataclasses import functools import json import os @@ -6,7 +7,6 @@ import sys import tomllib import typing from collections.abc import Set -from dataclasses import dataclass from pathlib import Path from tempfile import TemporaryDirectory from typing import Final, TypedDict @@ -41,7 +41,7 @@ from ..venv import constraint_flags, virtualenv IS_WIN: Final[bool] = sys.platform.startswith("win") -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class PythonConfiguration: version: str identifier: str diff --git a/cibuildwheel/platforms/windows.py b/cibuildwheel/platforms/windows.py index 52e081f7..ce2d312d 100644 --- a/cibuildwheel/platforms/windows.py +++ b/cibuildwheel/platforms/windows.py @@ -1,3 +1,4 @@ +import dataclasses import json import os import platform as platform_module @@ -5,7 +6,6 @@ import shutil import subprocess import textwrap from collections.abc import MutableMapping, Set -from dataclasses import dataclass from functools import cache from pathlib import Path from typing import assert_never @@ -51,7 +51,7 @@ def get_nuget_args( ] -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class PythonConfiguration: version: str arch: str diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py index 25f61176..42f4104b 100644 --- a/cibuildwheel/selector.py +++ b/cibuildwheel/selector.py @@ -1,5 +1,5 @@ +import dataclasses import itertools -from dataclasses import dataclass from enum import StrEnum from fnmatch import fnmatch from typing import Any @@ -58,7 +58,7 @@ class EnableGroup(StrEnum): return frozenset(result) -@dataclass(frozen=True, kw_only=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class BuildSelector: """ This class holds a set of build/skip patterns. You call an instance with a @@ -113,7 +113,7 @@ class BuildSelector: } -@dataclass(frozen=True) +@dataclasses.dataclass(frozen=True, kw_only=True) class TestSelector: """ A build selector that can only skip tests according to a skip pattern. diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py index b13ebc6b..295bc091 100644 --- a/cibuildwheel/util/packaging.py +++ b/cibuildwheel/util/packaging.py @@ -11,7 +11,7 @@ from .cmd import call from .helpers import parse_key_value_string, unwrap -@dataclass() +@dataclass(kw_only=True) class DependencyConstraints: base_file_path: Path | None = None packages: list[str] = field(default_factory=list) diff --git a/unit_test/get_platform_test.py b/unit_test/get_platform_test.py index 9bd46cd9..320b73ae 100644 --- a/unit_test/get_platform_test.py +++ b/unit_test/get_platform_test.py @@ -1,6 +1,8 @@ import contextlib import sys +from collections.abc import Generator from pathlib import Path +from typing import TYPE_CHECKING import pytest import setuptools._distutils.util @@ -10,23 +12,27 @@ from cibuildwheel.errors import FatalError from cibuildwheel.platforms.windows import PythonConfiguration, setup_setuptools_cross_compile # monkeypatching os.name is too flaky. E.g. It works on my machine, but fails in pipeline -if not sys.platform.startswith("win"): +if not sys.platform.startswith("win") and not TYPE_CHECKING: pytest.skip("Windows-only tests", allow_module_level=True) @contextlib.contextmanager -def patched_environment(monkeypatch: pytest.MonkeyPatch, environment: dict[str, str]): +def patched_environment( + monkeypatch: pytest.MonkeyPatch, environment: dict[str, str] +) -> Generator[None, None, None]: with monkeypatch.context() as mp: for envvar, val in environment.items(): mp.setenv(name=envvar, value=val) yield -def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): +def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: arch = "32" environment: dict[str, str] = {} - configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + configuration = PythonConfiguration( + version="irrelevant", arch=arch, identifier="irrelevant", url=None + ) setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) with patched_environment(monkeypatch, environment): @@ -36,11 +42,13 @@ def test_x86(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): assert target_platform == "win32" -def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): +def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: arch = "64" environment: dict[str, str] = {} - configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + configuration = PythonConfiguration( + version="irrelevant", arch=arch, identifier="irrelevant", url=None + ) setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) with patched_environment(monkeypatch, environment): @@ -53,11 +61,13 @@ def test_x64(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): @pytest.mark.skipif( detect_ci_provider() == CIProvider.azure_pipelines, reason="arm64 not recognised on azure" ) -def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): +def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: arch = "ARM64" environment: dict[str, str] = {} - configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + configuration = PythonConfiguration( + version="irrelevant", arch=arch, identifier="irrelevant", url=None + ) setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) with patched_environment(monkeypatch, environment): @@ -67,21 +77,25 @@ def test_arm(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): assert target_platform == "win-arm64" -def test_env_set(tmp_path: Path): +def test_env_set(tmp_path: Path) -> None: arch = "32" environment = {"VSCMD_ARG_TGT_ARCH": "x64"} - configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + configuration = PythonConfiguration( + version="irrelevant", arch=arch, identifier="irrelevant", url=None + ) with pytest.raises(FatalError, match="VSCMD_ARG_TGT_ARCH"): setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) -def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): +def test_env_blank(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: arch = "32" environment = {"VSCMD_ARG_TGT_ARCH": ""} - configuration = PythonConfiguration("irrelevant", arch, "irrelevant", None) + configuration = PythonConfiguration( + version="irrelevant", arch=arch, identifier="irrelevant", url=None + ) setup_setuptools_cross_compile(tmp_path, configuration, tmp_path, environment) with patched_environment(monkeypatch, environment):