#!/usr/bin/env python3 """Fail when production Python files exceed the configured line budget. The gate is intentionally narrow: - it only checks the `atlasbot/` package tree; - it treats each file independently; - it keeps the threshold explicit so CI can ratchet without guesswork. """ from __future__ import annotations import argparse from pathlib import Path def _count_lines(path: Path) -> int: """Return the physical line count for `path`. Input: - `path`: a readable Python source file. Output: - The number of newline-delimited lines in the file. """ return len(path.read_text(encoding="utf-8").splitlines()) def _iter_python_files(root: Path) -> list[Path]: """List production Python files under `root`. Input: - `root`: repository package root to scan. Output: - Sorted Python file paths, excluding bytecode and hidden caches. """ return sorted( path for path in root.rglob("*.py") if path.is_file() and "__pycache__" not in path.parts and ".venv" not in path.parts ) def main() -> int: """Run the size gate and return a process exit code.""" parser = argparse.ArgumentParser() parser.add_argument("--root", default="atlasbot") parser.add_argument("--max-lines", type=int, default=500) args = parser.parse_args() root = Path(args.root) violations: list[tuple[int, Path]] = [] for path in _iter_python_files(root): lines = _count_lines(path) if lines > args.max_lines: violations.append((lines, path)) if violations: for lines, path in sorted(violations, reverse=True): print(f"{path}: {lines} lines (limit {args.max_lines})") return 1 return 0 if __name__ == "__main__": raise SystemExit(main())