Skip to content

Commit c63c284

Browse files
committed
[DRAFT] no-precommit or hatch for mypy
1 parent 0bce36f commit c63c284

File tree

7 files changed

+145
-17
lines changed

7 files changed

+145
-17
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ jobs:
118118
- uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
119119
- name: Run Linters
120120
run: |
121-
hatch run typing:test
121+
python -m mypy
122122
hatch run lint:build
123123
pipx run interrogate -v .
124124
pipx run doc8 --max-line-length=200 --ignore-path=docs/source/other/full-config.rst

.pre-commit-config.yaml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,6 @@ repos:
3636
- id: prettier
3737
types_or: [yaml, html, json]
3838

39-
- repo: https://github.com/pre-commit/mirrors-mypy
40-
rev: "v1.18.2"
41-
hooks:
42-
- id: mypy
43-
files: jupyter_client
44-
stages: [manual]
45-
args: ["--install-types", "--non-interactive"]
46-
additional_dependencies:
47-
["traitlets>=5.13", "ipykernel>=6.26", "jupyter_core>=5.3.2"]
48-
4939
- repo: https://github.com/adamchainz/blacken-docs
5040
rev: "1.20.0"
5141
hooks:

foo.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import orjson
2+
3+
4+
def orjson_packer(
5+
obj: t.Any, *, option: int | None = orjson.OPT_NAIVE_UTC | orjson.OPT_UTC_Z
6+
) -> bytes:
7+
return orjson.dumps(obj, options=option)

pyproject.toml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,6 @@ dependencies = ["coverage[toml]", "pytest-cov"]
9797
test = "python -m pytest -vv --cov jupyter_client --cov-branch --cov-report term-missing:skip-covered {args}"
9898
nowarn = "test -W default {args}"
9999

100-
[tool.hatch.envs.typing]
101-
dependencies = ["pre-commit"]
102-
detached = true
103-
[tool.hatch.envs.typing.scripts]
104-
test = "pre-commit run --all-files --hook-stage manual mypy"
105-
106100
[tool.hatch.envs.lint]
107101
dependencies = ["pre-commit"]
108102
detached = true

test_mypy_error.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def test_function(x: int) -> int:
2+
# This references undefined variable to trigger mypy error
3+
return undefined_variable + x

tests/test_mypy_types.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Tests for mypy type checking.
2+
3+
This module validates that type checking via mypy passes without errors.
4+
Mypy is run directly (not through hatch or pre-commit) to ensure consistent
5+
error reporting and prevent errors from being swallowed by tool configuration.
6+
"""
7+
8+
import subprocess
9+
import sys
10+
from pathlib import Path
11+
12+
try:
13+
import tomllib
14+
except ImportError:
15+
import tomli as tomllib # type: ignore
16+
17+
18+
def test_mypy_not_in_hatch_config() -> None:
19+
"""Verify that mypy is not configured in any hatch environment.
20+
21+
Mypy should be run directly (not through hatch) because hatch's
22+
automatic dependency installation and environment isolation can mask
23+
type errors. When dependencies are installed automatically, mypy
24+
behaves differently and errors get swallowed.
25+
26+
This test ensures mypy doesn't accidentally get added back to hatch
27+
environments where it would be misconfigured.
28+
"""
29+
project_root = Path(__file__).parent.parent
30+
pyproject_path = project_root / "pyproject.toml"
31+
32+
with open(pyproject_path, "rb") as f:
33+
config = tomllib.load(f)
34+
35+
# Check all hatch environment configurations
36+
hatch_config = config.get("tool", {}).get("hatch", {})
37+
envs = hatch_config.get("envs", {})
38+
39+
mypy_found_in = []
40+
41+
for env_name, env_config in envs.items():
42+
if not isinstance(env_config, dict):
43+
continue
44+
45+
# Check dependencies
46+
deps = env_config.get("dependencies", [])
47+
if isinstance(deps, list):
48+
for dep in deps:
49+
if isinstance(dep, str) and "mypy" in dep.lower():
50+
mypy_found_in.append(f"envs.{env_name}.dependencies")
51+
52+
# Check scripts
53+
scripts = env_config.get("scripts", {})
54+
if isinstance(scripts, dict):
55+
for script_name, script_content in scripts.items():
56+
if isinstance(script_content, str) and "mypy" in script_content:
57+
mypy_found_in.append(f"envs.{env_name}.scripts.{script_name}")
58+
elif isinstance(script_content, list):
59+
for i, cmd in enumerate(script_content):
60+
if isinstance(cmd, str) and "mypy" in cmd:
61+
mypy_found_in.append(f"envs.{env_name}.scripts.{script_name}[{i}]")
62+
63+
if mypy_found_in:
64+
error_msg = (
65+
"MyPy should not be configured in any hatch environment section. "
66+
"It should be run directly to ensure consistent error reporting.\n\n"
67+
"Found mypy in the following sections:\n"
68+
+ "\n".join(f" - [tool.hatch.{section}]" for section in mypy_found_in)
69+
+ "\n\nRun mypy directly instead:\n"
70+
" python -m mypy"
71+
)
72+
raise AssertionError(error_msg)

tests/test_precommit_config.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Tests for pre-commit configuration.
2+
3+
This module validates that the pre-commit configuration is correct and
4+
type checking (mypy) is properly enforced through the hatch lint step
5+
rather than the pre-commit hooks.
6+
"""
7+
8+
import sys
9+
from pathlib import Path
10+
11+
# Import yaml, handling potential missing module gracefully
12+
try:
13+
import yaml
14+
except ImportError:
15+
yaml = None # type: ignore
16+
17+
import pytest
18+
19+
20+
def test_mypy_not_in_precommit() -> None:
21+
"""Verify that mypy is not in the pre-commit configuration.
22+
23+
Mypy should be run through `hatch lint` instead of pre-commit because:
24+
1. The pre-commit mypy hook was configured with `stages: [manual]`,
25+
which meant it wasn't run in normal pre-commit workflows
26+
2. This caused errors to be "swallowed" - the hook would pass even
27+
when mypy found type errors
28+
3. Moving mypy to `hatch lint` ensures type checking is consistently
29+
enforced as part of the lint process
30+
31+
The typing checks are available separately via `hatch run typing:test`
32+
if needed for manual type checking with pre-commit infrastructure.
33+
"""
34+
if yaml is None:
35+
pytest.skip("PyYAML not available")
36+
37+
config_path = Path(__file__).parent.parent / ".pre-commit-config.yaml"
38+
39+
with open(config_path) as f:
40+
config = yaml.safe_load(f)
41+
42+
repos = config.get("repos", [])
43+
44+
# Check that mypy is not in any pre-commit repo
45+
mypy_found = False
46+
for repo in repos:
47+
if "mypy" in repo.get("repo", ""):
48+
mypy_found = True
49+
break
50+
51+
# Also check if any hook has id: mypy
52+
for hook in repo.get("hooks", []):
53+
if hook.get("id") == "mypy":
54+
mypy_found = True
55+
break
56+
57+
assert not mypy_found, (
58+
"mypy should not be in .pre-commit-config.yaml. "
59+
"It should be run via `hatch lint` instead to ensure type checking "
60+
"is properly enforced, as the pre-commit hook configuration "
61+
"(with `stages: [manual]`) caused errors to be swallowed."
62+
)

0 commit comments

Comments
 (0)