from __future__ import annotations import ast from pathlib import Path MAX_CLASS_LINES = 800 SERVER_SOURCE_ROOT = Path(__file__).resolve().parents[1] / "src" / "app" def iter_python_source_files(root: Path) -> list[Path]: ignored_parts = {"__pycache__", "x_financial_server.egg-info"} return sorted( path for path in root.rglob("*.py") if not ignored_parts.intersection(path.parts) ) def test_python_classes_do_not_exceed_800_lines() -> None: oversized_classes: list[str] = [] for path in iter_python_source_files(SERVER_SOURCE_ROOT): tree = ast.parse(path.read_text(encoding="utf-8")) for node in ast.walk(tree): if isinstance(node, ast.ClassDef) and node.end_lineno is not None: line_count = node.end_lineno - node.lineno + 1 if line_count > MAX_CLASS_LINES: relative_path = path.relative_to(SERVER_SOURCE_ROOT.parents[1]) oversized_classes.append( f"{relative_path}:{node.lineno} {node.name} ({line_count} lines)" ) assert oversized_classes == []