Compare commits

...

2 Commits

Author SHA1 Message Date
abmantis c3cdafe24e Add scripts 2026-05-22 18:53:42 +01:00
abmantis 167e6d2bd8 Enable mypy explicit-override check 2026-05-22 15:08:45 +01:00
4 changed files with 82 additions and 1 deletions
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env python3
"""Add @override decorator to methods listed in a mypy error log."""
from __future__ import annotations
import argparse
from collections import defaultdict
from pathlib import Path
import re
import subprocess
ERROR_RE = re.compile(r"^(.+?):(\d+): error:.*\[explicit-override\]")
def decorator_stack_top(lines: list[str], def_idx: int) -> int:
"""Return the index of the topmost decorator above the def at def_idx."""
i = def_idx - 1
while i >= 0 and lines[i].lstrip().startswith("@"):
i -= 1
return i + 1
def main() -> None:
"""Run the script."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"input",
nargs="?",
default="explicit_override_errors.txt",
type=Path,
help="Path to the mypy error log (default: explicit_override_errors.txt)",
)
args = parser.parse_args()
by_file: dict[Path, set[int]] = defaultdict(set)
for line in args.input.read_text().splitlines():
if m := ERROR_RE.match(line):
by_file[Path(m.group(1))].add(int(m.group(2)))
for path, line_nums in by_file.items():
lines = path.read_text().splitlines(keepends=True)
for lineno in sorted(line_nums, reverse=True):
insert_idx = decorator_stack_top(lines, lineno - 1)
target = lines[insert_idx]
indent = target[: len(target) - len(target.lstrip())]
lines.insert(insert_idx, f"{indent}@override\n")
first_import = next(
i for i, ln in enumerate(lines) if ln.startswith(("import ", "from "))
)
lines.insert(first_import, "from typing import override\n")
path.write_text("".join(lines))
print(f"Updated {path} ({len(line_nums)} methods)")
if by_file:
subprocess.run(["ruff", "check", "--fix", *map(str, by_file)], check=False)
if __name__ == "__main__":
main()
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env python3
"""Run mypy on a directory and write `[explicit-override]` errors to a file."""
from __future__ import annotations
from pathlib import Path
import subprocess
import sys
OUTPUT = Path("explicit_override_errors.txt")
target = sys.argv[1]
result = subprocess.run(
["mypy", "--enable-error-code=explicit-override", target],
capture_output=True,
text=True,
check=False,
)
matches = [line for line in result.stdout.splitlines() if "[explicit-override]" in line]
OUTPUT.write_text("\n".join(matches) + ("\n" if matches else ""))
print(f"Wrote {len(matches)} errors to {OUTPUT}")
Generated
+1 -1
View File
@@ -17,7 +17,7 @@ no_implicit_optional = true
warn_incomplete_stub = true
warn_redundant_casts = true
warn_unused_ignores = true
enable_error_code = deprecated, ignore-without-code, redundant-self, truthy-iterable
enable_error_code = deprecated, explicit-override, ignore-without-code, redundant-self, truthy-iterable
disable_error_code = annotation-unchecked, import-not-found, import-untyped
extra_checks = false
check_untyped_defs = true
+1
View File
@@ -54,6 +54,7 @@ GENERAL_SETTINGS: Final[dict[str, str]] = {
"enable_error_code": ", ".join( # noqa: FLY002
[
"deprecated",
"explicit-override",
"ignore-without-code",
"redundant-self",
"truthy-iterable",