tests: fully type the test suite (#2794)

* tests: fully type the test suite

* chore: require more typing

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Henry Schreiner <henryfs@princeton.edu>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Henry Schreiner
2026-04-01 15:23:02 +01:00
committed by GitHub
co-authored by pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
parent 643b30c796
commit 097806b6b1
58 changed files with 627 additions and 339 deletions
+1 -1
View File
@@ -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")
+12 -5
View File
@@ -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)
+5 -2
View File
@@ -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)
+43 -23
View File
@@ -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.
"""
+4 -3
View File
@@ -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)
+4 -3
View File
@@ -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)
+5 -3
View File
@@ -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"
+4 -1
View File
@@ -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)
+2 -1
View File
@@ -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)
+6 -2
View File
@@ -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")
+2 -1
View File
@@ -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"]:
+5 -3
View File
@@ -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()
+3 -2
View File
@@ -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("""
+9 -3
View File
@@ -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")
+4 -3
View File
@@ -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")
+4 -3
View File
@@ -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
+6 -4
View File
@@ -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",
+23 -13
View File
@@ -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()
+2 -1
View File
@@ -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()
+23 -7
View File
@@ -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")
+2 -1
View File
@@ -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")
+3 -1
View File
@@ -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"
+2 -1
View File
@@ -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")
+2 -1
View File
@@ -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)
+2 -1
View File
@@ -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"
+7 -6
View File
@@ -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")
+2 -1
View File
@@ -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"
+2 -1
View File
@@ -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)
+10 -10
View File
@@ -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)
+12 -2
View File
@@ -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")
+3 -1
View File
@@ -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")
+3 -1
View File
@@ -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")
+2 -2
View File
@@ -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)