71 lines
1.8 KiB
Python
71 lines
1.8 KiB
Python
|
|
#!/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())
|
||
|
|
|