From c00161a13da89985bf2af71e88a0f5d79ca71af3 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 12 Jun 2026 22:47:58 -0400 Subject: [PATCH] Adjust audi_line_count.py to take into account doc strings --- scripts/audit_line_count.py | 50 +++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/scripts/audit_line_count.py b/scripts/audit_line_count.py index 855ec84a..38e9520a 100644 --- a/scripts/audit_line_count.py +++ b/scripts/audit_line_count.py @@ -17,8 +17,8 @@ import os import sys from collections import defaultdict from dataclasses import dataclass, field -from pathlib import Path -from typing import Iterator +from pathlib import Path +from typing import Iterator TARGET_DIRS = ["src", "simulation", "tests", "scripts"] IGNORED_PATHS = {"__pycache__", ".git", "node_modules", "venv", ".venv", "env", ".env"} @@ -100,23 +100,34 @@ def analyze_file(path: Path) -> FileStats | None: lines = content.splitlines() stats.total_lines = len(lines) - for line in lines: + docstring_lines = set() + tree = None + try: + tree = ast.parse(content, filename=str(path)) + + # Extract line numbers for all standalone string expressions (docstrings & block comments) + for node in ast.walk(tree): + if isinstance(node, ast.Expr) and isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): + if hasattr(node, "lineno") and hasattr(node, "end_lineno") and node.end_lineno is not None: + for ln in range(node.lineno, node.end_lineno + 1): + docstring_lines.add(ln) + except Exception: + pass + + for i, line in enumerate(lines, 1): stripped = line.strip() if not stripped: stats.blank_lines += 1 + elif i in docstring_lines: stats.comment_lines += 1 elif stripped.startswith("#"): stats.comment_lines += 1 else: stats.code_lines += 1 - try: - tree = ast.parse(content, filename=str(path)) - except Exception: - return stats - - visitor = CodeAnalyzer() - visitor.visit(tree) - stats.classes = visitor.classes - stats.functions = visitor.functions - stats.methods = visitor.methods - stats.top_level_decls = visitor.top_level_decls + if tree is not None: + visitor = CodeAnalyzer() + visitor.visit(tree) + stats.classes = visitor.classes + stats.functions = visitor.functions + stats.methods = visitor.methods + stats.top_level_decls = visitor.top_level_decls return stats @@ -146,12 +157,9 @@ def gather_dir_stats(root: Path) -> DirStats: def format_bytes(num_bytes: int) -> str: - if num_bytes < 1024: - return f"{num_bytes}B" - elif num_bytes < 1024 * 1024: - return f"{num_bytes / 1024:.1f}KB" - else: - return f"{num_bytes / (1024 * 1024):.1f}MB" + if num_bytes < 1024: return f"{num_bytes}B" + elif num_bytes < 1024 * 1024: return f"{num_bytes / 1024:.1f}KB" + else: return f"{num_bytes / (1024 * 1024):.1f}MB" def print_stats(d: DirStats) -> None: @@ -212,7 +220,7 @@ def main() -> None: print(f" Files: {combined.file_count:,}") print(f" Lines: {combined.total_lines:,} (code: {combined.code_lines:,} | comment: {combined.comment_lines:,} | blank: {combined.blank_lines:,})") print(f" Classes: {combined.class_count:,}") - print(f" Functions: {combined.function_count:,}") + print(f" Functions: {combined.function_count:,}") print(f" Methods: {combined.method_count:,}") total_decls = combined.class_count + combined.function_count + combined.method_count print(f" Total decls: {total_decls:,}")