chore: use kw_only (Python 3.10+) on most dataclasses (#2422)

* chore: use kw_only (Python 3.10+) on many dataclasses

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

* fix: expose windows file to type checker

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>

---------

Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
Henry Schreiner
2025-05-28 10:41:26 +01:00
committed by GitHub
parent e18ab66cca
commit db2d59be22
16 changed files with 59 additions and 44 deletions
+2 -2
View File
@@ -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]
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+1 -1
View File
@@ -102,7 +102,7 @@ class EnvironmentAssignmentBash:
return False
@dataclasses.dataclass
@dataclasses.dataclass(kw_only=True)
class ParsedEnvironment:
assignments: list[EnvironmentAssignment]
+2 -2
View File
@@ -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] = ()
+4 -3
View File
@@ -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
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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.
+1 -1
View File
@@ -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)
+26 -12
View File
@@ -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):