Skip to content

Commit baa8bfb

Browse files
Merge pull request #885 from dhellmann/mypy-coverage-2
feat(mypy): enable comprehensive strict type checking across codebase
2 parents f5b2d93 + 012052c commit baa8bfb

31 files changed

+232
-106
lines changed

AGENTS.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
### Do
2020

21-
- **Type annotations REQUIRED** on ALL functions including tests. Use syntax compatible with Python 3.11+
22-
- Use `X | None` not `Optional[X]`
2321
- Add docstrings on all public functions and classes
2422
- Use file-scoped commands for fast feedback (see below)
2523
- Follow existing patterns - search codebase for similar code
@@ -29,8 +27,6 @@
2927

3028
### Don't
3129

32-
- Don't use `Optional[X]` syntax (use `X | None`)
33-
- Don't omit type annotations or return types
3430
- Don't run full test suite for small changes (use file-scoped)
3531
- Don't create temporary helper scripts or workarounds
3632
- Don't commit without running quality checks

CONTRIBUTING.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ git remote add upstream https://github.com/python-wheel-build/fromager.git
4444

4545
# 4. Create development environment
4646
hatch env create
47+
48+
# 5. Install pre-commit hook (optional but recommended)
49+
./scripts/setup-pre-commit-hook.sh
4750
```
4851

4952
### Contribution Workflow
@@ -72,6 +75,25 @@ git push origin feat/<short-description>
7275
# 7. Create a pull request on GitHub
7376
```
7477

78+
### Pre-commit Hook (Recommended)
79+
80+
To ensure quality checks run automatically before each commit, install the pre-commit hook that runs both linting and mypy type checking:
81+
82+
```bash
83+
# Install the pre-commit hook (run once after cloning)
84+
./scripts/setup-pre-commit-hook.sh
85+
86+
# The hook automatically runs before each commit:
87+
# - hatch run lint:check
88+
# - hatch run mypy:check
89+
90+
# If the hook fails, it will prevent the commit and show helpful messages
91+
# You can fix issues automatically with:
92+
hatch run lint:fix
93+
```
94+
95+
The pre-commit hook prevents commits that would fail CI quality checks, saving time and ensuring consistent code quality.
96+
7597
---
7698

7799
## Coding Standards
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
import logging
2+
import pathlib
3+
import typing
24

3-
from fromager import external_commands
5+
from packaging.requirements import Requirement
6+
from packaging.version import Version
7+
8+
from fromager import build_environment, context, external_commands
49

510
logger = logging.getLogger(__name__)
611

712

8-
def build_wheel(ctx, build_env, extra_environ, req, sdist_root_dir, version):
13+
def build_wheel(
14+
ctx: context.WorkContext,
15+
build_env: build_environment.BuildEnvironment,
16+
extra_environ: dict[str, str],
17+
req: Requirement,
18+
sdist_root_dir: pathlib.Path,
19+
version: Version,
20+
) -> None:
921
# flit_core is a basic build system dependency for several
1022
# packages. It is capable of building its own wheels, so we use the
1123
# bootstrapping instructions to do that and put the wheel in the
@@ -15,8 +27,8 @@ def build_wheel(ctx, build_env, extra_environ, req, sdist_root_dir, version):
1527
# https://flit.pypa.io/en/stable/bootstrap.html
1628
logger.info('using override to build flit_core wheel in %s', sdist_root_dir)
1729
external_commands.run(
18-
[build_env.python, '-m', 'flit_core.wheel',
19-
'--outdir', ctx.wheels_build],
20-
cwd=sdist_root_dir,
30+
[str(build_env.python), '-m', 'flit_core.wheel',
31+
'--outdir', str(ctx.wheels_build)],
32+
cwd=str(sdist_root_dir),
2133
extra_environ=extra_environ,
2234
)

e2e/fromager_hooks/src/package_plugins/hooks.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def after_build_wheel(
1515
dist_version: str,
1616
sdist_filename: pathlib.Path,
1717
wheel_filename: pathlib.Path,
18-
):
18+
) -> None:
1919
logger.info(
2020
f"running post build hook in {__name__} for {sdist_filename} and {wheel_filename}"
2121
)
@@ -31,7 +31,7 @@ def after_bootstrap(
3131
dist_version: str,
3232
sdist_filename: pathlib.Path | None,
3333
wheel_filename: pathlib.Path | None,
34-
):
34+
) -> None:
3535
logger.info(
3636
f"running post bootstrap hook in {__name__} for {sdist_filename} and {wheel_filename}"
3737
)
@@ -46,7 +46,7 @@ def after_prebuilt_wheel(
4646
dist_name: str,
4747
dist_version: str,
4848
wheel_filename: pathlib.Path,
49-
):
49+
) -> None:
5050
logger.info(
5151
f"running post build hook in {__name__} for {wheel_filename}"
5252
)

e2e/setup_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import sys
66
import site
77

