diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 36ed2511b..aa07602eb 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/bin/update_pythons.py b/bin/update_pythons.py index 35f6a76ed..e57a71735 100755 --- a/bin/update_pythons.py +++ b/bin/update_pythons.py @@ -5,7 +5,6 @@ import copy import difflib import logging -import sys from collections.abc import Mapping, MutableMapping from pathlib import Path from typing import Any, Union @@ -13,19 +12,14 @@ 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") diff --git a/bin/update_virtualenv.py b/bin/update_virtualenv.py index d7c7dc8ae..a00724063 100755 --- a/bin/update_virtualenv.py +++ b/bin/update_virtualenv.py @@ -5,23 +5,17 @@ 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") diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 9dfba1512..3682fad49 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -16,15 +16,11 @@ 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, - GenericPythonConfiguration, - PlatformName, - assert_never, -) +from cibuildwheel.typing import PLATFORMS, GenericPythonConfiguration, PlatformName from cibuildwheel.util import ( CIBW_CACHE_PATH, BuildSelector, @@ -244,7 +240,7 @@ def _compute_platform(args: CommandLineArguments) -> PlatformName: return _compute_platform_ci() -class PlatformModule(typing.Protocol): +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( diff --git a/cibuildwheel/_compat/__init__.py b/cibuildwheel/_compat/__init__.py new file mode 100644 index 000000000..9d48db4f9 --- /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 100% rename from cibuildwheel/functools_cached_property_38.py rename to cibuildwheel/_compat/_functools_cached_property_38.py diff --git a/cibuildwheel/_compat/functools.py b/cibuildwheel/_compat/functools.py new file mode 100644 index 000000000..8fe86a3e4 --- /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 000000000..f3b30fe65 --- /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 000000000..62495adf9 --- /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 8c4b22939..9d9af38b8 100644 --- a/cibuildwheel/architecture.py +++ b/cibuildwheel/architecture.py @@ -7,7 +7,8 @@ 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"} diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py index 2a7b99a2a..3117b54ed 100644 --- a/cibuildwheel/environment.py +++ b/cibuildwheel/environment.py @@ -7,9 +7,8 @@ import bashlex import bashlex.errors -from cibuildwheel.typing import Protocol - from . import bashlex_eval +from ._compat.typing import Protocol class EnvironmentParseError(Exception): diff --git a/cibuildwheel/extra.py b/cibuildwheel/extra.py index 03e3ce6f0..5ac9ea1ff 100644 --- a/cibuildwheel/extra.py +++ b/cibuildwheel/extra.py @@ -7,7 +7,7 @@ from collections.abc import Mapping, Sequence from io import StringIO -from .typing import Protocol +from ._compat.typing import Protocol __all__ = ("Printable", "dump_python_configurations") diff --git a/cibuildwheel/linux.py b/cibuildwheel/linux.py index 4f019ac34..6fd393dc5 100644 --- a/cibuildwheel/linux.py +++ b/cibuildwheel/linux.py @@ -8,11 +8,12 @@ from pathlib import Path, PurePath, PurePosixPath 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, diff --git a/cibuildwheel/logger.py b/cibuildwheel/logger.py index 014f3b00d..bc63a3aaf 100644 --- a/cibuildwheel/logger.py +++ b/cibuildwheel/logger.py @@ -7,8 +7,8 @@ import time from typing import IO, AnyStr -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 = { diff --git a/cibuildwheel/macos.py b/cibuildwheel/macos.py index 75614449b..1c103629d 100644 --- a/cibuildwheel/macos.py +++ b/cibuildwheel/macos.py @@ -16,11 +16,12 @@ 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, diff --git a/cibuildwheel/oci_container.py b/cibuildwheel/oci_container.py index aa54eefbf..b2f6fe4af 100644 --- a/cibuildwheel/oci_container.py +++ b/cibuildwheel/oci_container.py @@ -15,9 +15,9 @@ from types import TracebackType from typing import IO, Dict -from cibuildwheel.util import CIProvider, detect_ci_provider - -from .typing import Literal, PathOrStr, PopenBytes +from ._compat.typing import Literal +from .typing import PathOrStr, PopenBytes +from .util import CIProvider, detect_ci_provider ContainerEngine = Literal["docker", "podman"] diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index d28e76214..736f13a73 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -15,19 +15,16 @@ from pathlib import Path from typing import Any, Dict, List, Union -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - 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 .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, diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py index 0b87415b6..95dc150d8 100644 --- a/cibuildwheel/projectfiles.py +++ b/cibuildwheel/projectfiles.py @@ -7,10 +7,7 @@ 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/typing.py b/cibuildwheel/typing.py index 9374401cb..7abd9e3cf 100644 --- a/cibuildwheel/typing.py +++ b/cibuildwheel/typing.py @@ -2,37 +2,21 @@ 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: diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py index 2ba4fe6a1..8b5f9f620 100644 --- a/cibuildwheel/util.py +++ b/cibuildwheel/util.py @@ -23,12 +23,6 @@ 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 @@ -36,7 +30,10 @@ 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", @@ -661,12 +658,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]: diff --git a/cibuildwheel/windows.py b/cibuildwheel/windows.py index caee9ce69..d3e587868 100644 --- a/cibuildwheel/windows.py +++ b/cibuildwheel/windows.py @@ -16,11 +16,12 @@ 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, diff --git a/pyproject.toml b/pyproject.toml index 570772db4..90420fcaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -149,7 +149,7 @@ extend-ignore = [ "PT007", # False positive (fixed upstream) ] target-version = "py37" -typing-modules = ["cibuildwheel.typing"] +typing-modules = ["cibuildwheel._compat.typing"] flake8-unused-arguments.ignore-variadic-names = true [tool.ruff.flake8-tidy-imports.banned-api] @@ -158,6 +158,15 @@ flake8-unused-arguments.ignore-variadic-names = true "typing.Iterator".msg = "Use collections.abc.Iterator instead." "typing.Sequence".msg = "Use collections.abc.Sequence instead." "typing.Set".msg = "Use collections.abc.Set instead." +"typing.Protocol".msg = "Use cibuildwheel._compat.typing.Protocol instead." +"typing.Final".msg = "Use cibuildwheel._compat.typing.Final instead." +"typing.Literal".msg = "Use cibuildwheel._compat.typing.Literal instead." +"typing.OrderedDict".msg = "Use cibuildwheel._compat.typing.OrderedDict instead." +"typing.TypedDict".msg = "Use cibuildwheel._compat.typing.TypedDict instead." +"typing.NotRequired".msg = "Use cibuildwheel._compat.typing.NotRequired instead." +"typing.assert_never".msg = "Use cibuildwheel._compat.typing.assert_never instead." +"tomllib".msg = "Use cibuildwheel._compat.tomllib instead." +"tomli".msg = "Use cibuildwheel._compat.tomllib instead." [tool.ruff.per-file-ignores] "unit_test/*" = ["PLC1901"] diff --git a/unit_test/build_ids_test.py b/unit_test/build_ids_test.py index 0f62ba408..a42af93de 100644 --- a/unit_test/build_ids_test.py +++ b/unit_test/build_ids_test.py @@ -1,14 +1,8 @@ from __future__ import annotations -import sys - -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - from packaging.version import Version +from cibuildwheel._compat import tomllib from cibuildwheel.extra import Printable, dump_python_configurations from cibuildwheel.util import resources_dir diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index 6cda8967b..8d2010ee3 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -6,12 +6,8 @@ import pytest -if sys.version_info >= (3, 11): - import tomllib -else: - import tomli as tomllib - from cibuildwheel.__main__ import main +from cibuildwheel._compat import tomllib from cibuildwheel.environment import ParsedEnvironment from cibuildwheel.options import BuildOptions, _get_pinned_container_images from cibuildwheel.util import BuildSelector, resources_dir, split_config_settings