Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ repos:
- nox
- packaging
- pygithub
- pytest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does having so many non pinned dependencies do not make pre-commit mypy fragile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little (though pytest isn't too bad). Though generally if something changes we just adapt to the change.

Pre-commit doesn't have a way to pin these. Maybe eventually uv will.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-commit doesn't have a way to pin these. Maybe eventually uv will.

What did you mean by uv will? pre-commit.ci do not allow to pass any constraints.

The only possible way that I see is to have own CI job to bump constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean if uv adds multiple independent locked environments (and tasks), we could use that to move this check to uv.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in napari (https://github.com/napari/napari) we use uv to compile a constraints file and call it from tox.

Maybe the same approach could be used here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this becomes too much of a problem, we could probably do something (setting it up with nox would be easy). But for now it's fine, and long term we might end up moving from mypy to red-knot anyway.

- rich
- tomli
- tomli_w
Expand All @@ -40,6 +41,7 @@ repos:
- types-jinja2
- types-pyyaml
- types-requests
- types-setuptools
- uv
- validate-pyproject
- id: mypy
Expand Down
18 changes: 10 additions & 8 deletions bin/make_dependency_update_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,29 @@
from __future__ import annotations

import os
import subprocess
import sys
import textwrap
import time
from pathlib import Path
from subprocess import run

import click


def shell(cmd, *, check: bool, **kwargs):
return run([cmd], shell=True, check=check, **kwargs)
def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]


def git_repo_has_changes():
unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
def git_repo_has_changes() -> bool:
unstaged_changes: bool = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
staged_changes: bool = (
shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
)
return unstaged_changes or staged_changes


@click.command()
def main():
def main() -> None:
project_root = Path(__file__).parent / ".."
os.chdir(project_root)

Expand Down Expand Up @@ -57,7 +59,7 @@ def main():
PR generated by `{os.path.basename(__file__)}`.
"""
)
run(
subprocess.run(
[
"gh",
"pr",
Expand Down
6 changes: 4 additions & 2 deletions bin/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ def get_projects(
return sorted((Project(item, github) for item in config), reverse=online)


def render_projects(projects: Sequence[Project], *, dest_path: Path, include_info: bool = True):
def render_projects(
projects: Sequence[Project], *, dest_path: Path, include_info: bool = True
) -> str:
io = StringIO()
print = functools.partial(builtins.print, file=io)

Expand Down Expand Up @@ -203,7 +205,7 @@ def insert_projects_table(
projects: Sequence[Project],
input_filename: str,
include_info: bool = True,
):
) -> None:
text = file.read_text()
projects_table = render_projects(projects, include_info=include_info, dest_path=file)

Expand Down
16 changes: 8 additions & 8 deletions bin/run_example_ci_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,29 @@

import os
import shutil
import subprocess
import sys
import textwrap
import time
import typing
from glob import glob
from pathlib import Path
from subprocess import run
from urllib.parse import quote

import click


def shell(cmd, *, check: bool, **kwargs):
return run([cmd], shell=True, check=check, **kwargs)
def shell(cmd: str, *, check: bool, **kwargs: object) -> subprocess.CompletedProcess[str]:
return subprocess.run([cmd], shell=True, check=check, **kwargs) # type: ignore[call-overload, no-any-return]


def git_repo_has_changes():
def git_repo_has_changes() -> bool:
unstaged_changes = shell("git diff-index --quiet HEAD --", check=False).returncode != 0
staged_changes = shell("git diff-index --quiet --cached HEAD --", check=False).returncode != 0
return unstaged_changes or staged_changes


def generate_basic_project(path):
def generate_basic_project(path: Path) -> None:
sys.path.insert(0, "")
from test.test_projects.c import new_c_project

Expand Down Expand Up @@ -79,7 +79,7 @@ class CIService(typing.NamedTuple):
]


def ci_service_for_config_file(config_file):
def ci_service_for_config_file(config_file: str) -> CIService:
filename = Path(config_file).name

try:
Expand Down Expand Up @@ -134,7 +134,7 @@ def run_example_ci_configs(config_files=None):
dst_config_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(src_config_file, dst_config_file)

run(["git", "add", example_project], check=True)
subprocess.run(["git", "add", example_project], check=True)
message = textwrap.dedent(
f"""\
Test example minimal configs
Expand All @@ -144,7 +144,7 @@ def run_example_ci_configs(config_files=None):
Time: {timestamp}
"""
)
run(["git", "commit", "--no-verify", "--message", message], check=True)
subprocess.run(["git", "commit", "--no-verify", "--message", message], check=True)
shell(f"git subtree --prefix={example_project} push origin {branch_name}", check=True)

print("---")
Expand Down
2 changes: 1 addition & 1 deletion bin/update_how_it_works_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
)


def main():
def main() -> None:
subprocess.run(["mkdocs", "build"], check=True)

hti = Html2Image(custom_flags=["--force-device-scale-factor=2"])
Expand Down
2 changes: 1 addition & 1 deletion bin/update_readme_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)


def main():
def main() -> None:
changelog_text = CHANGELOG_FILE.read_text()
readme_text = README_FILE.read_text()

Expand Down
2 changes: 1 addition & 1 deletion bin/update_virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class VersionTuple:
version_string: str


def git_ls_remote_versions(url) -> list[VersionTuple]:
def git_ls_remote_versions(url: str) -> list[VersionTuple]:
versions: list[VersionTuple] = []
tags = subprocess.run(
["git", "ls-remote", "--tags", url], check=True, text=True, capture_output=True
Expand Down
2 changes: 1 addition & 1 deletion docs/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
def define_env(env: Any) -> None:
"Hook function for mkdocs-macros"

@env.macro
@env.macro # type: ignore[misc]
def subprocess_run(*args: str) -> str:
"Run a subprocess and return the stdout"
env = os.environ.copy()
Expand Down
19 changes: 3 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,33 +113,20 @@ files = [
]
warn_unused_configs = true

warn_redundant_casts = true
no_implicit_reexport = true
strict_equality = true
warn_unused_ignores = true
check_untyped_defs = true

disallow_subclassing_any = true
disallow_any_generics = true
warn_return_any = true
no_implicit_optional = true
strict = true
disallow_untyped_defs = false

enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
warn_unreachable = true
warn_unreachable = false

[[tool.mypy.overrides]]
module = "cibuildwheel.*"
disallow_untyped_defs = true
disallow_untyped_calls = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true

[[tool.mypy.overrides]]
module = [
"setuptools",
"setuptools._distutils", # needed even if only directly import setuptools._distutils.util
"setuptools._distutils.util",
"pytest", # ignored in pre-commit to speed up check
"bashlex",
"bashlex.*",
"importlib_resources",
Expand Down
2 changes: 1 addition & 1 deletion test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from .utils import EMULATED_ARCHS, platform


def pytest_addoption(parser) -> None:
def pytest_addoption(parser: pytest.Parser) -> None:
parser.addoption(
"--run-emulation",
action="store",
Expand Down
8 changes: 4 additions & 4 deletions test/test_custom_repair_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ def test(tmp_path, capfd):
basic_project.generate(project_dir)

num_builds = len(utils.cibuildwheel_get_build_identifiers(project_dir))
if num_builds > 1:
expectation = pytest.raises(subprocess.CalledProcessError)
else:
expectation = does_not_raise()
expectation = (
pytest.raises(subprocess.CalledProcessError) if num_builds > 1 else does_not_raise()
)

with expectation as exc_info:
result = utils.cibuildwheel_run(
Expand All @@ -48,6 +47,7 @@ def test(tmp_path, capfd):

captured = capfd.readouterr()
if num_builds > 1:
assert exc_info is not None
assert "Build failed because a wheel named" in captured.err
assert exc_info.value.returncode == 6
else:
Expand Down
5 changes: 3 additions & 2 deletions test/test_dependency_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import platform
import re
import textwrap
from pathlib import Path

import pytest

Expand Down Expand Up @@ -46,8 +47,8 @@
VERSION_REGEX = r"([\w-]+)==([^\s]+)"


def get_versions_from_constraint_file(constraint_file):
constraint_file_text = constraint_file.read_text(encoding="utf8")
def get_versions_from_constraint_file(constraint_file: Path) -> dict[str, str]:
constraint_file_text = constraint_file.read_text(encoding="utf-8")

return dict(re.findall(VERSION_REGEX, constraint_file_text))

Expand Down
7 changes: 6 additions & 1 deletion test/test_from_sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
import sys
import textwrap
from collections.abc import Mapping
from pathlib import Path
from tempfile import TemporaryDirectory

Expand All @@ -28,7 +29,11 @@ def make_sdist(project: TestProject, working_dir: Path) -> Path:
return next(sdist_dir.glob("*.tar.gz"))


def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None):
def cibuildwheel_from_sdist_run(
sdist_path: Path | str,
add_env: Mapping[str, str] | None = None,
config_file: str | None = None,
) -> list[str]:
env = os.environ.copy()

if add_env:
Expand Down
2 changes: 1 addition & 1 deletion test/test_projects/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pathlib import Path


def main():
def main() -> None:
parser = ArgumentParser(
prog="python -m test.test_projects", description="Generate a test project to check it out"
)
Expand Down
6 changes: 3 additions & 3 deletions test/test_projects/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ class TestProject:
files: FilesDict
template_context: TemplateContext

def __init__(self):
def __init__(self) -> None:
self.files = {}
self.template_context = {}

def generate(self, path: Path):
def generate(self, path: Path) -> None:
for filename, content in self.files.items():
file_path = path / filename
file_path.parent.mkdir(parents=True, exist_ok=True)
Expand All @@ -37,7 +37,7 @@ def generate(self, path: Path):

f.write(content)

def copy(self):
def copy(self) -> TestProject:
other = TestProject()
other.files = self.files.copy()
other.template_context = self.template_context.copy()
Expand Down
14 changes: 7 additions & 7 deletions test/test_projects/c.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@

def new_c_project(
*,
spam_c_top_level_add="",
spam_c_function_add="",
setup_py_add="",
setup_py_extension_args_add="",
setup_py_setup_args_add="",
setup_cfg_add="",
):
spam_c_top_level_add: str = "",
spam_c_function_add: str = "",
setup_py_add: str = "",
setup_py_extension_args_add: str = "",
setup_py_setup_args_add: str = "",
setup_cfg_add: str = "",
) -> TestProject:
project = TestProject()

project.files.update(
Expand Down
2 changes: 1 addition & 1 deletion test/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def test_failing_test(tmp_path):
@pytest.mark.parametrize("test_runner", ["pytest", "unittest"])
def test_bare_pytest_invocation(
tmp_path: Path, capfd: pytest.CaptureFixture[str], test_runner: str
):
) -> None:
"""Check that if a user runs pytest in the the test cwd, it raises a helpful error"""
project_dir = tmp_path / "project"
output_dir = tmp_path / "output"
Expand Down
2 changes: 1 addition & 1 deletion test/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
basic_project = test_projects.new_c_project()


def skip_if_no_msvc(arm64=False):
def skip_if_no_msvc(arm64: bool = False) -> None:
programfiles = os.getenv("PROGRAMFILES(X86)", "") or os.getenv("PROGRAMFILES", "")
if not programfiles:
pytest.skip("Requires %PROGRAMFILES(X86)% variable to be set")
Expand Down
Loading