From 097806b6b1bd3cdd7b139ac63b20f85f76d72082 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Wed, 1 Apr 2026 10:23:02 -0400 Subject: [PATCH] tests: fully type the test suite (#2794) * tests: fully type the test suite * chore: require more typing Signed-off-by: Henry Schreiner * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Henry Schreiner Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- cibuildwheel/__main__.py | 2 +- cibuildwheel/environment.py | 2 +- cibuildwheel/options.py | 6 +- cibuildwheel/selector.py | 6 +- cibuildwheel/util/packaging.py | 4 +- docs/main.py | 2 +- pyproject.toml | 8 +- test/conftest.py | 2 +- test/test_0_basic.py | 17 +- test/test_abi_variants.py | 7 +- test/test_android.py | 66 ++++--- test/test_before_all.py | 7 +- test/test_before_build.py | 7 +- test/test_before_test.py | 8 +- test/test_build_frontend_args.py | 5 +- test/test_build_skip.py | 3 +- test/test_container_engine.py | 8 +- test/test_container_images.py | 3 +- test/test_cpp_standards.py | 8 +- test/test_custom_repair_wheel.py | 5 +- test/test_dependency_versions.py | 12 +- test/test_emulation.py | 7 +- test/test_environment.py | 7 +- test/test_from_sdist.py | 10 +- test/test_ios.py | 36 ++-- test/test_linux_python.py | 3 +- test/test_macos_archs.py | 30 +++- test/test_manylinuxXXXX_only.py | 3 +- test/test_meson_python.py | 4 +- test/test_musllinux_X_Y_only.py | 3 +- test/test_pep518.py | 3 +- test/test_pure_wheel.py | 3 +- test/test_pyodide.py | 13 +- test/test_ssl.py | 3 +- test/test_subdir_package.py | 3 +- test/test_testing.py | 20 +-- test/test_troubleshooting.py | 14 +- test/test_wheel_tag.py | 4 +- test/test_windows.py | 4 +- test/utils.py | 4 +- unit_test/architecture_test.py | 21 ++- unit_test/build_ids_test.py | 4 +- unit_test/build_selector_test.py | 30 ++-- unit_test/conftest.py | 6 +- unit_test/download_test.py | 7 +- unit_test/environment_test.py | 22 +-- unit_test/main_commands_test.py | 23 ++- unit_test/main_tests/conftest.py | 28 +-- unit_test/main_tests/main_options_test.py | 162 ++++++++++++++---- unit_test/main_tests/main_platform_test.py | 79 +++++++-- .../main_tests/main_requires_python_test.py | 23 ++- unit_test/oci_container_test.py | 41 +++-- unit_test/option_prepare_test.py | 16 +- unit_test/options_test.py | 12 +- unit_test/options_toml_test.py | 65 +++---- unit_test/projectfiles_test.py | 31 ++-- unit_test/utils_test.py | 30 ++-- unit_test/wheel_print_test.py | 4 +- 58 files changed, 627 insertions(+), 339 deletions(-) diff --git a/cibuildwheel/__main__.py b/cibuildwheel/__main__.py index 5b03265f..2f6e8d36 100644 --- a/cibuildwheel/__main__.py +++ b/cibuildwheel/__main__.py @@ -52,7 +52,7 @@ class Unbuffered: self.stream.writelines(data) self.stream.flush() - def __getattr__(self, attr: str) -> Any: + def __getattr__(self, attr: str) -> Any: # noqa: ANN401 return getattr(self.stream, attr) diff --git a/cibuildwheel/environment.py b/cibuildwheel/environment.py index 7e371b28..c9e53412 100644 --- a/cibuildwheel/environment.py +++ b/cibuildwheel/environment.py @@ -131,7 +131,7 @@ class ParsedEnvironment: def __repr__(self) -> str: return f"{self.__class__.__name__}({[repr(a) for a in self.assignments]!r})" - def options_summary(self) -> Any: + def options_summary(self) -> list[EnvironmentAssignment]: return self.assignments diff --git a/cibuildwheel/options.py b/cibuildwheel/options.py index d1efbbdc..705c2db0 100644 --- a/cibuildwheel/options.py +++ b/cibuildwheel/options.py @@ -987,8 +987,8 @@ class Options: def option_summary( self, option_name: str, - option_value: Any, - default_value: Any, + option_value: Any, # noqa: ANN401 + default_value: Any, # noqa: ANN401 overrides: Mapping[str, Any] | None = None, skip_unset: bool = False, ) -> str | None: @@ -1040,7 +1040,7 @@ class Options: return value @staticmethod - def option_summary_value(option_value: Any) -> str: + def option_summary_value(option_value: Any) -> str: # noqa: ANN401 if hasattr(option_value, "options_summary"): option_value = option_value.options_summary() diff --git a/cibuildwheel/selector.py b/cibuildwheel/selector.py index 609fe3e7..299a725e 100644 --- a/cibuildwheel/selector.py +++ b/cibuildwheel/selector.py @@ -2,7 +2,7 @@ import dataclasses import itertools from enum import StrEnum from fnmatch import fnmatch -from typing import Any, Self +from typing import Self import bracex from packaging.specifiers import SpecifierSet @@ -113,7 +113,7 @@ class BuildSelector: return should_build and not should_skip - def options_summary(self) -> Any: + def options_summary(self) -> dict[str, str | list[str]]: return { "build_config": self.build_config, "skip_config": self.skip_config, @@ -134,5 +134,5 @@ class TestSelector: should_skip = selector_matches(self.skip_config, build_id) return not should_skip - def options_summary(self) -> Any: + def options_summary(self) -> dict[str, str]: return {"skip_config": self.skip_config} diff --git a/cibuildwheel/util/packaging.py b/cibuildwheel/util/packaging.py index a068e829..82e3494c 100644 --- a/cibuildwheel/util/packaging.py +++ b/cibuildwheel/util/packaging.py @@ -2,7 +2,7 @@ import shlex from collections.abc import Mapping, Sequence from dataclasses import dataclass, field from pathlib import Path, PurePath -from typing import Any, Literal, Self, TypeVar +from typing import Literal, Self, TypeVar from packaging.utils import parse_wheel_filename @@ -102,7 +102,7 @@ class DependencyConstraints: return None - def options_summary(self) -> Any: + def options_summary(self) -> str | dict[str, str]: if self == DependencyConstraints.pinned(): return "pinned" elif self.packages: diff --git a/docs/main.py b/docs/main.py index f8dc1125..ce79df3a 100644 --- a/docs/main.py +++ b/docs/main.py @@ -8,7 +8,7 @@ import rich.console import rich.text -def define_env(env: Any) -> None: +def define_env(env: Any) -> None: # noqa: ANN401 "Hook function for mkdocs-macros" @env.macro # type: ignore[untyped-decorator] diff --git a/pyproject.toml b/pyproject.toml index 7fd4b49f..dbac2cdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,14 +129,9 @@ files = [ ] warn_unused_configs = true strict = true -disallow_untyped_defs = false enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] warn_unreachable = false -[[tool.mypy.overrides]] -module = "cibuildwheel.*" -disallow_untyped_defs = true - [[tool.mypy.overrides]] module = [ "setuptools._distutils", # needed even if only directly import setuptools._distutils.util @@ -196,6 +191,7 @@ extend-select = [ "B", # flake8-bugbear "I", # isort "ARG", # flake8-unused-arguments + "ANN", # flake8-annotations "C4", # flake8-comprehensions "EM", # flake8-errmsg "ICN", # flake8-import-conventions @@ -236,6 +232,8 @@ ignore = [ ] flake8-unused-arguments.ignore-variadic-names = true flake8-tidy-imports.ban-relative-imports = "all" +flake8-annotations.allow-star-arg-any = true +flake8-annotations.mypy-init-return = true [tool.ruff.lint.flake8-tidy-imports.banned-api] "typing.Mapping".msg = "Use collections.abc.Mapping instead." diff --git a/test/conftest.py b/test/conftest.py index 2a80e770..64c3208a 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -46,7 +46,7 @@ def pytest_addoption(parser: pytest.Parser) -> None: ) -def pytest_configure(config): +def pytest_configure(config: pytest.Config) -> None: flag_enable = config.getoption("--enable") flag_platform = config.getoption("--platform") diff --git a/test/test_0_basic.py b/test/test_0_basic.py index 2524d628..6c805b1b 100644 --- a/test/test_0_basic.py +++ b/test/test_0_basic.py @@ -1,4 +1,5 @@ import textwrap +from pathlib import Path import packaging.utils import pytest @@ -21,7 +22,7 @@ basic_project = test_projects.new_c_project( @pytest.mark.serial -def test_dummy_serial(): +def test_dummy_serial() -> None: """A no-op test to ensure that at least one serial test is always found. Without this no-op test, CI fails on CircleCI because no serial tests are @@ -29,7 +30,9 @@ def test_dummy_serial(): """ -def test(tmp_path, build_frontend_env, capfd): +def test( + tmp_path: Path, build_frontend_env: dict[str, str], capfd: pytest.CaptureFixture[str] +) -> None: project_dir = tmp_path / "project" basic_project.generate(project_dir) @@ -52,7 +55,7 @@ def test(tmp_path, build_frontend_env, capfd): @pytest.mark.skip(reason="to keep test output clean") -def test_sample_build(tmp_path, capfd): +def test_sample_build(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: project_dir = tmp_path / "project" basic_project.generate(project_dir) @@ -70,7 +73,9 @@ def test_sample_build(tmp_path, capfd): @pytest.mark.parametrize( "enable_setting", ["", "cpython-prerelease", "pypy", "cpython-freethreading"] ) -def test_build_identifiers(tmp_path, enable_setting, monkeypatch): +def test_build_identifiers( + tmp_path: Path, enable_setting: str, monkeypatch: pytest.MonkeyPatch +) -> None: project_dir = tmp_path / "project" basic_project.generate(project_dir) @@ -93,7 +98,9 @@ def test_build_identifiers(tmp_path, enable_setting, monkeypatch): (None, {"CIBW_ALLOW_EMPTY": "1"}), ], ) -def test_allow_empty(tmp_path, add_args, env_allow_empty): +def test_allow_empty( + tmp_path: Path, add_args: list[str] | None, env_allow_empty: dict[str, str] +) -> None: project_dir = tmp_path / "project" basic_project.generate(project_dir) diff --git a/test/test_abi_variants.py b/test/test_abi_variants.py index 999323ae..e38b3706 100644 --- a/test/test_abi_variants.py +++ b/test/test_abi_variants.py @@ -1,4 +1,7 @@ import textwrap +from pathlib import Path + +import pytest from . import test_projects, utils @@ -31,7 +34,7 @@ limited_api_project = test_projects.new_c_project( limited_api_project.files["pyproject.toml"] = pyproject_toml -def test_abi3(tmp_path): +def test_abi3(tmp_path: Path) -> None: project_dir = tmp_path / "project" limited_api_project.generate(project_dir) @@ -178,7 +181,7 @@ ctypes_project.files["test/add_test.py"] = textwrap.dedent( ctypes_project.files["pyproject.toml"] = pyproject_toml -def test_abi_none(tmp_path, capfd): +def test_abi_none(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: project_dir = tmp_path / "project" ctypes_project.generate(project_dir) diff --git a/test/test_android.py b/test/test_android.py index eb16e2b1..64bbbad1 100644 --- a/test/test_android.py +++ b/test/test_android.py @@ -2,6 +2,8 @@ import os import platform import re import sys +import typing +from collections.abc import Callable from dataclasses import dataclass from pathlib import Path from shutil import rmtree, which @@ -52,8 +54,10 @@ if "ANDROID_HOME" not in os.environ: # Many CI services don't support running the Android emulator: see platforms.md. supports_emulator = (not ci) or ("GITHUB_ACTIONS" in os.environ and platform.system() == "Linux") +T = typing.TypeVar("T", bound=Callable[..., typing.Any]) -def needs_emulator(test): + +def needs_emulator(test: T) -> T: # All copies of the testbed app run on the same emulator with the same # application ID, so these tests must be run serially. test = pytest.mark.serial(test) @@ -85,7 +89,7 @@ cp313_env = { } -def test_android_home(tmp_path, capfd): +def test_android_home(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: new_c_project().generate(tmp_path) env = os.environ.copy() del env["ANDROID_HOME"] @@ -100,7 +104,7 @@ def test_android_home(tmp_path, capfd): # a build, which is marked as serial so it will run before the parallel tests, but isn't # marked as needs_emulator so it will run on all CI platforms. @pytest.mark.serial -def test_expected_wheels(tmp_path, spam_env): +def test_expected_wheels(tmp_path: Path, spam_env: dict[str, str]) -> None: # Since this test covers all Python versions, check the cross venv. test_module = "_cross_venv_test_android" project = new_c_project(setup_py_add=f"import {test_module}") @@ -119,7 +123,7 @@ def test_expected_wheels(tmp_path, spam_env): @needs_emulator -def test_frontend_good(tmp_path, build_frontend_env): +def test_frontend_good(tmp_path: Path, build_frontend_env: dict[str, str]) -> None: new_c_project().generate(tmp_path) wheels = cibuildwheel_run( tmp_path, @@ -129,7 +133,7 @@ def test_frontend_good(tmp_path, build_frontend_env): @pytest.mark.parametrize("frontend", ["pip"]) -def test_frontend_bad(frontend, tmp_path, capfd): +def test_frontend_bad(frontend: str, tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: new_c_project().generate(tmp_path) with pytest.raises(CalledProcessError): cibuildwheel_run( @@ -140,7 +144,7 @@ def test_frontend_bad(frontend, tmp_path, capfd): @needs_emulator -def test_archs(tmp_path, capfd): +def test_archs(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: new_c_project().generate(tmp_path) # Build all architectures while checking the handling of the `before` commands. @@ -189,7 +193,7 @@ def test_archs(tmp_path, capfd): pytest.fail(f"Unexpected line: {line!r}") -def test_build_requires(tmp_path, capfd): +def test_build_requires(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # Build-time requirements should be installed for the build platform, not for Android. Prove # this by installing some non-pure-Python requirements and using them in setup.py. # @@ -226,13 +230,13 @@ def test_build_requires(tmp_path, capfd): @pytest.fixture -def spam_env(tmp_path): +def spam_env(tmp_path: Path) -> dict[str, str]: project = new_c_project() project.files["test_spam.py"] = dedent( """\ import spam - def test_spam(): + def test_spam() -> None: assert spam.filter("ham") assert not spam.filter("spam") print("Spam test passed") @@ -240,7 +244,7 @@ def spam_env(tmp_path): ) project.files["test_empty.py"] = dedent( """\ - def test_empty(): + def test_empty() -> None: pass """ ) @@ -265,7 +269,13 @@ def spam_env(tmp_path): ("pytest test_spam.py", "=== 1 passed in "), ], ) -def test_test_command_good(command, expected_output, tmp_path, spam_env, capfd): +def test_test_command_good( + command: str, + expected_output: str, + tmp_path: Path, + spam_env: dict[str, str], + capfd: pytest.CaptureFixture[str], +) -> None: cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command}) stdout, stderr = capfd.readouterr() assert expected_output in stdout @@ -300,7 +310,13 @@ BAD_PLACEHOLDER_ERROR = ( ("pytest test_ham.py", "not found: test_ham.py"), ], ) -def test_test_command_bad(command, expected_output, tmp_path, spam_env, capfd): +def test_test_command_bad( + command: str, + expected_output: str, + tmp_path: Path, + spam_env: dict[str, str], + capfd: pytest.CaptureFixture[str], +) -> None: with pytest.raises(CalledProcessError): cibuildwheel_run(tmp_path, add_env={**spam_env, "CIBW_TEST_COMMAND": command}) assert expected_output in capfd.readouterr().err @@ -314,7 +330,9 @@ def test_test_command_bad(command, expected_output, tmp_path, spam_env, capfd): ("-E", 1), ], ) -def test_test_command_python_options(options, expected, tmp_path, capfd): +def test_test_command_python_options( + options: str, expected: int, tmp_path: Path, capfd: pytest.CaptureFixture[str] +) -> None: project = new_c_project() project.generate(tmp_path) @@ -330,7 +348,9 @@ def test_test_command_python_options(options, expected, tmp_path, capfd): @needs_emulator -def test_package_subdir(tmp_path, spam_env, capfd): +def test_package_subdir( + tmp_path: Path, spam_env: dict[str, str], capfd: pytest.CaptureFixture[str] +) -> None: spam_paths = list(tmp_path.iterdir()) package_dir = tmp_path / "package" package_dir.mkdir() @@ -345,7 +365,7 @@ def test_package_subdir(tmp_path, spam_env, capfd): @needs_emulator -def test_no_test_sources(tmp_path, capfd): +def test_no_test_sources(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: new_c_project().generate(tmp_path) with pytest.raises(CalledProcessError): cibuildwheel_run( @@ -359,17 +379,17 @@ def test_no_test_sources(tmp_path, capfd): @needs_emulator -def test_environment_markers(tmp_path): +def test_environment_markers(tmp_path: Path) -> None: project = new_c_project() test_filename = "test_environment_markers.py" project.files[test_filename] = dedent( """\ import pytest - def test_android(): + def test_android() -> None: import certifi - def test_not_android(): + def test_not_android() -> None: try: import platformdirs except ImportError: @@ -392,7 +412,7 @@ def test_environment_markers(tmp_path): @needs_emulator -def test_verbosity(tmp_path, capfd): +def test_verbosity(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: new_c_project().generate(tmp_path) test_env = { **cp313_env, @@ -418,7 +438,7 @@ def test_verbosity(tmp_path, capfd): @needs_emulator -def test_api_level(tmp_path, capfd): +def test_api_level(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: project = new_c_project() project.files["pyproject.toml"] = dedent( """\ @@ -450,7 +470,7 @@ def test_api_level(tmp_path, capfd): @needs_emulator -def test_libcxx(tmp_path, capfd): +def test_libcxx(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: project_dir = tmp_path / "project" output_dir = tmp_path / "output" @@ -499,7 +519,7 @@ def test_libcxx(tmp_path, capfd): @needs_emulator -def test_setuptools_rust(tmp_path, capfd): +def test_setuptools_rust(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """ Test Android cross-compilation using the setuptools-rust toolchain. """ @@ -584,7 +604,7 @@ def test_setuptools_rust(tmp_path, capfd): @needs_emulator -def test_maturin(tmp_path, capfd): +def test_maturin(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """ Test Android cross-compilation using the maturin backend. """ diff --git a/test/test_before_all.py b/test/test_before_all.py index f0c89972..156b3850 100644 --- a/test/test_before_all.py +++ b/test/test_before_all.py @@ -1,5 +1,6 @@ import subprocess import textwrap +from pathlib import Path import pytest @@ -29,7 +30,7 @@ project_with_before_build_asserts = test_projects.new_c_project( ) -def test(tmp_path): +def test(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_before_build_asserts.generate(project_dir) @@ -61,7 +62,7 @@ def test(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_failing_command(tmp_path): +def test_failing_command(tmp_path: Path) -> None: project_dir = tmp_path / "project" test_projects.new_c_project().generate(project_dir) @@ -75,7 +76,7 @@ def test_failing_command(tmp_path): ) -def test_cwd(tmp_path): +def test_cwd(tmp_path: Path) -> None: project_dir = tmp_path / "project" test_projects.new_c_project().generate(project_dir) diff --git a/test/test_before_build.py b/test/test_before_build.py index 767bb06c..37cbe9ac 100644 --- a/test/test_before_build.py +++ b/test/test_before_build.py @@ -1,5 +1,6 @@ import subprocess import textwrap +from pathlib import Path import pytest @@ -36,7 +37,7 @@ project_with_before_build_asserts = test_projects.new_c_project( ) -def test(tmp_path): +def test(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_before_build_asserts.generate(project_dir) @@ -65,7 +66,7 @@ def test(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_failing_command(tmp_path): +def test_failing_command(tmp_path: Path) -> None: project_dir = tmp_path / "project" test_projects.new_c_project().generate(project_dir) @@ -79,7 +80,7 @@ def test_failing_command(tmp_path): ) -def test_cwd(tmp_path): +def test_cwd(tmp_path: Path) -> None: project_dir = tmp_path / "project" test_projects.new_c_project().generate(project_dir) diff --git a/test/test_before_test.py b/test/test_before_test.py index bf023849..d067edd7 100644 --- a/test/test_before_test.py +++ b/test/test_before_test.py @@ -1,3 +1,5 @@ +from pathlib import Path + from . import test_projects, utils before_test_project = test_projects.new_c_project() @@ -11,7 +13,7 @@ PROJECT_DIR = Path(__file__).parent.parent.resolve() class TestBeforeTest(TestCase): - def test_version(self): + def test_version(self) -> None: # assert that the Python version as written to pythonversion_bt.txt in the CIBW_BEFORE_TEST step # is the same one as is currently running. # because of use symlinks in MacOS run this test is also need @@ -20,7 +22,7 @@ class TestBeforeTest(TestCase): print('sys.version', sys.version) assert stored_version == sys.version - def test_prefix(self): + def test_prefix(self) -> None: # check that the prefix also was written stored_prefix = PROJECT_DIR.joinpath('pythonprefix_bt.txt').read_text() print('stored_prefix', stored_prefix) @@ -32,7 +34,7 @@ class TestBeforeTest(TestCase): """ -def test(tmp_path, build_frontend_env): +def test(tmp_path: Path, build_frontend_env: dict[str, str]) -> None: project_dir = tmp_path / "project" before_test_project.generate(project_dir) test_project_dir = project_dir / "dependency" diff --git a/test/test_build_frontend_args.py b/test/test_build_frontend_args.py index 8f5732a1..4940e02f 100644 --- a/test/test_build_frontend_args.py +++ b/test/test_build_frontend_args.py @@ -1,4 +1,5 @@ import subprocess +from pathlib import Path import pytest @@ -12,7 +13,9 @@ from . import test_projects, utils "build", ], ) -def test_build_frontend_args(tmp_path, capfd, frontend_name): +def test_build_frontend_args( + tmp_path: Path, capfd: pytest.CaptureFixture[str], frontend_name: str +) -> None: project = test_projects.new_c_project() project_dir = tmp_path / "project" project.generate(project_dir) diff --git a/test/test_build_skip.py b/test/test_build_skip.py index ad7d6f9d..d9b1bf32 100644 --- a/test/test_build_skip.py +++ b/test/test_build_skip.py @@ -1,4 +1,5 @@ import textwrap +from pathlib import Path from . import test_projects, utils @@ -15,7 +16,7 @@ project_with_skip_asserts = test_projects.new_c_project( ) -def test(tmp_path): +def test(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_skip_asserts.generate(project_dir) diff --git a/test/test_container_engine.py b/test/test_container_engine.py index 510916f0..68968dfd 100644 --- a/test/test_container_engine.py +++ b/test/test_container_engine.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from . import test_projects, utils @@ -5,7 +7,9 @@ from . import test_projects, utils basic_project = test_projects.new_c_project() -def test_podman(tmp_path, capfd, request): +def test_podman( + tmp_path: Path, capfd: pytest.CaptureFixture[str], request: pytest.FixtureRequest +) -> None: if utils.get_platform() != "linux": pytest.skip("the test is only relevant to the linux build") @@ -35,7 +39,7 @@ def test_podman(tmp_path, capfd, request): assert "test log statement from before-all" in captured.out -def test_create_args(tmp_path, capfd): +def test_create_args(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: if utils.get_platform() != "linux": pytest.skip("the test is only relevant to the linux build") diff --git a/test/test_container_images.py b/test/test_container_images.py index 02263c64..5a32bd4a 100644 --- a/test/test_container_images.py +++ b/test/test_container_images.py @@ -1,5 +1,6 @@ import platform import textwrap +from pathlib import Path import pytest @@ -22,7 +23,7 @@ dockcross_only_project = test_projects.new_c_project( @pytest.mark.usefixtures("docker_cleanup") -def test(tmp_path): +def test(tmp_path: Path) -> None: if utils.get_platform() != "linux": pytest.skip("the test is only relevant to the linux build") if platform.machine() not in ["x86_64", "i686"]: diff --git a/test/test_cpp_standards.py b/test/test_cpp_standards.py index fb7a0a84..deba8e64 100644 --- a/test/test_cpp_standards.py +++ b/test/test_cpp_standards.py @@ -1,3 +1,5 @@ +from pathlib import Path + import jinja2 from . import utils @@ -52,7 +54,7 @@ cpp_test_project.files["setup.py"] = jinja2.Template(setup_py_template) cpp_test_project.files["spam.cpp"] = jinja2.Template(spam_cpp_template) -def test_cpp11(tmp_path): +def test_cpp11(tmp_path: Path) -> None: # This test checks that the C++11 standard is supported project_dir = tmp_path / "project" cpp11_project = cpp_test_project.copy() @@ -68,7 +70,7 @@ def test_cpp11(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_cpp14(tmp_path): +def test_cpp14(tmp_path: Path) -> None: # This test checks that the C++14 standard is supported project_dir = tmp_path / "project" cpp14_project = cpp_test_project.copy() @@ -84,7 +86,7 @@ def test_cpp14(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_cpp17(tmp_path): +def test_cpp17(tmp_path: Path) -> None: # This test checks that the C++17 standard is supported project_dir = tmp_path / "project" cpp17_project = cpp_test_project.copy() diff --git a/test/test_custom_repair_wheel.py b/test/test_custom_repair_wheel.py index 83aaddd6..901f559c 100644 --- a/test/test_custom_repair_wheel.py +++ b/test/test_custom_repair_wheel.py @@ -1,6 +1,7 @@ import subprocess import textwrap from contextlib import nullcontext as does_not_raise +from pathlib import Path import pytest @@ -30,7 +31,7 @@ shutil.copy(wheel, dest) """ -def test(tmp_path, capfd): +def test(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # this test checks that a generated wheel name shall be unique in a given cibuildwheel run project_dir = tmp_path / "project" basic_project.generate(project_dir) @@ -68,7 +69,7 @@ def test(tmp_path, capfd): ], ids=["no-placeholder", "package-placeholder", "project-placeholder"], ) -def test_repair_wheel_command_structure(tmp_path, repair_command): +def test_repair_wheel_command_structure(tmp_path: Path, repair_command: str) -> None: project_dir = tmp_path / "project" project = test_projects.new_c_project() project.files["repair.py"] = textwrap.dedent(""" diff --git a/test/test_dependency_versions.py b/test/test_dependency_versions.py index 86dd6f22..855fd354 100644 --- a/test/test_dependency_versions.py +++ b/test/test_dependency_versions.py @@ -36,7 +36,9 @@ for name, expected_version in expected_versions.items(): """ -def test_check_versions_script(tmp_path, build_frontend_env_nouv, capfd): +def test_check_versions_script( + tmp_path: Path, build_frontend_env_nouv: dict[str, str], capfd: pytest.CaptureFixture[str] +) -> None: if utils.get_platform() == "linux": pytest.skip("we don't test dependency versions on linux, refer to other tests") @@ -76,7 +78,9 @@ def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]: @pytest.mark.parametrize("python_version", ["3.8", "3.12"]) -def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv): +def test_pinned_versions( + tmp_path: Path, python_version: str, build_frontend_env_nouv: dict[str, str] +) -> None: if utils.get_platform() == "linux": pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead") if python_version != "3.12" and utils.get_platform() == "pyodide": @@ -127,7 +131,9 @@ def test_pinned_versions(tmp_path, python_version, build_frontend_env_nouv): @pytest.mark.parametrize("method", ["inline", "file"]) -def test_dependency_constraints(method, tmp_path, build_frontend_env_nouv): +def test_dependency_constraints( + method: str, tmp_path: Path, build_frontend_env_nouv: dict[str, str] +) -> None: if utils.get_platform() == "linux": pytest.skip("linux doesn't pin individual tool versions, it pins manylinux images instead") diff --git a/test/test_emulation.py b/test/test_emulation.py index b0dc4c0d..b475b6a7 100644 --- a/test/test_emulation.py +++ b/test/test_emulation.py @@ -1,5 +1,6 @@ import itertools import subprocess +from pathlib import Path import pytest @@ -10,13 +11,13 @@ project_with_a_test = test_projects.new_c_project() project_with_a_test.files["test/spam_test.py"] = r""" import spam -def test_spam(): +def test_spam() -> None: assert spam.filter("spam") == 0 assert spam.filter("ham") != 0 """ -def test(tmp_path, request): +def test(tmp_path: Path, request: pytest.FixtureRequest) -> None: archs = request.config.getoption("--run-emulation") if archs is None: pytest.skip("needs --run-emulation option to run") @@ -45,7 +46,7 @@ def test(tmp_path, request): assert set(actual_wheels) == set(expected_wheels) -def test_setting_arch_on_other_platforms(tmp_path, capfd): +def test_setting_arch_on_other_platforms(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: if utils.get_platform() == "linux": pytest.skip("this test checks the behaviour on platforms other than linux") diff --git a/test/test_environment.py b/test/test_environment.py index 30bab81f..b12a8999 100644 --- a/test/test_environment.py +++ b/test/test_environment.py @@ -2,6 +2,7 @@ import os import subprocess import sys import textwrap +from pathlib import Path import pytest @@ -33,7 +34,7 @@ project_with_environment_asserts = test_projects.new_c_project( ) -def test(tmp_path): +def test(tmp_path: Path) -> None: python_echo = f"'{sys.executable}' -c \"import sys; print(*sys.argv[1:])\"" project_dir = tmp_path / "project" project_with_environment_asserts.generate(project_dir) @@ -55,7 +56,7 @@ def test(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_overridden_path(tmp_path, capfd): +def test_overridden_path(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: project_dir = tmp_path / "project" output_dir = tmp_path / "output" @@ -108,7 +109,7 @@ def test_overridden_path(tmp_path, capfd): ), ], ) -def test_overridden_pip_constraint(tmp_path, build_frontend): +def test_overridden_pip_constraint(tmp_path: Path, build_frontend: str) -> None: """ Verify that users can use PIP_CONSTRAINT to specify a specific version of a build-system.requires dependency, by asserting the version of pytz in the diff --git a/test/test_from_sdist.py b/test/test_from_sdist.py index b916271c..094e96d8 100644 --- a/test/test_from_sdist.py +++ b/test/test_from_sdist.py @@ -6,6 +6,8 @@ from collections.abc import Mapping from pathlib import Path from tempfile import TemporaryDirectory +import pytest + from test.test_projects.base import TestProject from . import test_projects, utils @@ -59,7 +61,7 @@ def cibuildwheel_from_sdist_run( # tests -def test_simple(tmp_path): +def test_simple(tmp_path: Path) -> None: basic_project = test_projects.new_c_project() # make an sdist of the project @@ -87,7 +89,7 @@ def test_simple(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_external_config_file_argument(tmp_path, capfd): +def test_external_config_file_argument(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: basic_project = test_projects.new_c_project() # make an sdist of the project @@ -118,7 +120,7 @@ def test_external_config_file_argument(tmp_path, capfd): assert "test log statement from before-all" in captured.out -def test_config_in_pyproject_toml(tmp_path, capfd): +def test_config_in_pyproject_toml(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # make a project with a pyproject.toml project = test_projects.new_c_project() project.files["pyproject.toml"] = textwrap.dedent( @@ -145,7 +147,7 @@ def test_config_in_pyproject_toml(tmp_path, capfd): assert "test log statement from before-build 8419" in captured.out -def test_internal_config_file_argument(tmp_path, capfd): +def test_internal_config_file_argument(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # make a project with a config file inside project = test_projects.new_c_project( setup_cfg_add="include_package_data = True", diff --git a/test/test_ios.py b/test/test_ios.py index ebf30c51..14e1e45c 100644 --- a/test/test_ios.py +++ b/test/test_ios.py @@ -3,6 +3,7 @@ import platform import shutil import subprocess import textwrap +from pathlib import Path import pytest @@ -22,7 +23,7 @@ import platform from unittest import TestCase class TestPlatform(TestCase): - def test_platform(self): + def test_platform(self) -> None: self.assertEqual(platform.machine(), "{platform.machine()}") """ @@ -59,7 +60,12 @@ def skip_if_ios_testing_not_supported() -> None: {"CIBW_PLATFORM": "ios", "CIBW_BUILD_FRONTEND": "build"}, ], ) -def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd): +def test_ios_platforms( + tmp_path: Path, + build_config: dict[str, str], + monkeypatch: pytest.MonkeyPatch, + capfd: pytest.CaptureFixture[str], +) -> None: skip_if_ios_testing_not_supported() # Create a temporary "bin" directory, symlink a tool that we know eixsts @@ -67,7 +73,9 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd): # and add the temp bin directory to the PATH. tools_dir = tmp_path / "bin" tools_dir.mkdir() - tools_dir.joinpath("does-exist").symlink_to(shutil.which("true")) + true_app = shutil.which("true") + assert true_app is not None + tools_dir.joinpath("does-exist").symlink_to(true_app) monkeypatch.setenv("PATH", str(tools_dir), prepend=os.pathsep) @@ -110,7 +118,7 @@ def test_ios_platforms(tmp_path, build_config, monkeypatch, capfd): @pytest.mark.serial -def test_no_test_sources(tmp_path, capfd): +def test_no_test_sources(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """Build will provide a helpful error if pytest is run and test-sources is not defined.""" skip_if_ios_testing_not_supported() @@ -138,7 +146,7 @@ def test_no_test_sources(tmp_path, capfd): ) -def test_ios_testing_with_placeholder(tmp_path, capfd): +def test_ios_testing_with_placeholder(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """ Tests with the {project} placeholder are not supported on iOS, because the test command is run in the simulator. @@ -168,7 +176,7 @@ def test_ios_testing_with_placeholder(tmp_path, capfd): @pytest.mark.serial @pytest.mark.flaky(reruns=2) -def test_ios_test_command_short_circuit(tmp_path, capfd): +def test_ios_test_command_short_circuit(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: skip_if_ios_testing_not_supported() project_dir = tmp_path / "project" @@ -196,7 +204,7 @@ def test_ios_test_command_short_circuit(tmp_path, capfd): assert "Zen of Python" not in captured.out + captured.err -def test_missing_xbuild_tool(tmp_path, capfd): +def test_missing_xbuild_tool(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """Build will fail if xbuild-tools references a non-existent tool.""" skip_if_ios_testing_not_supported() @@ -220,7 +228,7 @@ def test_missing_xbuild_tool(tmp_path, capfd): assert "Could not find a 'does-not-exist' executable on the path." in captured.err -def test_no_xbuild_tool_definition(tmp_path, capfd): +def test_no_xbuild_tool_definition(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """Build will succeed with a warning if there is no xbuild-tools definition.""" skip_if_ios_testing_not_supported() @@ -254,7 +262,7 @@ def test_no_xbuild_tool_definition(tmp_path, capfd): assert "Your project configuration does not define any cross-build tools." in captured.err -def test_empty_xbuild_tool_definition(tmp_path, capfd): +def test_empty_xbuild_tool_definition(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """Build will succeed with no warning if there is an empty xbuild-tools definition.""" skip_if_ios_testing_not_supported() @@ -286,7 +294,9 @@ def test_empty_xbuild_tool_definition(tmp_path, capfd): @pytest.mark.serial -def test_ios_test_command_without_python_dash_m(tmp_path, capfd): +def test_ios_test_command_without_python_dash_m( + tmp_path: Path, capfd: pytest.CaptureFixture[str] +) -> None: """pytest should be able to run without python -m, but it should warn.""" skip_if_ios_testing_not_supported() @@ -296,7 +306,7 @@ def test_ios_test_command_without_python_dash_m(tmp_path, capfd): project.files["tests/__init__.py"] = "" project.files["tests/test_spam.py"] = textwrap.dedent(""" import spam - def test_spam(): + def test_spam() -> None: assert spam.filter("spam") == 0 assert spam.filter("ham") != 0 """) @@ -324,7 +334,7 @@ def test_ios_test_command_without_python_dash_m(tmp_path, capfd): assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err -def test_ios_test_command_invalid(tmp_path, capfd): +def test_ios_test_command_invalid(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: """Test command should raise an error if it's clearly invalid.""" skip_if_ios_testing_not_supported() @@ -347,7 +357,7 @@ def test_ios_test_command_invalid(tmp_path, capfd): assert "iOS tests configured with a test command which doesn't start with 'python -m'" in err -def test_repair_step(tmp_path): +def test_repair_step(tmp_path: Path) -> None: """Build will succeed & the custom repair step is called.""" skip_if_ios_testing_not_supported() diff --git a/test/test_linux_python.py b/test/test_linux_python.py index 48a70c2e..d0f93f55 100644 --- a/test/test_linux_python.py +++ b/test/test_linux_python.py @@ -1,12 +1,13 @@ import platform import subprocess +from pathlib import Path import pytest from . import test_projects, utils -def test_python_exist(tmp_path, capfd): +def test_python_exist(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: if utils.get_platform() != "linux": pytest.skip("the test is only relevant to the linux build") machine = platform.machine() diff --git a/test/test_macos_archs.py b/test/test_macos_archs.py index f799e55b..534541af 100644 --- a/test/test_macos_archs.py +++ b/test/test_macos_archs.py @@ -1,4 +1,5 @@ import platform +from pathlib import Path import pytest @@ -19,7 +20,7 @@ ALL_MACOS_WHEELS = { DEPLOYMENT_TARGET_TOO_LOW_WARNING = "Bumping MACOSX_DEPLOYMENT_TARGET" -def test_cross_compiled_build(tmp_path): +def test_cross_compiled_build(tmp_path: Path) -> None: if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") if utils.get_xcode_version() < (12, 2): @@ -51,7 +52,12 @@ def test_cross_compiled_build(tmp_path): }, ], ) -def test_cross_compiled_test(tmp_path, capfd, build_universal2, test_config): +def test_cross_compiled_test( + tmp_path: Path, + capfd: pytest.CaptureFixture[str], + build_universal2: bool, + test_config: dict[str, str], +) -> None: if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") if utils.get_xcode_version() < (12, 2): @@ -110,7 +116,9 @@ def test_cross_compiled_test(tmp_path, capfd, build_universal2, test_config): assert set(actual_wheels) == set(expected_wheels) -def test_deployment_target_warning_is_firing(tmp_path, capfd): +def test_deployment_target_warning_is_firing( + tmp_path: Path, capfd: pytest.CaptureFixture[str] +) -> None: # force the warning to check that we can detect it if it happens if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") @@ -133,7 +141,9 @@ def test_deployment_target_warning_is_firing(tmp_path, capfd): @pytest.mark.parametrize("skip_arm64_test", [False, True]) -def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test): +def test_universal2_testing_on_x86_64( + tmp_path: Path, capfd: pytest.CaptureFixture[str], skip_arm64_test: bool +) -> None: if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") if utils.get_xcode_version() < (12, 2): @@ -172,7 +182,9 @@ def test_universal2_testing_on_x86_64(tmp_path, capfd, skip_arm64_test): assert set(actual_wheels) == set(expected_wheels) -def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd): +def test_universal2_testing_on_arm64( + build_frontend_env: dict[str, str], tmp_path: Path, capfd: pytest.CaptureFixture[str] +) -> None: # cibuildwheel should test the universal2 wheel on both x86_64 and arm64, when run on arm64 if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") @@ -203,7 +215,9 @@ def test_universal2_testing_on_arm64(build_frontend_env, tmp_path, capfd): assert set(actual_wheels) == set(expected_wheels) -def test_cp38_arm64_testing(tmp_path, capfd, request): +def test_cp38_arm64_testing( + tmp_path: Path, capfd: pytest.CaptureFixture[str], request: pytest.FixtureRequest +) -> None: if utils.get_platform() != "macos": pytest.skip("this test is only relevant to macos") if utils.get_xcode_version() < (12, 2): @@ -238,7 +252,9 @@ def test_cp38_arm64_testing(tmp_path, capfd, request): assert set(actual_wheels) == set(expected_wheels) -def test_cp38_arm64_testing_universal2_installer(tmp_path, capfd, request): +def test_cp38_arm64_testing_universal2_installer( + tmp_path: Path, capfd: pytest.CaptureFixture[str], request: pytest.FixtureRequest +) -> None: if not request.config.getoption("--run-cp38-universal2"): pytest.skip("needs --run-cp38-universal2 option to run") diff --git a/test/test_manylinuxXXXX_only.py b/test/test_manylinuxXXXX_only.py index e114bae3..2fbb4e3f 100644 --- a/test/test_manylinuxXXXX_only.py +++ b/test/test_manylinuxXXXX_only.py @@ -1,5 +1,6 @@ import platform import textwrap +from pathlib import Path import pytest @@ -61,7 +62,7 @@ project_with_manylinux_symbols = test_projects.new_c_project( ], ) @pytest.mark.usefixtures("docker_cleanup") -def test(manylinux_image, tmp_path): +def test(manylinux_image: str, tmp_path: Path) -> None: if utils.get_platform() != "linux": pytest.skip("the container image test is only relevant to the linux build") diff --git a/test/test_meson_python.py b/test/test_meson_python.py index dcfeb2c5..5d507be6 100644 --- a/test/test_meson_python.py +++ b/test/test_meson_python.py @@ -1,3 +1,5 @@ +from pathlib import Path + import packaging.utils from . import test_projects, utils @@ -5,7 +7,7 @@ from . import test_projects, utils meson_project = test_projects.new_meson_project() -def test_meson_python_basic(tmp_path, build_frontend_env): +def test_meson_python_basic(tmp_path: Path, build_frontend_env: dict[str, str]) -> None: """Test that cibuildwheel can build a project with meson-python backend.""" project_dir = tmp_path / "project" diff --git a/test/test_musllinux_X_Y_only.py b/test/test_musllinux_X_Y_only.py index 5e2f2e24..2a3f70af 100644 --- a/test/test_musllinux_X_Y_only.py +++ b/test/test_musllinux_X_Y_only.py @@ -1,4 +1,5 @@ import textwrap +from pathlib import Path import pytest @@ -27,7 +28,7 @@ project_with_manylinux_symbols = test_projects.new_c_project( ["musllinux_1_2"], ) @pytest.mark.usefixtures("docker_cleanup") -def test(musllinux_image, tmp_path): +def test(musllinux_image: str, tmp_path: Path) -> None: if utils.get_platform() != "linux": pytest.skip("the container image test is only relevant to the linux build") diff --git a/test/test_pep518.py b/test/test_pep518.py index 702e7f18..4bc1fa6b 100644 --- a/test/test_pep518.py +++ b/test/test_pep518.py @@ -1,4 +1,5 @@ import textwrap +from pathlib import Path from . import test_projects, utils @@ -29,7 +30,7 @@ build-backend = "setuptools.build_meta" """ -def test_pep518(tmp_path, build_frontend_env): +def test_pep518(tmp_path: Path, build_frontend_env: dict[str, str]) -> None: project_dir = tmp_path / "project" basic_project.generate(project_dir) diff --git a/test/test_pure_wheel.py b/test/test_pure_wheel.py index d309d8a9..dc7c0995 100644 --- a/test/test_pure_wheel.py +++ b/test/test_pure_wheel.py @@ -1,4 +1,5 @@ import subprocess +from pathlib import Path import pytest @@ -23,7 +24,7 @@ def a_function(): """ -def test(tmp_path, capfd): +def test(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: # this test checks that if a pure wheel is generated, the build should # fail. project_dir = tmp_path / "project" diff --git a/test/test_pyodide.py b/test/test_pyodide.py index f0cb4962..45355ad5 100644 --- a/test/test_pyodide.py +++ b/test/test_pyodide.py @@ -3,6 +3,7 @@ import os import subprocess import sys import textwrap +from pathlib import Path import pytest @@ -48,7 +49,7 @@ if __name__ == "__main__": @pytest.mark.parametrize("use_pyproject_toml", [True, False]) -def test_pyodide_build(tmp_path, use_pyproject_toml): +def test_pyodide_build(tmp_path: Path, use_pyproject_toml: bool) -> None: if sys.platform == "win32": pytest.skip("pyodide-build doesn't work correctly on Windows") @@ -88,7 +89,7 @@ def test_pyodide_build(tmp_path, use_pyproject_toml): assert set(actual_wheels) == set(expected_wheels) -def test_pyodide_version_incompatible(tmp_path, capfd): +def test_pyodide_version_incompatible(tmp_path: Path, capfd: pytest.CaptureFixture[str]) -> None: if sys.platform == "win32": pytest.skip("pyodide-build doesn't work correctly on Windows") @@ -110,19 +111,19 @@ def test_pyodide_version_incompatible(tmp_path, capfd): @pytest.mark.parametrize("expect_failure", [True, False]) -def test_pyodide_build_and_test(tmp_path, expect_failure): +def test_pyodide_build_and_test(tmp_path: Path, expect_failure: bool) -> None: if sys.platform == "win32": pytest.skip("pyodide-build doesn't work correctly on Windows") if expect_failure: basic_project.files["test/spam_test.py"] = textwrap.dedent(r""" - def test_filter(): + def test_filter() -> None: assert 0 == 1 """) else: basic_project.files["test/spam_test.py"] = textwrap.dedent(r""" import spam - def test_filter(): + def test_filter() -> None: assert spam.filter("spam") == 0 """) basic_project.generate(tmp_path) @@ -149,7 +150,7 @@ def test_pyodide_build_and_test(tmp_path, expect_failure): assert set(actual_wheels) == set(expected_wheels) -def test_pyodide_repair_wheel(tmp_path): +def test_pyodide_repair_wheel(tmp_path: Path) -> None: if sys.platform == "win32": pytest.skip("pyodide-build doesn't work correctly on Windows") diff --git a/test/test_ssl.py b/test/test_ssl.py index 60e7ffef..c43d18fb 100644 --- a/test/test_ssl.py +++ b/test/test_ssl.py @@ -1,4 +1,5 @@ import textwrap +from pathlib import Path import pytest @@ -32,7 +33,7 @@ project_with_ssl_tests = test_projects.new_c_project( @pytest.mark.flaky(reruns=2) -def test(tmp_path): +def test(tmp_path: Path) -> None: # this test checks that SSL is working in the build environment using # some checks in setup.py. project_dir = tmp_path / "project" diff --git a/test/test_subdir_package.py b/test/test_subdir_package.py index 2675ef2e..e842b2d5 100644 --- a/test/test_subdir_package.py +++ b/test/test_subdir_package.py @@ -1,6 +1,7 @@ from pathlib import Path import jinja2 +import pytest from . import utils from .test_projects import TestProject @@ -31,7 +32,7 @@ print('before_build.py executed!') """ -def test(capfd, tmp_path): +def test(capfd: pytest.CaptureFixture[str], tmp_path: Path) -> None: project_dir = tmp_path / "project" subdir_package_project.generate(project_dir) diff --git a/test/test_testing.py b/test/test_testing.py index 65cb66ba..6305cffc 100644 --- a/test/test_testing.py +++ b/test/test_testing.py @@ -43,11 +43,11 @@ def path_contains(parent, child): class TestSpam(TestCase): - def test_filter(self): + def test_filter(self) -> None: self.assertEqual(0, spam.filter("spam")) self.assertNotEqual(0, spam.filter("ham")) - def test_virtualenv(self): + def test_virtualenv(self) -> None: # sys.prefix is different from sys.base_prefix when running a virtualenv # See https://docs.python.org/3/library/venv.html, which virtualenv seems # to honor in recent releases @@ -58,7 +58,7 @@ class TestSpam(TestCase): self.assertTrue(path_contains(sys.prefix, spam.__file__)) self.assertIn("VIRTUAL_ENV", os.environ) - def test_uname(self): + def test_uname(self) -> None: if platform.system() == "Windows": return # if we're running in 32-bit Python, check that the machine is i686. @@ -69,7 +69,7 @@ class TestSpam(TestCase): ''' -def test(tmp_path): +def test(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_a_test.generate(project_dir) @@ -90,7 +90,7 @@ def test(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_extras_require(tmp_path): +def test_extras_require(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_a_test.generate(project_dir) @@ -112,7 +112,7 @@ def test_extras_require(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_dependency_groups(tmp_path): +def test_dependency_groups(tmp_path: Path) -> None: group_project = project_with_a_test.copy() group_project.files["pyproject.toml"] = inspect.cleandoc(""" [build-system] @@ -149,12 +149,12 @@ project_with_a_failing_test.files["test/spam_test.py"] = r""" from unittest import TestCase class TestSpam(TestCase): - def test_something(self): + def test_something(self) -> None: self.fail('this test is supposed to fail') """ -def test_failing_test(tmp_path): +def test_failing_test(tmp_path: Path) -> None: """Ensure a failing test causes cibuildwheel to error out and exit""" project_dir = tmp_path / "project" output_dir = tmp_path / "output" @@ -215,7 +215,7 @@ def test_bare_pytest_invocation( ) -def test_test_sources(tmp_path): +def test_test_sources(tmp_path: Path) -> None: project_dir = tmp_path / "project" project_with_a_test.generate(project_dir) @@ -235,7 +235,7 @@ def test_test_sources(tmp_path): assert set(actual_wheels) == set(expected_wheels) -def test_test_environment(tmp_path): +def test_test_environment(tmp_path: Path) -> None: project_dir = tmp_path / "project" test_projects.new_c_project().generate(project_dir) diff --git a/test/test_troubleshooting.py b/test/test_troubleshooting.py index 1b8e8a37..4c468024 100644 --- a/test/test_troubleshooting.py +++ b/test/test_troubleshooting.py @@ -1,4 +1,5 @@ import subprocess +from pathlib import Path import pytest @@ -9,7 +10,12 @@ SO_FILE_WARNING = "NOTE: Shared object (.so) files found in this project." @pytest.mark.parametrize("project_contains_so_files", [False, True]) -def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project_contains_so_files): +def test_failed_build_with_so_files( + tmp_path: Path, + capfd: pytest.CaptureFixture[str], + build_frontend_env: dict[str, str], + project_contains_so_files: bool, +) -> None: project = TestProject() project.files["setup.py"] = "raise Exception('this build will fail')\n" if project_contains_so_files: @@ -35,7 +41,11 @@ def test_failed_build_with_so_files(tmp_path, capfd, build_frontend_env, project @pytest.mark.parametrize("project_contains_so_files", [False, True]) -def test_failed_repair_with_so_files(tmp_path, capfd, project_contains_so_files): +def test_failed_repair_with_so_files( + tmp_path: Path, + capfd: pytest.CaptureFixture[str], + project_contains_so_files: bool, +) -> None: if utils.get_platform() != "linux": pytest.skip("this test is only relevant to the linux build") diff --git a/test/test_wheel_tag.py b/test/test_wheel_tag.py index 09c99cca..2d985924 100644 --- a/test/test_wheel_tag.py +++ b/test/test_wheel_tag.py @@ -1,3 +1,5 @@ +from pathlib import Path + import pytest from . import test_projects, utils @@ -5,7 +7,7 @@ from . import test_projects, utils basic_project = test_projects.new_c_project() -def test_wheel_tag_is_correct_when_using_macosx_deployment_target(tmp_path): +def test_wheel_tag_is_correct_when_using_macosx_deployment_target(tmp_path: Path) -> None: if utils.get_platform() != "macos": pytest.skip("This test is only relevant to MACOSX_DEPLOYMENT_TARGET") diff --git a/test/test_windows.py b/test/test_windows.py index 073669ba..fac779b2 100644 --- a/test/test_windows.py +++ b/test/test_windows.py @@ -38,7 +38,9 @@ def skip_if_no_msvc(arm64: bool = False) -> None: @pytest.mark.parametrize("use_pyproject_toml", [True, False]) -def test_wheel_tag_is_correct_when_using_windows_cross_compile(tmp_path, use_pyproject_toml): +def test_wheel_tag_is_correct_when_using_windows_cross_compile( + tmp_path: Path, use_pyproject_toml: bool +) -> None: if utils.get_platform() != "windows": pytest.skip("This test is only relevant to Windows") diff --git a/test/utils.py b/test/utils.py index bb599abd..dc04640f 100644 --- a/test/utils.py +++ b/test/utils.py @@ -11,7 +11,7 @@ import sys from collections.abc import Generator, Mapping, Sequence from pathlib import Path from tempfile import TemporaryDirectory -from typing import Any, Final +from typing import Final import pytest @@ -458,7 +458,7 @@ def get_xcode_version() -> tuple[int, int]: return (int(version_parts[0]), int(version_parts[1])) -def skip_if_pyodide(reason: str) -> Any: +def skip_if_pyodide(reason: str) -> pytest.MarkDecorator: return pytest.mark.skipif(get_platform() == "pyodide", reason=reason) diff --git a/unit_test/architecture_test.py b/unit_test/architecture_test.py index 3033e2be..f9112f6c 100644 --- a/unit_test/architecture_test.py +++ b/unit_test/architecture_test.py @@ -6,6 +6,7 @@ import pytest import cibuildwheel.architecture from cibuildwheel.architecture import Architecture, arch_synonym +from cibuildwheel.typing import PlatformName @pytest.fixture( @@ -20,7 +21,9 @@ from cibuildwheel.architecture import Architecture, arch_synonym pytest.param(("windows", "win32", "ARM64", "arm"), id="windows-arm"), ] ) -def platform_machine(request, monkeypatch): +def platform_machine( + request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch +) -> tuple[str, str]: platform_name, platform_value, machine_value, machine_name = request.param monkeypatch.setattr(sys, "platform", platform_value) monkeypatch.setattr(platform_module, "machine", lambda: machine_value) @@ -28,7 +31,7 @@ def platform_machine(request, monkeypatch): return platform_name, machine_name -def test_arch_auto(platform_machine): +def test_arch_auto(platform_machine: tuple[str, str]) -> None: _, machine_name = platform_machine arch_set = Architecture.auto_archs("linux") @@ -52,7 +55,7 @@ def test_arch_auto(platform_machine): assert arch_set == expected[machine_name] -def test_arch_auto64(platform_machine): +def test_arch_auto64(platform_machine: tuple[str, str]) -> None: _, machine_name = platform_machine arch_set = Architecture.parse_config("auto64", "linux") @@ -68,7 +71,7 @@ def test_arch_auto64(platform_machine): assert arch_set == expected[machine_name] -def test_arch_auto32(platform_machine): +def test_arch_auto32(platform_machine: tuple[str, str]) -> None: _, machine_name = platform_machine arch_set = Architecture.parse_config("auto32", "linux") @@ -83,7 +86,7 @@ def test_arch_auto32(platform_machine): assert arch_set == expected[machine_name] -def test_arch_auto_no_aarch32(monkeypatch): +def test_arch_auto_no_aarch32(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "platform", "linux") monkeypatch.setattr(platform_module, "machine", lambda: "aarch64") monkeypatch.setattr(shutil, "which", lambda *args, **kwargs: None) @@ -103,14 +106,14 @@ def test_arch_auto_no_aarch32(monkeypatch): assert arch_set == set() -def test_arch_native_on_ios(monkeypatch): +def test_arch_native_on_ios(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setattr(platform_module, "machine", lambda: "arm64") arch_set = Architecture.parse_config("native", platform="ios") assert arch_set == {Architecture.arm64_iphonesimulator} -def test_arch_auto_on_ios(monkeypatch): +def test_arch_auto_on_ios(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "platform", "darwin") monkeypatch.setattr(platform_module, "machine", lambda: "arm64") arch_set = Architecture.parse_config("auto", platform="ios") @@ -130,5 +133,7 @@ def test_arch_auto_on_ios(monkeypatch): ("x86", "windows", "macos", None), ], ) -def test_arch_synonym(arch, from_platform, to_platform, expected): +def test_arch_synonym( + arch: str, from_platform: PlatformName, to_platform: PlatformName, expected: str | None +) -> None: assert arch_synonym(arch, from_platform, to_platform) == expected diff --git a/unit_test/build_ids_test.py b/unit_test/build_ids_test.py index 13014c24..76647623 100644 --- a/unit_test/build_ids_test.py +++ b/unit_test/build_ids_test.py @@ -6,7 +6,7 @@ from cibuildwheel.extra import Printable, dump_python_configurations from cibuildwheel.util import resources -def test_compare_configs(): +def test_compare_configs() -> None: txt = resources.BUILD_PLATFORMS.read_text() with resources.BUILD_PLATFORMS.open("rb") as f2: @@ -18,7 +18,7 @@ def test_compare_configs(): assert new_txt == txt -def test_dump_with_Version(): +def test_dump_with_Version() -> None: # MyPy doesn't understand deeply nested dicts correctly example: dict[str, dict[str, list[dict[str, Printable]]]] = { "windows": { diff --git a/unit_test/build_selector_test.py b/unit_test/build_selector_test.py index af054230..71a6fcd6 100644 --- a/unit_test/build_selector_test.py +++ b/unit_test/build_selector_test.py @@ -4,7 +4,7 @@ import cibuildwheel.selector from cibuildwheel.selector import BuildSelector, EnableGroup -def test_build(): +def test_build() -> None: build_selector = BuildSelector( build_config="cp3*-* *-manylinux*", skip_config="", enable=frozenset([EnableGroup.PyPy]) ) @@ -41,7 +41,7 @@ def test_build(): assert not build_selector("pp311-win_amd64") -def test_build_filter_pre(): +def test_build_filter_pre() -> None: build_selector = BuildSelector( build_config="cp3*-* *-manylinux*", skip_config="", @@ -55,7 +55,7 @@ def test_build_filter_pre(): assert not build_selector("cp313t-manylinux_x86_64") -def test_build_filter_pypy(): +def test_build_filter_pypy() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -67,7 +67,7 @@ def test_build_filter_pypy(): assert not build_selector("pp39-manylinux_x86_64") -def test_build_filter_pypy_eol(): +def test_build_filter_pypy_eol() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -79,7 +79,7 @@ def test_build_filter_pypy_eol(): assert build_selector("pp39-manylinux_x86_64") -def test_build_filter_pypy_all(): +def test_build_filter_pypy_all() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -91,7 +91,7 @@ def test_build_filter_pypy_all(): assert build_selector("pp39-manylinux_x86_64") -def test_build_filter_pyodide_prerelease(): +def test_build_filter_pyodide_prerelease() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -101,7 +101,7 @@ def test_build_filter_pyodide_prerelease(): assert build_selector("cp313-pyodide_wasm32") -def test_build_filter_pyodide(): +def test_build_filter_pyodide() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -111,7 +111,7 @@ def test_build_filter_pyodide(): assert build_selector("cp313-pyodide_wasm32") -def test_skip(): +def test_skip() -> None: build_selector = BuildSelector( build_config="*", skip_config="pp310-* cp3?-manylinux_i686 cp36-win* *-win32", @@ -136,7 +136,7 @@ def test_skip(): assert build_selector("cp37-win_amd64") -def test_build_and_skip(): +def test_build_and_skip() -> None: build_selector = BuildSelector( build_config="cp36-* cp37-macosx* *-manylinux*", skip_config="pp37-* cp37-manylinux_i686", @@ -160,7 +160,7 @@ def test_build_and_skip(): assert not build_selector("cp37-win_amd64") -def test_build_braces(): +def test_build_braces() -> None: build_selector = BuildSelector(build_config="cp{36,37}*", skip_config="") assert build_selector("cp36-manylinux_x86_64") @@ -169,7 +169,7 @@ def test_build_braces(): assert not build_selector("cp39-manylinux_x86_64") -def test_build_limited_python(): +def test_build_limited_python() -> None: build_selector = BuildSelector( build_config="*", skip_config="", @@ -190,7 +190,7 @@ def test_build_limited_python(): assert build_selector("gp311_242-win_amd64") -def test_build_limited_python_partial(): +def test_build_limited_python_partial() -> None: build_selector = BuildSelector( build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6, !=3.7.*") ) @@ -201,7 +201,7 @@ def test_build_limited_python_partial(): assert build_selector("cp39-manylinux_x86_64") -def test_build_limited_python_patch(): +def test_build_limited_python_patch() -> None: build_selector = BuildSelector( build_config="*", skip_config="", requires_python=SpecifierSet(">=3.6.8") ) @@ -210,13 +210,13 @@ def test_build_limited_python_patch(): assert build_selector("cp37-manylinux_x86_64") -def test_build_free_threaded_python(): +def test_build_free_threaded_python() -> None: build_selector = BuildSelector(build_config="*", skip_config="", enable=frozenset(EnableGroup)) assert build_selector("cp313t-manylinux_x86_64") -def test_testing_selector(): +def test_testing_selector() -> None: # This is not a global import to keep pytest from collecting it as a test test_selector = cibuildwheel.selector.TestSelector(skip_config="cp36-*") diff --git a/unit_test/conftest.py b/unit_test/conftest.py index a51f10d8..bb1533e0 100644 --- a/unit_test/conftest.py +++ b/unit_test/conftest.py @@ -6,7 +6,7 @@ import pytest MOCK_PACKAGE_DIR = Path("some_package_dir") -def pytest_addoption(parser): +def pytest_addoption(parser: pytest.Parser) -> None: parser.addoption("--run-docker", action="store_true", default=False, help="run docker tests") parser.addoption("--run-podman", action="store_true", default=False, help="run podman tests") parser.addoption( @@ -18,13 +18,13 @@ def pytest_addoption(parser): @pytest.fixture -def fake_package_dir(tmp_path, monkeypatch): +def fake_package_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> list[str]: """ Monkey-patch enough for the main() function to run """ real_path_exists = Path.exists - def mock_path_exists(path): + def mock_path_exists(path: Path) -> bool: if str(path).endswith(str(MOCK_PACKAGE_DIR / "setup.py")): return True else: diff --git a/unit_test/download_test.py b/unit_test/download_test.py index 4c5f9edc..68c7d926 100644 --- a/unit_test/download_test.py +++ b/unit_test/download_test.py @@ -1,4 +1,5 @@ import ssl +from pathlib import Path import certifi import pytest @@ -8,21 +9,21 @@ from cibuildwheel.util.file import download DOWNLOAD_URL = "https://cdn.jsdelivr.net/gh/pypa/cibuildwheel@v1.6.3/requirements-dev.txt" -def test_download(monkeypatch, tmp_path): +def test_download(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: monkeypatch.delenv("SSL_CERT_FILE", raising=False) dest = tmp_path / "file.txt" download(DOWNLOAD_URL, dest) assert len(dest.read_bytes()) == 134 -def test_download_good_ssl_cert_file(monkeypatch, tmp_path): +def test_download_good_ssl_cert_file(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: monkeypatch.setenv("SSL_CERT_FILE", certifi.where()) dest = tmp_path / "file.txt" download(DOWNLOAD_URL, dest) assert len(dest.read_bytes()) == 134 -def test_download_bad_ssl_cert_file(monkeypatch, tmp_path): +def test_download_bad_ssl_cert_file(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: bad_cafile = tmp_path / "ca.pem" bad_cafile.write_text("bad certificates") monkeypatch.setenv("SSL_CERT_FILE", str(bad_cafile)) diff --git a/unit_test/environment_test.py b/unit_test/environment_test.py index c5e6e57c..e0f4daba 100644 --- a/unit_test/environment_test.py +++ b/unit_test/environment_test.py @@ -7,7 +7,7 @@ from cibuildwheel.environment import parse_environment PYTHON_ECHO = f"'{sys.executable}' -c \"import sys; print(*sys.argv[1:])\"" -def test_basic_parsing(): +def test_basic_parsing() -> None: environment_recipe = parse_environment("VAR=1 VBR=2") environment_dict = environment_recipe.as_dictionary(prev_environment={}) @@ -15,7 +15,7 @@ def test_basic_parsing(): assert environment_dict == {"VAR": "1", "VBR": "2"} -def test_quotes(): +def test_quotes() -> None: environment_recipe = parse_environment("A=1 VAR=\"1 NOT_A_VAR=2\" VBR='vbr'") environment_dict = environment_recipe.as_dictionary(prev_environment={}) @@ -23,7 +23,7 @@ def test_quotes(): assert environment_dict == {"A": "1", "VAR": "1 NOT_A_VAR=2", "VBR": "vbr"} -def test_inheritance(): +def test_inheritance() -> None: environment_recipe = parse_environment("PATH=$PATH:/usr/local/bin") environment_dict = environment_recipe.as_dictionary(prev_environment={"PATH": "/usr/bin"}) @@ -31,7 +31,7 @@ def test_inheritance(): assert environment_dict == {"PATH": "/usr/bin:/usr/local/bin"} -def test_shell_eval(): +def test_shell_eval() -> None: environment_recipe = parse_environment(f'VAR="$({PYTHON_ECHO} "a test" string)"') env_copy = os.environ.copy() @@ -42,7 +42,7 @@ def test_shell_eval(): assert environment_dict["VAR"] == "a test string" -def test_shell_eval_and_env(): +def test_shell_eval_and_env() -> None: environment_recipe = parse_environment(f'VAR="$({PYTHON_ECHO} "$PREV_VAR" string)"') prev_environment = {**os.environ, "PREV_VAR": "1 2 3"} @@ -51,7 +51,7 @@ def test_shell_eval_and_env(): assert environment_dict == {**prev_environment, "VAR": "1 2 3 string"} -def test_empty_var(): +def test_empty_var() -> None: environment_recipe = parse_environment("CFLAGS=") environment_dict = environment_recipe.as_dictionary(prev_environment={"CFLAGS": "-Wall"}) @@ -59,7 +59,7 @@ def test_empty_var(): assert environment_dict == {"CFLAGS": ""} -def test_no_vars(): +def test_no_vars() -> None: environment_recipe = parse_environment("") environment_dict = environment_recipe.as_dictionary(prev_environment={}) @@ -67,7 +67,7 @@ def test_no_vars(): assert environment_dict == {} -def test_no_vars_pass_through(): +def test_no_vars_pass_through() -> None: environment_recipe = parse_environment("") environment_dict = environment_recipe.as_dictionary( @@ -77,7 +77,7 @@ def test_no_vars_pass_through(): assert environment_dict == {"CIBUILDWHEEL": "awesome"} -def test_operators_inside_eval(): +def test_operators_inside_eval() -> None: environment_recipe = parse_environment( f'SOMETHING="$({PYTHON_ECHO} a; {PYTHON_ECHO} b; {PYTHON_ECHO} c)"' ) @@ -88,7 +88,7 @@ def test_operators_inside_eval(): assert environment_dict.get("SOMETHING") == "a\nb\nc" -def test_substitution_with_backslash(): +def test_substitution_with_backslash() -> None: environment_recipe = parse_environment('PATH2="somewhere_else;$PATH1"') environment_dict = environment_recipe.as_dictionary(prev_environment={"PATH1": "c:\\folder\\"}) @@ -96,7 +96,7 @@ def test_substitution_with_backslash(): assert environment_dict.get("PATH2") == "somewhere_else;c:\\folder\\" -def test_awkwardly_quoted_variable(): +def test_awkwardly_quoted_variable() -> None: environment_recipe = parse_environment( f'VAR2=something"like this""$VAR1"$VAR1$({PYTHON_ECHO} "there is more")"$({PYTHON_ECHO} "and more!")"' ) diff --git a/unit_test/main_commands_test.py b/unit_test/main_commands_test.py index e6b4c464..4383fe87 100644 --- a/unit_test/main_commands_test.py +++ b/unit_test/main_commands_test.py @@ -1,6 +1,7 @@ import errno import shutil import sys +from pathlib import Path import pytest @@ -8,7 +9,9 @@ import cibuildwheel.__main__ as main_module from cibuildwheel.__main__ import main -def test_clean_cache_when_cache_exists(tmp_path, monkeypatch, capfd): +def test_clean_cache_when_cache_exists( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: fake_cache_dir = (tmp_path / "cibw_cache").resolve() monkeypatch.setattr(main_module, "CIBW_CACHE_PATH", fake_cache_dir) @@ -40,7 +43,9 @@ def test_clean_cache_when_cache_exists(tmp_path, monkeypatch, capfd): assert not fake_cache_dir.exists() -def test_clean_cache_when_cache_does_not_exist(tmp_path, monkeypatch, capfd): +def test_clean_cache_when_cache_does_not_exist( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: fake_cache_dir = (tmp_path / "nonexistent_cache").resolve() monkeypatch.setattr(main_module, "CIBW_CACHE_PATH", fake_cache_dir) @@ -55,7 +60,9 @@ def test_clean_cache_when_cache_does_not_exist(tmp_path, monkeypatch, capfd): assert f"Cache directory does not exist: {fake_cache_dir}" in out -def test_clean_cache_with_error(tmp_path, monkeypatch, capfd): +def test_clean_cache_with_error( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: fake_cache_dir = (tmp_path / "cibw_cache").resolve() monkeypatch.setattr(main_module, "CIBW_CACHE_PATH", fake_cache_dir) @@ -73,7 +80,7 @@ def test_clean_cache_with_error(tmp_path, monkeypatch, capfd): monkeypatch.setattr(sys, "argv", ["cibuildwheel", "--clean-cache"]) - def fake_rmtree(path): # noqa: ARG001 + def fake_rmtree(path: Path) -> None: # noqa: ARG001 raise OSError(errno.EACCES, "Permission denied") monkeypatch.setattr(shutil, "rmtree", fake_rmtree) @@ -88,7 +95,9 @@ def test_clean_cache_with_error(tmp_path, monkeypatch, capfd): assert "Error clearing cache:" in err -def test_clean_cache_without_sentinel(tmp_path, monkeypatch, capfd): +def test_clean_cache_without_sentinel( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: fake_cache_dir = (tmp_path / "not_a_cache").resolve() monkeypatch.setattr(main_module, "CIBW_CACHE_PATH", fake_cache_dir) @@ -106,7 +115,9 @@ def test_clean_cache_without_sentinel(tmp_path, monkeypatch, capfd): assert fake_cache_dir.exists() -def test_clean_cache_with_invalid_signature(tmp_path, monkeypatch, capfd): +def test_clean_cache_with_invalid_signature( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capfd: pytest.CaptureFixture[str] +) -> None: fake_cache_dir = (tmp_path / "fake_cache").resolve() monkeypatch.setattr(main_module, "CIBW_CACHE_PATH", fake_cache_dir) diff --git a/unit_test/main_tests/conftest.py b/unit_test/main_tests/conftest.py index cf92ea1a..df683c67 100644 --- a/unit_test/main_tests/conftest.py +++ b/unit_test/main_tests/conftest.py @@ -2,7 +2,9 @@ import contextlib import platform as platform_module import subprocess import sys +from collections.abc import Generator, Iterator from pathlib import Path +from typing import Any import pytest @@ -15,27 +17,27 @@ from cibuildwheel.util import file class ArgsInterceptor: def __init__(self) -> None: self.call_count = 0 - self.args: tuple[object, ...] | None = None - self.kwargs: dict[str, object] | None = None + self.args: tuple[Any, ...] = () + self.kwargs: dict[str, Any] = {} - def __call__(self, *args: object, **kwargs: object) -> None: + def __call__(self, *args: Any, **kwargs: Any) -> None: self.call_count += 1 self.args = args self.kwargs = kwargs @pytest.fixture(autouse=True) -def mock_protection(monkeypatch): +def mock_protection(monkeypatch: pytest.MonkeyPatch) -> None: """ Ensure that a unit test will never actually run a cibuildwheel 'build' function, which shouldn't be run on a developer's machine """ - def fail_on_call(*args, **kwargs): + def fail_on_call(*args: object, **kwargs: object) -> None: msg = "This should never be called" raise RuntimeError(msg) - def ignore_call(*args, **kwargs): + def ignore_call(*args: object, **kwargs: object) -> None: pass monkeypatch.setattr(subprocess, "Popen", fail_on_call) @@ -49,27 +51,27 @@ def mock_protection(monkeypatch): @pytest.fixture(autouse=True) -def fake_package_dir_autouse(fake_package_dir): +def fake_package_dir_autouse(fake_package_dir: list[str]) -> None: pass @pytest.fixture(autouse=True) -def disable_print_wheels(monkeypatch): +def disable_print_wheels(monkeypatch: pytest.MonkeyPatch) -> None: @contextlib.contextmanager - def empty_cm(*args, **kwargs): + def empty_cm(*args: object, **kwargs: object) -> Generator[None, None, None]: yield monkeypatch.setattr(Logger, "print_summary", empty_cm) @pytest.fixture -def allow_empty(monkeypatch, fake_package_dir): +def allow_empty(monkeypatch: pytest.MonkeyPatch, fake_package_dir: list[str]) -> None: monkeypatch.setattr(sys, "argv", [*fake_package_dir, "--allow-empty"]) @pytest.fixture(params=["linux", "macos", "windows"]) -def platform(request, monkeypatch): - platform_value = request.param +def platform(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) -> str: + platform_value: str = request.param monkeypatch.setenv("CIBW_PLATFORM", platform_value) if platform_value == "windows": @@ -84,7 +86,7 @@ def platform(request, monkeypatch): @pytest.fixture -def intercepted_build_args(monkeypatch): +def intercepted_build_args(monkeypatch: pytest.MonkeyPatch) -> Iterator[ArgsInterceptor]: intercepted = ArgsInterceptor() monkeypatch.setattr(android, "build", intercepted) diff --git a/unit_test/main_tests/main_options_test.py b/unit_test/main_tests/main_options_test.py index b518750b..5e9e008a 100644 --- a/unit_test/main_tests/main_options_test.py +++ b/unit_test/main_tests/main_options_test.py @@ -1,7 +1,9 @@ import sys import tomllib +from collections.abc import Mapping from fnmatch import fnmatch from pathlib import Path +from typing import TYPE_CHECKING import pytest @@ -13,10 +15,15 @@ from cibuildwheel.selector import BuildSelector, EnableGroup from cibuildwheel.util import resources from cibuildwheel.util.packaging import DependencyConstraints +if TYPE_CHECKING: + from .conftest import ArgsInterceptor + # CIBW_PLATFORM is tested in main_platform_test.py -def test_old_free_threaded(monkeypatch, capsys): +def test_old_free_threaded( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setenv("CIBW_FREE_THREADED_SUPPORT", "ON") with pytest.raises(SystemExit): @@ -29,7 +36,9 @@ def test_old_free_threaded(monkeypatch, capsys): @pytest.mark.usefixtures("platform") -def test_output_dir(intercepted_build_args, monkeypatch): +def test_output_dir( + intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch +) -> None: OUTPUT_DIR = Path("some_output_dir") monkeypatch.setenv("CIBW_OUTPUT_DIR", str(OUTPUT_DIR)) @@ -40,7 +49,7 @@ def test_output_dir(intercepted_build_args, monkeypatch): @pytest.mark.usefixtures("platform") -def test_output_dir_default(intercepted_build_args): +def test_output_dir_default(intercepted_build_args: "ArgsInterceptor") -> None: main() assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse").resolve() @@ -48,7 +57,11 @@ def test_output_dir_default(intercepted_build_args): @pytest.mark.usefixtures("platform") @pytest.mark.parametrize("also_set_environment", [False, True]) -def test_output_dir_argument(also_set_environment, intercepted_build_args, monkeypatch): +def test_output_dir_argument( + also_set_environment: bool, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: OUTPUT_DIR = Path("some_output_dir") monkeypatch.setattr(sys, "argv", [*sys.argv, "--output-dir", str(OUTPUT_DIR)]) @@ -61,7 +74,9 @@ def test_output_dir_argument(also_set_environment, intercepted_build_args, monke @pytest.mark.usefixtures("platform", "allow_empty") -def test_build_selector(intercepted_build_args, monkeypatch): +def test_build_selector( + intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_BUILD", "cp313-*") monkeypatch.setenv("CIBW_SKIP", "cp39-*") @@ -76,7 +91,9 @@ def test_build_selector(intercepted_build_args, monkeypatch): @pytest.mark.usefixtures("platform", "allow_empty") -def test_invalid_build_selector(monkeypatch, capsys): +def test_invalid_build_selector( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setenv("CIBW_BUILD", "invalid") with pytest.raises(SystemExit) as e: @@ -95,7 +112,12 @@ def test_invalid_build_selector(monkeypatch, capsys): ], ) @pytest.mark.usefixtures("platform", "intercepted_build_args") -def test_invalid_skip_selector(monkeypatch, capsys, option_name, option_env_var): +def test_invalid_skip_selector( + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + option_name: str, + option_env_var: str, +) -> None: monkeypatch.setenv(option_env_var, "invalid") main() @@ -109,7 +131,9 @@ def test_invalid_skip_selector(monkeypatch, capsys, option_name, option_env_var) "selector", ["*-macosx_universal2:arm64", "*-macosx_universal2:x86_64", "*-macosx_arm64"] ) @pytest.mark.usefixtures("platform", "intercepted_build_args") -def test_valid_test_skip_selector(monkeypatch, capsys, selector): +def test_valid_test_skip_selector( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], selector: str +) -> None: monkeypatch.setenv("CIBW_TEST_SKIP", selector) main() @@ -121,7 +145,9 @@ def test_valid_test_skip_selector(monkeypatch, capsys, selector): @pytest.mark.parametrize("selector", ["*-macosx_universal2:invalid", "*-macosx_arm64:arm64"]) @pytest.mark.usefixtures("platform", "intercepted_build_args") -def test_invalid_test_skip_selector(monkeypatch, capsys, selector): +def test_invalid_test_skip_selector( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], selector: str +) -> None: monkeypatch.setenv("CIBW_TEST_SKIP", selector) main() @@ -132,7 +158,7 @@ def test_invalid_test_skip_selector(monkeypatch, capsys, selector): @pytest.mark.usefixtures("platform", "intercepted_build_args") -def test_empty_selector(monkeypatch): +def test_empty_selector(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("CIBW_SKIP", "*") with pytest.raises(SystemExit) as e: @@ -161,8 +187,13 @@ def test_empty_selector(monkeypatch): ], ) def test_manylinux_images( - architecture, image, full_image, platform, intercepted_build_args, monkeypatch -): + architecture: str, + image: str | None, + full_image: str, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if image is not None: monkeypatch.setenv("CIBW_MANYLINUX_" + architecture.upper() + "_IMAGE", image) @@ -194,8 +225,12 @@ def get_default_repair_command(platform: str) -> str: @pytest.mark.parametrize("repair_command", [None, "repair", "repair -w {dest_dir} {wheel}"]) @pytest.mark.parametrize("platform_specific", [False, True]) def test_repair_command( - repair_command, platform_specific, platform, intercepted_build_args, monkeypatch -): + repair_command: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if repair_command is not None: if platform_specific: monkeypatch.setenv("CIBW_REPAIR_WHEEL_COMMAND_" + platform.upper(), repair_command) @@ -216,7 +251,13 @@ def test_repair_command( [{}, {"something": "value"}, {"something": "value", "something_else": "other_value"}], ) @pytest.mark.parametrize("platform_specific", [False, True]) -def test_environment(environment, platform_specific, platform, intercepted_build_args, monkeypatch): +def test_environment( + environment: Mapping[str, str], + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: env_string = " ".join(f"{k}={v}" for k, v in environment.items()) if platform_specific: monkeypatch.setenv("CIBW_ENVIRONMENT_" + platform.upper(), env_string) @@ -236,8 +277,12 @@ def test_environment(environment, platform_specific, platform, intercepted_build @pytest.mark.parametrize("test_requires", [None, "requirement other_requirement"]) @pytest.mark.parametrize("platform_specific", [False, True]) def test_test_requires( - test_requires, platform_specific, platform, intercepted_build_args, monkeypatch -): + test_requires: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if test_requires is not None: if platform_specific: monkeypatch.setenv("CIBW_TEST_REQUIRES_" + platform.upper(), test_requires) @@ -254,7 +299,13 @@ def test_test_requires( @pytest.mark.parametrize("test_extras", [None, "extras"]) @pytest.mark.parametrize("platform_specific", [False, True]) -def test_test_extras(test_extras, platform_specific, platform, intercepted_build_args, monkeypatch): +def test_test_extras( + test_extras: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if test_extras is not None: if platform_specific: monkeypatch.setenv("CIBW_TEST_EXTRAS_" + platform.upper(), test_extras) @@ -272,8 +323,12 @@ def test_test_extras(test_extras, platform_specific, platform, intercepted_build @pytest.mark.parametrize("test_command", [None, "test --command"]) @pytest.mark.parametrize("platform_specific", [False, True]) def test_test_command( - test_command, platform_specific, platform, intercepted_build_args, monkeypatch -): + test_command: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if test_command is not None: if platform_specific: monkeypatch.setenv("CIBW_TEST_COMMAND_" + platform.upper(), test_command) @@ -291,8 +346,12 @@ def test_test_command( @pytest.mark.parametrize("before_build", [None, "before --build"]) @pytest.mark.parametrize("platform_specific", [False, True]) def test_before_build( - before_build, platform_specific, platform, intercepted_build_args, monkeypatch -): + before_build: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if before_build is not None: if platform_specific: monkeypatch.setenv("CIBW_BEFORE_BUILD_" + platform.upper(), before_build) @@ -309,8 +368,12 @@ def test_before_build( @pytest.mark.parametrize("build_verbosity", [None, 0, 2, -2, 4, -4]) @pytest.mark.parametrize("platform_specific", [False, True]) def test_build_verbosity( - build_verbosity, platform_specific, platform, intercepted_build_args, monkeypatch -): + build_verbosity: int | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if build_verbosity is not None: if platform_specific: monkeypatch.setenv("CIBW_BUILD_VERBOSITY_" + platform.upper(), str(build_verbosity)) @@ -326,7 +389,12 @@ def test_build_verbosity( @pytest.mark.parametrize("platform_specific", [False, True]) -def test_config_settings(platform_specific, platform, intercepted_build_args, monkeypatch): +def test_config_settings( + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: config_settings = ( 'setting=value setting=value2 triplet=1 triplet=2 triplet=3 other="something else"' ) @@ -376,7 +444,9 @@ def test_config_settings(platform_specific, platform, intercepted_build_args, mo ], ) @pytest.mark.usefixtures("platform", "intercepted_build_args", "allow_empty") -def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys): +def test_build_selector_deprecated_error( + monkeypatch: pytest.MonkeyPatch, selector: str, pattern: str, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setenv(selector, pattern) monkeypatch.delenv("CIBW_ENABLE", raising=False) @@ -396,7 +466,13 @@ def test_build_selector_deprecated_error(monkeypatch, selector, pattern, capsys) @pytest.mark.parametrize("before_all", ["", None, "test text"]) @pytest.mark.parametrize("platform_specific", [False, True]) -def test_before_all(before_all, platform_specific, platform, intercepted_build_args, monkeypatch): +def test_before_all( + before_all: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, +) -> None: if before_all is not None: if platform_specific: monkeypatch.setenv("CIBW_BEFORE_ALL_" + platform.upper(), before_all) @@ -417,8 +493,13 @@ def test_before_all(before_all, platform_specific, platform, intercepted_build_a ) @pytest.mark.parametrize("platform_specific", [False, True]) def test_dependency_versions( - dependency_versions, platform_specific, platform, intercepted_build_args, monkeypatch, tmp_path -): + dependency_versions: str | None, + platform_specific: bool, + platform: str, + intercepted_build_args: "ArgsInterceptor", + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: option_value = dependency_versions if dependency_versions == "FILE": @@ -443,13 +524,16 @@ def test_dependency_versions( assert dependency_constraints == DependencyConstraints.latest() elif dependency_versions == "FILE": assert dependency_constraints.base_file_path + assert option_value is not None assert dependency_constraints.base_file_path.samefile(Path(option_value)) elif dependency_versions.startswith("packages:"): assert dependency_constraints.packages == ["pip==21.0.0"] @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"]) -def test_debug_traceback(monkeypatch, method, capfd): +def test_debug_traceback( + monkeypatch: pytest.MonkeyPatch, method: str, capfd: pytest.CaptureFixture[str] +) -> None: if method == "command_line": monkeypatch.setattr(sys, "argv", [*sys.argv, "--debug-traceback"]) elif method == "env_var": @@ -471,7 +555,9 @@ def test_debug_traceback(monkeypatch, method, capfd): @pytest.mark.parametrize("method", ["unset", "command_line", "env_var"]) -def test_enable(method, intercepted_build_args, monkeypatch): +def test_enable( + method: str, intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.delenv("CIBW_ENABLE", raising=False) if method == "command_line": @@ -489,7 +575,9 @@ def test_enable(method, intercepted_build_args, monkeypatch): assert enable_groups == frozenset([EnableGroup.PyPy, EnableGroup.GraalPy]) -def test_enable_all(intercepted_build_args, monkeypatch): +def test_enable_all( + intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "all"]) main() @@ -498,7 +586,9 @@ def test_enable_all(intercepted_build_args, monkeypatch): assert enable_groups == EnableGroup.all_groups() -def test_enable_arg_inherits(intercepted_build_args, monkeypatch): +def test_enable_arg_inherits( + intercepted_build_args: "ArgsInterceptor", monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ENABLE", "pypy graalpy") monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "cpython-prerelease"]) @@ -511,7 +601,9 @@ def test_enable_arg_inherits(intercepted_build_args, monkeypatch): ) -def test_enable_arg_error_message(monkeypatch, capsys): +def test_enable_arg_error_message( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr(sys, "argv", [*sys.argv, "--enable", "invalid_group"]) with pytest.raises(SystemExit) as ex: @@ -522,7 +614,7 @@ def test_enable_arg_error_message(monkeypatch, capsys): assert "Valid group names are:" in err -def test_defaults(platform, intercepted_build_args): +def test_defaults(platform: str, intercepted_build_args: "ArgsInterceptor") -> None: main() build_options: BuildOptions = intercepted_build_args.args[0].build_options(identifier=None) diff --git a/unit_test/main_tests/main_platform_test.py b/unit_test/main_tests/main_platform_test.py index 75d609f6..874aa257 100644 --- a/unit_test/main_tests/main_platform_test.py +++ b/unit_test/main_tests/main_platform_test.py @@ -5,12 +5,18 @@ import pytest from cibuildwheel.__main__ import main from cibuildwheel.architecture import Architecture from cibuildwheel.selector import EnableGroup +from cibuildwheel.typing import PlatformName from ..conftest import MOCK_PACKAGE_DIR +from .conftest import ArgsInterceptor @pytest.mark.parametrize("option_value", [None, "auto", ""]) -def test_platform_unset_or_auto(monkeypatch, intercepted_build_args, option_value): +def test_platform_unset_or_auto( + monkeypatch: pytest.MonkeyPatch, + intercepted_build_args: ArgsInterceptor, + option_value: str | None, +) -> None: if option_value is None: monkeypatch.delenv("CIBW_PLATFORM", raising=False) else: @@ -31,7 +37,9 @@ def test_platform_unset_or_auto(monkeypatch, intercepted_build_args, option_valu pytest.fail(f"Unknown platform: {sys.platform}") -def test_unknown_platform_on_ci(monkeypatch, capsys): +def test_unknown_platform_on_ci( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setenv("CI", "true") monkeypatch.setattr(sys, "platform", "nonexistent") monkeypatch.delenv("CIBW_PLATFORM", raising=False) @@ -44,7 +52,9 @@ def test_unknown_platform_on_ci(monkeypatch, capsys): assert 'Unable to detect platform from "sys.platform"' in err -def test_unknown_platform(monkeypatch, capsys): +def test_unknown_platform( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setenv("CIBW_PLATFORM", "nonexistent") with pytest.raises(SystemExit) as exit: @@ -55,7 +65,9 @@ def test_unknown_platform(monkeypatch, capsys): assert "Unsupported platform: nonexistent" in err -def test_platform_argument(platform, intercepted_build_args, monkeypatch): +def test_platform_argument( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_PLATFORM", "nonexistent") monkeypatch.setattr(sys, "argv", [*sys.argv, "--platform", platform]) @@ -67,14 +79,14 @@ def test_platform_argument(platform, intercepted_build_args, monkeypatch): @pytest.mark.usefixtures("platform") -def test_platform_environment(intercepted_build_args): +def test_platform_environment(intercepted_build_args: ArgsInterceptor) -> None: main() options = intercepted_build_args.args[0] assert options.globals.package_dir == MOCK_PACKAGE_DIR.resolve() -def test_archs_default(platform, intercepted_build_args): +def test_archs_default(platform: str, intercepted_build_args: ArgsInterceptor) -> None: main() options = intercepted_build_args.args[0] @@ -87,7 +99,12 @@ def test_archs_default(platform, intercepted_build_args): @pytest.mark.parametrize("use_env_var", [False, True]) -def test_archs_argument(platform, intercepted_build_args, monkeypatch, use_env_var): +def test_archs_argument( + platform: str, + intercepted_build_args: ArgsInterceptor, + monkeypatch: pytest.MonkeyPatch, + use_env_var: bool, +) -> None: if use_env_var: monkeypatch.setenv("CIBW_ARCHS", "ppc64le") else: @@ -105,7 +122,9 @@ def test_archs_argument(platform, intercepted_build_args, monkeypatch, use_env_v assert options.globals.architectures == {Architecture.ppc64le} -def test_archs_platform_specific(platform, intercepted_build_args, monkeypatch): +def test_archs_platform_specific( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "unused") monkeypatch.setenv("CIBW_ARCHS_LINUX", "ppc64le") monkeypatch.setenv("CIBW_ARCHS_WINDOWS", "x86") @@ -122,7 +141,9 @@ def test_archs_platform_specific(platform, intercepted_build_args, monkeypatch): assert options.globals.architectures == {Architecture.x86_64} -def test_archs_platform_native(platform, intercepted_build_args, monkeypatch): +def test_archs_platform_native( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "native") main() @@ -134,7 +155,9 @@ def test_archs_platform_native(platform, intercepted_build_args, monkeypatch): assert options.globals.architectures == {Architecture.AMD64} -def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch): +def test_archs_platform_auto64( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "auto64") main() @@ -146,7 +169,9 @@ def test_archs_platform_auto64(platform, intercepted_build_args, monkeypatch): assert options.globals.architectures == {Architecture.AMD64} -def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch): +def test_archs_platform_auto32( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "auto32") if platform == "macos": @@ -165,7 +190,9 @@ def test_archs_platform_auto32(platform, intercepted_build_args, monkeypatch): assert options.globals.architectures == {Architecture.x86} -def test_archs_platform_all(platform, intercepted_build_args, monkeypatch): +def test_archs_platform_all( + platform: str, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "all") main() @@ -204,7 +231,12 @@ def test_archs_platform_all(platform, intercepted_build_args, monkeypatch): ("cp311-macosx_x86_64", "macos"), ), ) -def test_only_argument(intercepted_build_args, monkeypatch, only, plat): +def test_only_argument( + intercepted_build_args: ArgsInterceptor, + monkeypatch: pytest.MonkeyPatch, + only: str, + plat: PlatformName, +) -> None: monkeypatch.setenv("CIBW_BUILD", "unused") monkeypatch.setenv("CIBW_SKIP", "unused") monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", only]) @@ -220,14 +252,14 @@ def test_only_argument(intercepted_build_args, monkeypatch, only, plat): @pytest.mark.parametrize("only", ("cp311-manylxinux_x86_64", "some_linux_thing")) -def test_only_failed(monkeypatch, only): +def test_only_failed(monkeypatch: pytest.MonkeyPatch, only: str) -> None: monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", only]) with pytest.raises(SystemExit): main() -def test_only_no_platform(monkeypatch): +def test_only_no_platform(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( sys, "argv", [*sys.argv, "--only", "cp311-manylinux_x86_64", "--platform", "macos"] ) @@ -236,7 +268,7 @@ def test_only_no_platform(monkeypatch): main() -def test_only_no_archs(monkeypatch): +def test_only_no_archs(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( sys, "argv", [*sys.argv, "--only", "cp311-manylinux_x86_64", "--archs", "x86_64"] ) @@ -254,7 +286,12 @@ def test_only_no_archs(monkeypatch): ("CIBW_PLATFORM", "macos"), ), ) -def test_only_overrides_env_vars(monkeypatch, intercepted_build_args, envvar_name, envvar_value): +def test_only_overrides_env_vars( + monkeypatch: pytest.MonkeyPatch, + intercepted_build_args: ArgsInterceptor, + envvar_name: str, + envvar_value: str, +) -> None: monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", "cp311-manylinux_x86_64"]) monkeypatch.setenv(envvar_name, envvar_value) @@ -267,7 +304,9 @@ def test_only_overrides_env_vars(monkeypatch, intercepted_build_args, envvar_nam assert options.globals.architectures == Architecture.all_archs("linux") -def test_pyodide_on_windows(monkeypatch, capsys): +def test_pyodide_on_windows( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: monkeypatch.setattr(sys, "platform", "win32") monkeypatch.setattr(sys, "argv", [*sys.argv, "--only", "cp312-pyodide_wasm32"]) @@ -280,7 +319,9 @@ def test_pyodide_on_windows(monkeypatch, capsys): assert "Building for pyodide is not supported on Windows" in err -def test_empty_archs_platform(platform, intercepted_build_args, monkeypatch): +def test_empty_archs_platform( + platform: PlatformName, intercepted_build_args: ArgsInterceptor, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBW_ARCHS", "") main() diff --git a/unit_test/main_tests/main_requires_python_test.py b/unit_test/main_tests/main_requires_python_test.py index 2fff5275..c79688d0 100644 --- a/unit_test/main_tests/main_requires_python_test.py +++ b/unit_test/main_tests/main_requires_python_test.py @@ -1,14 +1,17 @@ import sys import textwrap +from pathlib import Path import pytest from packaging.specifiers import SpecifierSet from cibuildwheel.__main__ import main +from .conftest import ArgsInterceptor + @pytest.fixture(autouse=True) -def fake_package_dir(monkeypatch, tmp_path): +def fake_package_dir(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> Path: """ Set up a fake project """ @@ -24,7 +27,7 @@ def fake_package_dir(monkeypatch, tmp_path): @pytest.mark.usefixtures("platform") -def test_no_override(intercepted_build_args): +def test_no_override(intercepted_build_args: ArgsInterceptor) -> None: main() options = intercepted_build_args.args[0] @@ -37,7 +40,9 @@ def test_no_override(intercepted_build_args): @pytest.mark.usefixtures("platform") -def test_override_env(monkeypatch, intercepted_build_args): +def test_override_env( + monkeypatch: pytest.MonkeyPatch, intercepted_build_args: ArgsInterceptor +) -> None: monkeypatch.setenv("CIBW_PROJECT_REQUIRES_PYTHON", ">=3.8") main() @@ -52,7 +57,9 @@ def test_override_env(monkeypatch, intercepted_build_args): @pytest.mark.usefixtures("platform") -def test_override_setup_cfg(intercepted_build_args, fake_package_dir): +def test_override_setup_cfg( + intercepted_build_args: ArgsInterceptor, fake_package_dir: Path +) -> None: fake_package_dir.joinpath("setup.cfg").write_text( textwrap.dedent( """ @@ -74,7 +81,9 @@ def test_override_setup_cfg(intercepted_build_args, fake_package_dir): @pytest.mark.usefixtures("platform") -def test_override_pyproject_toml(intercepted_build_args, fake_package_dir): +def test_override_pyproject_toml( + intercepted_build_args: ArgsInterceptor, fake_package_dir: Path +) -> None: fake_package_dir.joinpath("pyproject.toml").write_text( textwrap.dedent( """ @@ -96,7 +105,9 @@ def test_override_pyproject_toml(intercepted_build_args, fake_package_dir): @pytest.mark.usefixtures("platform") -def test_override_setup_py_simple(intercepted_build_args, fake_package_dir): +def test_override_setup_py_simple( + intercepted_build_args: ArgsInterceptor, fake_package_dir: Path +) -> None: fake_package_dir.joinpath("setup.py").write_text( textwrap.dedent( """ diff --git a/unit_test/oci_container_test.py b/unit_test/oci_container_test.py index 24ba3ae2..7a9fa236 100644 --- a/unit_test/oci_container_test.py +++ b/unit_test/oci_container_test.py @@ -1,3 +1,4 @@ +import contextlib import json import os import random @@ -6,6 +7,7 @@ import subprocess import sys import textwrap import time +from collections.abc import Iterator from contextlib import nullcontext from pathlib import Path, PurePath, PurePosixPath @@ -34,7 +36,7 @@ PODMAN = OCIContainerEngineConfig(name="podman") @pytest.fixture(params=["docker", "podman"], scope="module") -def container_engine(request): +def container_engine(request: pytest.FixtureRequest) -> Iterator[OCIContainerEngineConfig]: if request.param == "docker" and not request.config.getoption("--run-docker"): pytest.skip("need --run-docker option to run") if request.param == "podman" and not request.config.getoption("--run-podman"): @@ -63,21 +65,21 @@ def container_engine(request): # Tests -def test_simple(container_engine): +def test_simple(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM ) as container: assert container.call(["echo", "hello"], capture_output=True) == "hello\n" -def test_no_lf(container_engine): +def test_no_lf(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM ) as container: assert container.call(["printf", "hello"], capture_output=True) == "hello" -def test_debug_info(container_engine): +def test_debug_info(container_engine: OCIContainerEngineConfig) -> None: container = OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM ) @@ -86,7 +88,7 @@ def test_debug_info(container_engine): pass -def test_environment(container_engine): +def test_environment(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM ) as container: @@ -98,7 +100,9 @@ def test_environment(container_engine): ) -def test_environment_pass(container_engine, monkeypatch): +def test_environment_pass( + container_engine: OCIContainerEngineConfig, monkeypatch: pytest.MonkeyPatch +) -> None: monkeypatch.setenv("CIBUILDWHEEL", "1") monkeypatch.setenv("SOURCE_DATE_EPOCH", "1489957071") with OCIContainer( @@ -111,7 +115,7 @@ def test_environment_pass(container_engine, monkeypatch): ) -def test_cwd(container_engine): +def test_cwd(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, @@ -122,7 +126,7 @@ def test_cwd(container_engine): assert container.call(["pwd"], capture_output=True, cwd="/opt") == "/opt\n" -def test_container_removed(container_engine): +def test_container_removed(container_engine: OCIContainerEngineConfig) -> None: # test is flaky on some platforms, implement retry for 5 second timeout = 50 # * 100 ms = 5s with OCIContainer( @@ -157,7 +161,7 @@ def test_container_removed(container_engine): assert container_name not in docker_containers_listing -def test_large_environment(container_engine): +def test_large_environment(container_engine: OCIContainerEngineConfig) -> None: # max environment variable size is 128kB long_env_var_length = 127 * 1024 large_environment = { @@ -177,7 +181,7 @@ def test_large_environment(container_engine): ) -def test_binary_output(container_engine): +def test_binary_output(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=DEFAULT_OCI_PLATFORM ) as container: @@ -472,7 +476,9 @@ def test_create_args_volume(tmp_path: Path, container_engine: OCIContainerEngine ), ], ) -def test_parse_engine_config(config, name, create_args, capsys): +def test_parse_engine_config( + config: str, name: str, create_args: tuple[str, ...], capsys: pytest.CaptureFixture[str] +) -> None: engine_config = OCIContainerEngineConfig.from_config_string(config) assert engine_config.name == name assert engine_config.create_args == create_args @@ -485,7 +491,7 @@ def test_parse_engine_config(config, name, create_args, capsys): @pytest.mark.skipif(DEFAULT_OCI_PLATFORM != OCIPlatform.AMD64, reason="Only runs on x86_64") -def test_enforce_32_bit(container_engine): +def test_enforce_32_bit(container_engine: OCIContainerEngineConfig) -> None: with OCIContainer( engine=container_engine, image=DEFAULT_IMAGE, oci_platform=OCIPlatform.i386 ) as container: @@ -567,7 +573,7 @@ def test_local_image( @pytest.mark.parametrize("platform", list(OCIPlatform)) -def test_multiarch_image(container_engine, platform): +def test_multiarch_image(container_engine: OCIContainerEngineConfig, platform: OCIPlatform) -> None: if detect_ci_provider() == CIProvider.travis_ci and DEFAULT_OCI_PLATFORM not in { OCIPlatform.AMD64, platform, @@ -657,8 +663,13 @@ def test_multiarch_image(container_engine, platform): ("podman", '{"Client":{"Version":"2.1.0~rc1"}}', pytest.raises(OCIEngineTooOldError)), ], ) -def test_engine_version(engine_name, version, context, monkeypatch): - def mockcall(*args, **kwargs): +def test_engine_version( + engine_name: str, + version: str | None, + context: contextlib.AbstractContextManager[None], + monkeypatch: pytest.MonkeyPatch, +) -> None: + def mockcall(*args: object, **kwargs: object) -> str: if version is None: raise subprocess.CalledProcessError(1, " ".join(str(arg) for arg in args)) return version diff --git a/unit_test/option_prepare_test.py b/unit_test/option_prepare_test.py index e209178a..43fecd3a 100644 --- a/unit_test/option_prepare_test.py +++ b/unit_test/option_prepare_test.py @@ -3,7 +3,7 @@ import subprocess import sys import typing from contextlib import contextmanager -from pathlib import PurePosixPath +from pathlib import Path, PurePosixPath from unittest import mock import pytest @@ -18,19 +18,19 @@ ALL_IDS = DEFAULT_IDS | {"cp313t", "pp38", "pp39", "pp310", "pp311", "gp311_242" @pytest.fixture -def mock_build_container(monkeypatch): - def fail_on_call(*args, **kwargs): +def mock_build_container(monkeypatch: pytest.MonkeyPatch) -> None: + def fail_on_call(*args: object, **kwargs: object) -> None: msg = "This should never be called" raise RuntimeError(msg) - def ignore_call(*args, **kwargs): + def ignore_call(*args: object, **kwargs: object) -> None: pass @contextmanager - def nullcontext(enter_result=None): + def nullcontext(enter_result: object = None) -> typing.Generator[object, None, None]: yield enter_result - def ignore_context_call(*args, **kwargs): + def ignore_context_call(*args: object, **kwargs: object) -> typing.ContextManager[object]: return nullcontext(kwargs) monkeypatch.setenv("CIBW_PLATFORM", "linux") @@ -49,7 +49,7 @@ def mock_build_container(monkeypatch): @pytest.mark.usefixtures("mock_build_container", "fake_package_dir") -def test_build_default_launches(monkeypatch): +def test_build_default_launches(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(sys, "argv", [*sys.argv, "--platform=linux"]) monkeypatch.setenv("CIBW_ARCHS", "auto64 auto32") monkeypatch.delenv("CIBW_ENABLE", raising=False) @@ -95,7 +95,7 @@ def test_build_default_launches(monkeypatch): @pytest.mark.usefixtures("mock_build_container") -def test_build_with_override_launches(monkeypatch, tmp_path): +def test_build_with_override_launches(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: pkg_dir = tmp_path / "cibw_package" pkg_dir.mkdir() diff --git a/unit_test/options_test.py b/unit_test/options_test.py index 16082500..0c42b1f9 100644 --- a/unit_test/options_test.py +++ b/unit_test/options_test.py @@ -48,7 +48,7 @@ manylinux-x86_64-image = "manylinux_2_34" """ -def test_options_1(tmp_path, monkeypatch): +def test_options_1(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: with tmp_path.joinpath("pyproject.toml").open("w") as f: f.write(PYPROJECT_1) @@ -96,7 +96,7 @@ def test_options_1(tmp_path, monkeypatch): assert local.pyodide_version == "0.28.0" -def test_passthrough(tmp_path, monkeypatch): +def test_passthrough(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: with tmp_path.joinpath("pyproject.toml").open("w") as f: f.write(PYPROJECT_1) @@ -126,7 +126,9 @@ def test_passthrough(tmp_path, monkeypatch): "a trailing backslash \\", ], ) -def test_passthrough_evil(tmp_path, monkeypatch, env_var_value): +def test_passthrough_evil( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, env_var_value: str +) -> None: args = CommandLineArguments.defaults() args.package_dir = tmp_path @@ -157,7 +159,7 @@ xfail_env_parse = pytest.mark.xfail( pytest.param("a trailing backslash \\", marks=[xfail_env_parse]), ], ) -def test_toml_environment_evil(tmp_path, env_var_value): +def test_toml_environment_evil(tmp_path: Path, env_var_value: str) -> None: args = CommandLineArguments.defaults() args.package_dir = tmp_path @@ -296,7 +298,7 @@ def test_container_engine_option( assert parsed_container_engine.disable_host_mount == result_disable_host_mount -def test_environment_pass_references(): +def test_environment_pass_references() -> None: options = Options( platform="linux", command_line_arguments=CommandLineArguments.defaults(), diff --git a/unit_test/options_toml_test.py b/unit_test/options_toml_test.py index c305b0ec..429dba76 100644 --- a/unit_test/options_toml_test.py +++ b/unit_test/options_toml_test.py @@ -1,5 +1,6 @@ import shlex -from typing import TYPE_CHECKING +from pathlib import Path +from typing import Any, cast import pytest @@ -12,9 +13,7 @@ from cibuildwheel.options import ( ShlexTableFormat, _resolve_cascade, ) - -if TYPE_CHECKING: - from pathlib import Path +from cibuildwheel.typing import PlatformName PYPROJECT_1 = """ [tool.cibuildwheel] @@ -39,12 +38,12 @@ test-requires = ["other", "many"] @pytest.fixture(params=["linux", "macos", "windows"]) -def platform(request): - return request.param +def platform(request: pytest.FixtureRequest) -> PlatformName: + return cast("PlatformName", request.param) @pytest.mark.parametrize("fname", ["pyproject.toml", "cibuildwheel.toml"]) -def test_simple_settings(tmp_path, platform, fname): +def test_simple_settings(tmp_path: Path, platform: PlatformName, fname: str) -> None: config_file_path: Path = tmp_path / fname config_file_path.write_text(PYPROJECT_1) @@ -83,7 +82,7 @@ def test_simple_settings(tmp_path, platform, fname): options_reader.get("test-extras", option_format=ShlexTableFormat()) -def test_envvar_override(tmp_path, platform): +def test_envvar_override(tmp_path: Path, platform: PlatformName) -> None: config_file_path: Path = tmp_path / "pyproject.toml" config_file_path.write_text(PYPROJECT_1) @@ -128,7 +127,7 @@ def test_envvar_override(tmp_path, platform): assert options_reader.get("test-command") == "mytest" -def test_project_global_override_default_platform(tmp_path, platform): +def test_project_global_override_default_platform(tmp_path: Path, platform: PlatformName) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -140,14 +139,14 @@ repair-wheel-command = "repair-project-global" assert options_reader.get("repair-wheel-command") == "repair-project-global" -def test_env_global_override_default_platform(platform): +def test_env_global_override_default_platform(platform: PlatformName) -> None: options_reader = OptionsReader( platform=platform, env={"CIBW_REPAIR_WHEEL_COMMAND": "repair-env-global"} ) assert options_reader.get("repair-wheel-command") == "repair-env-global" -def test_env_global_override_project_platform(tmp_path, platform): +def test_env_global_override_project_platform(tmp_path: Path, platform: PlatformName) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -161,7 +160,7 @@ repair-wheel-command = "repair-project-macos" ) options_reader = OptionsReader( pyproject_toml, - platform=platform, + platform=cast("Any", platform), env={ "CIBW_REPAIR_WHEEL_COMMAND": "repair-env-global", }, @@ -169,7 +168,7 @@ repair-wheel-command = "repair-project-macos" assert options_reader.get("repair-wheel-command") == "repair-env-global" -def test_global_platform_order(tmp_path, platform): +def test_global_platform_order(tmp_path: Path, platform: str) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -183,11 +182,11 @@ repair-wheel-command = "repair-project-macos" repair-wheel-command = "repair-project-global" """ ) - options_reader = OptionsReader(pyproject_toml, platform=platform, env={}) + options_reader = OptionsReader(pyproject_toml, platform=cast("Any", platform), env={}) assert options_reader.get("repair-wheel-command") == f"repair-project-{platform}" -def test_unexpected_key(tmp_path): +def test_unexpected_key(tmp_path: Path) -> None: # Note that platform contents are only checked when running # for that platform. pyproject_toml = tmp_path / "pyproject.toml" @@ -204,7 +203,7 @@ repairs-wheel-command = "repair-project-linux" assert "repair-wheel-command" in str(excinfo.value) -def test_underscores_in_key(tmp_path): +def test_underscores_in_key(tmp_path: Path) -> None: # Note that platform contents are only checked when running # for that platform. pyproject_toml = tmp_path / "pyproject.toml" @@ -221,7 +220,7 @@ repair_wheel_command = "repair-project-linux" assert "repair-wheel-command" in str(excinfo.value) -def test_unexpected_table(tmp_path): +def test_unexpected_table(tmp_path: Path) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -233,7 +232,7 @@ repair-wheel-command = "repair-project-linux" OptionsReader(pyproject_toml, platform="linux", env={}) -def test_unsupported_join(tmp_path): +def test_unsupported_join(tmp_path: Path) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -248,7 +247,7 @@ build = ["1", "2"] options_reader.get("build") -def test_disallowed_a(tmp_path): +def test_disallowed_a(tmp_path: Path) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -262,7 +261,7 @@ manylinux-x86_64-image = "manylinux1" OptionsReader(pyproject_toml, platform="windows", disallow=disallow, env={}) -def test_environment_override_empty(tmp_path): +def test_environment_override_empty(tmp_path: Path) -> None: pyproject_toml = tmp_path / "pyproject.toml" pyproject_toml.write_text( """ @@ -296,7 +295,7 @@ manylinux-x86_64-image = "" @pytest.mark.parametrize("ignore_empty", [True, False], ids=["ignore_empty", "no_ignore_empty"]) -def test_resolve_cascade(ignore_empty): +def test_resolve_cascade(ignore_empty: bool) -> None: answer = _resolve_cascade( ("not", InheritRule.NONE), (None, InheritRule.NONE), @@ -327,7 +326,7 @@ def test_resolve_cascade(ignore_empty): @pytest.mark.parametrize("ignore_empty", [True, False], ids=["ignore_empty", "no_ignore_empty"]) @pytest.mark.parametrize("rule", [InheritRule.PREPEND, InheritRule.NONE, InheritRule.APPEND]) -def test_resolve_cascade_merge_list(ignore_empty, rule): +def test_resolve_cascade_merge_list(ignore_empty: bool, rule: InheritRule) -> None: answer = _resolve_cascade( (["a1", "a2"], InheritRule.NONE), ([], InheritRule.NONE), @@ -348,7 +347,7 @@ def test_resolve_cascade_merge_list(ignore_empty, rule): @pytest.mark.parametrize("rule", [InheritRule.PREPEND, InheritRule.NONE, InheritRule.APPEND]) -def test_resolve_cascade_merge_dict(rule): +def test_resolve_cascade_merge_dict(rule: InheritRule) -> None: answer = _resolve_cascade( ({"value": "a1", "base": "b1"}, InheritRule.NONE), (None, InheritRule.NONE), @@ -365,7 +364,7 @@ def test_resolve_cascade_merge_dict(rule): assert answer == "value=override base=b1" -def test_resolve_cascade_merge_strings(): +def test_resolve_cascade_merge_strings() -> None: answer = _resolve_cascade( ("value=a1 base=b1", InheritRule.NONE), ("value=override", InheritRule.APPEND), @@ -374,7 +373,7 @@ def test_resolve_cascade_merge_strings(): assert answer == "value=override base=b1" -def test_resolve_cascade_merge_different_types(): +def test_resolve_cascade_merge_different_types() -> None: answer = _resolve_cascade( ("value=a1 base=b1", InheritRule.NONE), ({"value": "override"}, InheritRule.APPEND), @@ -420,11 +419,13 @@ test-command = ["extra-prepend"] """ -def test_pyproject_2(tmp_path, platform): +def test_pyproject_2(tmp_path: Path, platform: str) -> None: pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text(PYPROJECT_2) - options_reader = OptionsReader(config_file_path=pyproject_toml, platform=platform, env={}) + options_reader = OptionsReader( + config_file_path=pyproject_toml, platform=cast("Any", platform), env={} + ) assert options_reader.get("test-command", option_format=ListFormat(" && ")) == "pyproject" with options_reader.identifier("random"): @@ -451,7 +452,7 @@ def test_pyproject_2(tmp_path, platform): ) -def test_overrides_not_a_list(tmp_path, platform): +def test_overrides_not_a_list(tmp_path: Path, platform: str) -> None: pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text( @@ -465,10 +466,10 @@ test-command = "pyproject-override" ) with pytest.raises(OptionsReaderError): - OptionsReader(config_file_path=pyproject_toml, platform=platform, env={}) + OptionsReader(config_file_path=pyproject_toml, platform=cast("Any", platform), env={}) -def test_config_settings(tmp_path): +def test_config_settings(tmp_path: Path) -> None: pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text( """\ @@ -485,7 +486,7 @@ other = ["two", "three"] ) -def test_pip_config_settings(tmp_path): +def test_pip_config_settings(tmp_path: Path) -> None: pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text( """\ @@ -501,7 +502,7 @@ def test_pip_config_settings(tmp_path): ) -def test_overrides_inherit(tmp_path): +def test_overrides_inherit(tmp_path: Path) -> None: pyproject_toml: Path = tmp_path / "pyproject.toml" pyproject_toml.write_text( """\ diff --git a/unit_test/projectfiles_test.py b/unit_test/projectfiles_test.py index 3bb659f7..3e4068df 100644 --- a/unit_test/projectfiles_test.py +++ b/unit_test/projectfiles_test.py @@ -1,4 +1,5 @@ import tomllib +from pathlib import Path from textwrap import dedent import pytest @@ -10,7 +11,7 @@ from cibuildwheel.projectfiles import ( ) -def test_read_setup_py_simple(tmp_path): +def test_read_setup_py_simple(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -31,7 +32,7 @@ def test_read_setup_py_simple(tmp_path): assert get_requires_python_str(tmp_path, {}) == "1.23" -def test_read_setup_py_if_main(tmp_path): +def test_read_setup_py_if_main(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -53,7 +54,7 @@ def test_read_setup_py_if_main(tmp_path): assert get_requires_python_str(tmp_path, {}) == "1.23" -def test_read_setup_py_if_main_reversed(tmp_path): +def test_read_setup_py_if_main_reversed(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -75,7 +76,7 @@ def test_read_setup_py_if_main_reversed(tmp_path): assert get_requires_python_str(tmp_path, {}) == "1.23" -def test_read_setup_py_if_invalid(tmp_path): +def test_read_setup_py_if_invalid(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -97,7 +98,7 @@ def test_read_setup_py_if_invalid(tmp_path): assert not get_requires_python_str(tmp_path, {}) -def test_read_setup_py_full(tmp_path): +def test_read_setup_py_full(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w", encoding="utf8") as f: f.write( dedent( @@ -123,7 +124,7 @@ def test_read_setup_py_full(tmp_path): assert get_requires_python_str(tmp_path, {}) == "1.24" -def test_read_setup_py_assign(tmp_path): +def test_read_setup_py_assign(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -146,7 +147,7 @@ def test_read_setup_py_assign(tmp_path): assert get_requires_python_str(tmp_path, {}) is None -def test_read_setup_py_None(tmp_path): +def test_read_setup_py_None(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -169,7 +170,7 @@ def test_read_setup_py_None(tmp_path): assert get_requires_python_str(tmp_path, {}) is None -def test_read_setup_py_empty(tmp_path): +def test_read_setup_py_empty(tmp_path: Path) -> None: with open(tmp_path / "setup.py", "w") as f: f.write( dedent( @@ -191,7 +192,7 @@ def test_read_setup_py_empty(tmp_path): assert get_requires_python_str(tmp_path, {}) is None -def test_read_setup_cfg(tmp_path): +def test_read_setup_cfg(tmp_path: Path) -> None: with open(tmp_path / "setup.cfg", "w") as f: f.write( dedent( @@ -207,7 +208,7 @@ def test_read_setup_cfg(tmp_path): assert get_requires_python_str(tmp_path, {}) == "1.234" -def test_read_setup_cfg_empty(tmp_path): +def test_read_setup_cfg_empty(tmp_path: Path) -> None: with open(tmp_path / "setup.cfg", "w") as f: f.write( dedent( @@ -223,7 +224,7 @@ def test_read_setup_cfg_empty(tmp_path): assert get_requires_python_str(tmp_path, {}) is None -def test_read_pyproject_toml(tmp_path): +def test_read_pyproject_toml(tmp_path: Path) -> None: with open(tmp_path / "pyproject.toml", "w") as f: f.write( dedent( @@ -242,7 +243,7 @@ def test_read_pyproject_toml(tmp_path): assert get_requires_python_str(tmp_path, pyproject_toml) == "1.654" -def test_read_pyproject_toml_empty(tmp_path): +def test_read_pyproject_toml_empty(tmp_path: Path) -> None: with open(tmp_path / "pyproject.toml", "w") as f: f.write( dedent( @@ -258,7 +259,7 @@ def test_read_pyproject_toml_empty(tmp_path): assert get_requires_python_str(tmp_path, pyproject_toml) is None -def test_read_dep_groups(): +def test_read_dep_groups() -> None: pyproject_toml = {"dependency-groups": {"group1": ["pkg1", "pkg2"], "group2": ["pkg3"]}} assert resolve_dependency_groups(pyproject_toml) == () assert resolve_dependency_groups(pyproject_toml, "group1") == ("pkg1", "pkg2") @@ -266,11 +267,11 @@ def test_read_dep_groups(): assert resolve_dependency_groups(pyproject_toml, "group1", "group2") == ("pkg1", "pkg2", "pkg3") -def test_dep_group_no_file_error(): +def test_dep_group_no_file_error() -> None: with pytest.raises(FileNotFoundError, match=r"pyproject\.toml"): resolve_dependency_groups(None, "test") -def test_dep_group_no_section_error(): +def test_dep_group_no_section_error() -> None: with pytest.raises(KeyError, match=r"pyproject\.toml"): resolve_dependency_groups({}, "test") diff --git a/unit_test/utils_test.py b/unit_test/utils_test.py index 8b0be33d..3ce67e27 100644 --- a/unit_test/utils_test.py +++ b/unit_test/utils_test.py @@ -1,5 +1,5 @@ import textwrap -from pathlib import PurePath +from pathlib import Path, PurePath from unittest.mock import Mock, call import pytest @@ -18,7 +18,7 @@ from cibuildwheel.util.helpers import ( from cibuildwheel.util.packaging import find_compatible_wheel -def test_format_safe(): +def test_format_safe() -> None: assert format_safe("{wheel}", wheel="filename.whl") == "filename.whl" assert format_safe("command #{wheel}", wheel="filename.whl") == "command {wheel}" assert format_safe("{command #{wheel}}", wheel="filename.whl") == "{command {wheel}}" @@ -37,7 +37,7 @@ def test_format_safe(): assert format_safe("#{not_a_param} {param}", param="1") == "#{not_a_param} 1" -def test_prepare_command(): +def test_prepare_command() -> None: assert prepare_command("python -m {project}", project="project") == "python -m project" assert prepare_command("python -m {something}", project="project") == "python -m {something}" assert ( @@ -103,7 +103,7 @@ def test_find_compatible_wheel_not_found(wheel: str, identifier: str) -> None: assert find_compatible_wheel([PurePath(wheel)], identifier) is None -def test_fix_ansi_codes_for_github_actions(): +def test_fix_ansi_codes_for_github_actions() -> None: input = textwrap.dedent( """ This line is normal @@ -131,7 +131,7 @@ def test_fix_ansi_codes_for_github_actions(): assert output == expected -def test_parse_key_value_string(): +def test_parse_key_value_string() -> None: assert parse_key_value_string("bar", positional_arg_names=["foo"]) == {"foo": ["bar"]} assert parse_key_value_string("foo:bar", kw_arg_names=["foo"]) == {"foo": ["bar"]} with pytest.raises(ValueError, match="Too many positional arguments"): @@ -212,7 +212,7 @@ def test_parse_key_value_string(): } -def test_flexible_version_comparisons(): +def test_flexible_version_comparisons() -> None: assert FlexibleVersion("2.0") == FlexibleVersion("2") assert FlexibleVersion("2.0") < FlexibleVersion("2.1") assert FlexibleVersion("2.1") > FlexibleVersion("2") @@ -227,7 +227,7 @@ def test_flexible_version_comparisons(): @pytest.fixture -def sample_project(tmp_path): +def sample_project(tmp_path: Path) -> Path: """Create a directory structure that contains a range of files.""" project_path = tmp_path / "project" @@ -324,7 +324,13 @@ def sample_project(tmp_path): ), ], ) -def test_copy_test_sources(tmp_path, sample_project, test_sources, expected, not_expected): +def test_copy_test_sources( + tmp_path: Path, + sample_project: Path, + test_sources: list[str], + expected: list[str], + not_expected: list[str], +) -> None: """Test sources can be copied into the test directory.""" target = tmp_path / "somewhere/test_cwd" copy_test_sources(test_sources, sample_project, target) @@ -336,7 +342,7 @@ def test_copy_test_sources(tmp_path, sample_project, test_sources, expected, not assert not (tmp_path / "somewhere/test_cwd" / path).exists() -def test_copy_test_sources_missing_file(tmp_path, sample_project): +def test_copy_test_sources_missing_file(tmp_path: Path, sample_project: Path) -> None: """If test_sources references a folder that doesn't exist, an error is raised.""" with pytest.raises( @@ -350,7 +356,7 @@ def test_copy_test_sources_missing_file(tmp_path, sample_project): ) -def test_copy_test_sources_alternate_copy_into(sample_project): +def test_copy_test_sources_alternate_copy_into(sample_project: Path) -> None: """If an alternate copy_into method is provided, it is used.""" target = PurePath("/container/test_cwd") @@ -367,7 +373,7 @@ def test_copy_test_sources_alternate_copy_into(sample_project): ) -def test_unwrap(): +def test_unwrap() -> None: assert ( unwrap(""" This is a @@ -378,7 +384,7 @@ def test_unwrap(): ) -def test_unwrap_preserving_paragraphs(): +def test_unwrap_preserving_paragraphs() -> None: assert ( unwrap(""" This is a diff --git a/unit_test/wheel_print_test.py b/unit_test/wheel_print_test.py index b7e6953f..823dafd5 100644 --- a/unit_test/wheel_print_test.py +++ b/unit_test/wheel_print_test.py @@ -9,7 +9,7 @@ OPTIONS_DEFAULTS = Options("linux", CommandLineArguments.defaults(), {}, default FILE = Path(__file__) -def test_printout_wheels(capsys): +def test_printout_wheels(capsys: pytest.CaptureFixture[str]) -> None: log = Logger() log.fold_mode = "disabled" log.colors_enabled = False @@ -32,7 +32,7 @@ def test_printout_wheels(capsys): assert "SHA256=" in captured.out -def test_no_printout_on_error(capsys): +def test_no_printout_on_error(capsys: pytest.CaptureFixture[str]) -> None: log = Logger() with pytest.raises(RuntimeError), log.print_summary(options=OPTIONS_DEFAULTS): raise RuntimeError()