8-
def setup_coverage():
8+
def setup_coverage() -> None:
99
"""Create coverage.pth file for subprocess coverage collection."""
1010
# Get the virtual environment root
1111
venv_root = pathlib.Path(sys.prefix)

pyproject.toml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,18 @@ ignore = [
187187
known-first-party = ["fromager"]
188188

189189
[tool.mypy]
190-
mypy_path = ["src"]
190+
mypy_path = ["src", "tests", "e2e"]
191191
# TODO: tighten type checks
192-
# check_untyped_defs = true
193-
# disallow_incomplete_defs = true
194-
# disallow_untyped_defs = true
195-
# warn_return_any = true
192+
check_untyped_defs = true
193+
disallow_incomplete_defs = true
194+
disallow_untyped_defs = true
195+
warn_return_any = true
196196

197197
# TODO: remove excludes and silent follow
198198
follow_imports = "silent"
199199
exclude = [
200-
"^src/fromager/sources\\.py$",
201-
"^src/fromager/sdist\\.py$",
202-
"^src/fromager/commands/build\\.py$",
203-
"^src/fromager/settings\\.py$",
200+
"^e2e/.*/build/.*$",
201+
"^e2e/pyo3_test/.*$",
204202
]
205203

206204
[[tool.mypy.overrides]]
@@ -274,6 +272,7 @@ features = ["mypy"]
274272
check = [
275273
"mypy -p fromager",
276274
"mypy tests/",
275+
"mypy e2e/",
277276
]
278277

279278
[tool.hatch.envs.docs]

scripts/pre-commit

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Git pre-commit hook to run quality checks
4+
# This ensures all commits pass linting and type checking
5+
#
6+
7+
set -e # Exit immediately if any command fails
8+
9+
echo "Running pre-commit checks..."
10+
11+
# Run lint check
12+
echo "🔍 Running lint check..."
13+
if ! hatch run lint:check; then
14+
echo "❌ Lint check failed. Please fix linting issues and try again."
15+
echo " You can run 'hatch run lint:fix' to automatically fix many issues."
16+
exit 1
17+
fi
18+
19+
# Run mypy type checking
20+
echo "🔍 Running mypy type check..."
21+
if ! hatch run mypy:check; then
22+
echo "❌ MyPy type check failed. Please fix type annotations and try again."
23+
exit 1
24+
fi
25+
26+
echo "✅ All pre-commit checks passed!"

scripts/setup-pre-commit-hook.sh

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Setup script to install the pre-commit hook for fromager development
4+
#
5+
# This script copies the pre-commit hook to .git/hooks/ and makes it executable
6+
#
7+
8+
set -e
9+
10+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
12+
HOOKS_DIR="$REPO_ROOT/.git/hooks"
13+
PRE_COMMIT_HOOK="$HOOKS_DIR/pre-commit"
14+
15+
echo "🔧 Setting up pre-commit hook for fromager..."
16+
17+
# Check if we're in a git repository
18+
if [ ! -d "$HOOKS_DIR" ]; then
19+
echo "❌ Error: Not in a git repository or .git/hooks directory not found"
20+
exit 1
21+
fi
22+
23+
# Copy the pre-commit hook script
24+
cp "$SCRIPT_DIR/pre-commit" "$PRE_COMMIT_HOOK"
25+
26+
# Make it executable
27+
chmod +x "$PRE_COMMIT_HOOK"
28+
29+
echo "✅ Pre-commit hook installed successfully!"
30+
echo ""
31+
echo "The hook will now run 'hatch run lint:check' and 'hatch run mypy:check'"
32+
echo "before every commit to ensure code quality."
33+
echo ""
34+
echo "To bypass the hook for a specific commit (not recommended):"
35+
echo " git commit --no-verify -m \"message\""

src/fromager/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ def main(
258258
main.add_command(cmd)
259259

260260

261-
def _format_exception(exc):
261+
def _format_exception(exc: BaseException) -> str:
262262
if exc.__cause__:
263263
cause = _format_exception(exc.__cause__)
264264
return f"{exc} because {cause}"

src/fromager/bootstrapper.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def _build_sdist(
292292
self.ctx, self.ctx.sdists_builds, req, str(resolved_version)
293293
)
294294
if not find_sdist_result:
295-
sdist_filename = sources.build_sdist(
295+
sdist_filename: pathlib.Path = sources.build_sdist(
296296
ctx=self.ctx,
297297
req=req,
298298
version=resolved_version,
@@ -1015,7 +1015,9 @@ def _resolve_from_version_source(
10151015
logger.debug(f"could not resolve {req} from {version_source}: {err}")
10161016
return None
10171017

1018-
def _create_unpack_dir(self, req: Requirement, resolved_version: Version):
1018+
def _create_unpack_dir(
1019+
self, req: Requirement, resolved_version: Version
1020+
) -> pathlib.Path:
10191021
unpack_dir = self.ctx.work_dir / f"{req.name}-{resolved_version}"
10201022
unpack_dir.mkdir(parents=True, exist_ok=True)
10211023
return unpack_dir

0 commit comments

Comments
 (0)