GitHub - mukundzha/avouch: Review the Python you changed, not the Python you inherited. Git-aware AST code review that runs in the seconds before git push.
Pangram verdict · v3.3
We believe this text is mainly AI, with some human-written content.
AI likelihood · overall
AIArticle text · 822 words · 2 segments analyzed
Review the Python you changed, not the Python you inherited. Avouch is a lightweight, Git-aware static analysis CLI for Python. It asks Git which files your next commit will touch, parses each changed .py file with the standard ast module, and reports structural problems against limits you configure in avouch.toml. No daemon. No network. No path lists to maintain. Run it in the seconds before git push, fix what it flags, push. pip install avouch cd your-repo avouch Table of contents Why it exists Installation Quick start JSON output Quiet mode GitHub Actions Other CI systems Configuration Rules How it works Repository layout Adding a rule Testing Roadmap FAQ Contributing License Why it exists The review set is the diff, not the repository. Avouch computes the review set from Git at run time (git diff HEAD --name-only plus untracked files). Every finding is attributable to work you are about to push — never to the legacy you inherited. Metrics are exact. Parameter counts, nesting depth, and line spans come from the AST, not regex. If a metric cannot be computed exactly, Avouch does not claim it. Errors are data. An unreadable or syntactically broken file becomes an ERROR entry in the report. One broken file never cancels the review of the others. Avouch reviews; it does not gate. The exit code signals the outcome — 0 clean, 1 violations found, 2 Avouch error — but enforcement belongs in an opt-in interface, not in a tool you run before every push. The runtime is the standard library. Three git subprocess calls and ast/tomllib. No daemon to keep alive; runtime is bounded by the size of your diff, not your repository. Installation Requires Python 3.10+ (rules use ast.Match; configuration uses tomllib) and Git on PATH. pip install avouch or from source: git clone https://github.com/mukundzha/avouch.git cd avouch pip install -e . Both register the avouch console script (avouch.cli:main). Quick start The interface is one command with a small set of optional flags: cd your-repo # ... make a change ... avouch # human report avouch --json # one JSON document on stdout avouch --docs # built-in documentation; no review performed avouch --version # print the version and exit avouch --verbose # step-by-step review details on stderr avouch --quiet # analyze, print no report; exit code only avouch --changed # compact added/deleted view of changed files vs HEAD avouch --staged # review only files staged for the next commit avouch --all-files # review every eligible Python file, not just the diff avouch --not-git # review every eligible .py file on disk; no Git repo needed avouch --help # every flag The review set is defined by Git, so there is nothing to configure at invocation time. With --not-git, Avouch skips the Git requirement and reviews every eligible .py file found by walking the current directory instead (skipping Git, cache, and virtual-environment directories). Avouch reviews: tracked files modified vs. HEAD (git diff HEAD --name-only), and untracked .py files (git ls-files --others --exclude-standard). Deleted paths and non-.py files are skipped. Committed, untouched files never appear in the output. Files that look generated (generated.py, *_generated.py, codegen.py, autogen.py, … — see src/avouch/utility/is_generated.py) are skipped too. The review-scope flags --changed, --staged, and --all-files are mutually exclusive — pick at most one. The output flags --json, --verbose, and --quiet combine freely with any review scope. A run with findings $ avouch AVOUCH · 2 FILES · 4 WARN ──────────────────────────────────────────────────────────────────────────────── bad.py:1: SCR002: Bare except detected. Catch a specific exception instead, e.g. except ValueError:. │ 1 │ def connect(host, port, user, password, db, timeout): │ ^^^^^^^ SCR002 2 │ try: │ bad.py:1: SCR014: Too many parameters (6/5). Group related parameters into a data class or dictionary. │ 1 │ def connect(host, port, user, password, db, timeout): │ ^^^^^^^ SCR014 2 │ try: │ ──────────────────────────────────────────────────────────────────────────────── BY RULE SCR002 Bare except 1 SCR014 Too many parameters 1 ──────────────────────────────────────────────────────────────────────────────── PASSED ✓ src/util.py Header — AVOUCH · N FILES · W WARN · E ERR: file and per-severity counts, followed by the per-file findings. Findings — each finding renders compiler-style: a file:line header with the rule id and full message, then the offending code region with dimmed line numbers and a caret ^^^^^ under the flagged name (rule id in blue on a TTY). BY RULE summary — findings counted per rule, most common first, with counts aligned on the right. Rendered only when findings exist. PASSING grid — compliant files, compressed to a few lines with a [+N more] note when there are many. Identical (component, rule) findings are deduplicated per file — the header counts every finding, so with overlapping rule IDs (SCR004 / SCR006 duplicate-branch) the row count can be lower than the header count.
A clean run $ avouch All clean. Edge cases $ cd /tmp/somewhere-without-git $ avouch error: no Git repository found hint: run Avouch from inside a Git repository, or use --not-git to review files without Git $ cd ~/fresh-checkout # e.g.