WUT_Computer_Science/.pre-commit-config.yaml
Krzysztof kuhy Rudnicki 2a59de6363 The six mypy errors are gone: four were fixable, two are scoped
The note in TODO-refactor-remaining.md said ERSMS's four were structural and
that a fix "would rewrite both models". Both halves were wrong.
flask-sqlalchemy 3.1.1 does ship py.typed; the real blocker was that
`class User(db.Model)` is attribute access, which mypy cannot resolve in a
base-class position.

backend/models.py now declares a named DeclarativeBase with SQLAlchemy 2.0
Mapped[] columns. The Model mixin goes away with the base, so the six legacy
Model.query call sites became db.session selects and the two table names are
spelled out rather than derived.

Verified by driving the real routes through Flask's test client with Firebase
stubbed out -- 16 scenarios covering every one of the six call sites, plus
both CREATE TABLE statements, byte-identical to the submitted version. That
harness caught the one thing review would not have: mapped_column(ForeignKey(
"user.uid")) silently infers VARCHAR(80) from the referenced column, which
had changed the submitted `user_id INTEGER`. An explicit Integer puts it back.

app.py went over the 250-line cap on the way, hence the split; models.py takes
the shebang + exec bit that NLP's nine import-only modules already use, since
__init__.py would make `from models import` unresolvable to mypy while it
still works at runtime. backend/Dockerfile copied only app.py, so it needed
the new file too.

PSD's two are pyflink's MapFunction and KeyedProcessFunction, which ship no
types and are not installed in that venv at all -- a hand-written .pyi would
assert an API nothing here can check. A [[tool.mypy.overrides]] scoped to that
one module turns off disallow_subclassing_any and nothing else.

ERSMS 4 -> 0, PSD 2 -> 0, under --strict. ruff and ruff format clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0172dj34FDpao6wsix8ZuCzE
2026-08-29 11:00:28 +02:00

111 lines
4.6 KiB
YAML

# Install: ./install.sh (or: pre-commit install && pre-commit install --hook-type pre-push)
# Fast lint: pre-commit run --all-files
#
# Ordering rule for this repo: a hook lands only once the thing it gates is
# already clean, so `pre-commit run --all-files` is green at every commit.
# Hooks still to come, each blocked on its own cleanup:
# - file-length blocked on the .tex/code splits and TODO-file-length-250.md
# - mypy the six third-party-typing errors are GONE (ERSMS moved
# to a typed DeclarativeBase; PSD has a scoped override).
# Still blocked on ECOTE/NLP/EARIN, and on the fact that
# mypy cannot run repo-wide -- many courses ship a main.py
# and it stops at "Duplicate module named". It needs a
# per-directory driver, not just a clean tree.
# - dependency-freshness blocked on the upgrade itself; the gate and the
# upgrade must land in the SAME commit or the repo is
# instantly and permanently red
# - trailing-whitespace / end-of-file-fixer these REWRITE files, so they get
# their own reviewed cleanup commit first
default_language_version:
python: python3
default_stages: [pre-commit]
fail_fast: false
repos:
# Landed 2026-08-29, the day the archive first reached zero ruff findings
# repo-wide. Pinned exactly, per rules/dependency-freshness: 0.16.5 is the
# newest stable. These hooks pass --force-exclude themselves, so the
# vendored trees excluded in pyproject.toml stay excluded even though
# pre-commit hands ruff explicit filenames.
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.16.5
hooks:
- id: ruff-check
types_or: [python, pyi, jupyter]
- id: ruff-format
args: [--check]
types_or: [python, pyi, jupyter]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
# Structural checks only -- none of these rewrite a file.
- id: check-merge-conflict
- id: check-case-conflict
- id: check-symlinks
- id: destroyed-symlinks
- id: detect-private-key
# tsconfig*.json and .vscode/*.json are JSONC -- both TypeScript and
# VS Code document comments and trailing commas as valid there, and
# check-json uses a strict RFC parser. This is the wrong parser for
# those files, not a defect being suppressed; every other .json is
# still checked. (The one real failure this hook found, a committed
# merge conflict in EARIN/.vscode/extensions.json, is fixed.)
- id: check-json
exclude: |
(?x)^(
.*/tsconfig[^/]*\.json|
.*/\.vscode/[^/]*\.json
)$
- id: check-toml
- id: check-yaml
args: [--unsafe]
# 2 MB. The four 39 MB movies.csv copies are already tracked and stay
# (approved decision 6); this stops the NEXT one.
- id: check-added-large-files
args: [--maxkb=2000]
- repo: local
hooks:
# Coursework deliverables are allowed under the two course trees;
# compiled output is allowed nowhere. See .binary-allowlist.
- id: no-binaries
name: block binary files outside the allowlist
entry: scripts/check_no_binaries.sh
language: script
pass_filenames: true
# A tracked file that .gitignore also matches fails silently on
# `git add`. 130 of those were fixed by hand; this keeps them fixed.
- id: ignore-consistency
name: no tracked file is gitignored
entry: scripts/check_ignore_consistency.sh
language: script
pass_filenames: false
always_run: true
# Thin shim into ~/utils, which owns the four markdown namespaces.
- id: md-naming
name: markdown naming convention
entry: scripts/check_md_naming.sh
language: system
files: \.(md|markdown)$
# CLAUDE.md has always said a hook rejects these. Now one does.
- id: no-suppressions
name: no noqa or type-ignore comments
entry: scripts/check_no_suppressions.sh
language: script
types_or: [python, pyi, jupyter]
# -x so shellcheck FOLLOWS sourced files. The 44 per-course run.sh stubs
# source scripts/course_lib.sh; without -x every one of them reports
# SC1091 "not following", and the only other way to silence that is a
# per-file disable, which is banned here.
- id: shellcheck
name: shellcheck
entry: bash -c 'printf "%s\0" "$@" | xargs -0 -n 40 shellcheck -x' --
language: system
types: [shell]
exclude_types: [zsh]