#!/usr/bin/env python3 """Count lines of Python code (excluding comments and blank lines).""" import sys import ast from pathlib import Path def count_python_loc(filepath: str) -> dict: """ Count logical lines of Python code. Returns: dict with total_lines, code_lines, comment_lines, blank_lines """ path = Path(filepath) if not path.exists(): return {"error": f"File not found: {filepath}", "valid": False} try: with open(filepath, 'r') as f: lines = f.readlines() except Exception as e: return {"error": str(e), "valid": False} total = len(lines) code_lines = 0 comment_lines = 0 blank_lines = 0 in_docstring = False docstring_delim = None for line in lines: stripped = line.strip() # Check for docstring start/end if not in_docstring and (stripped.startswith('"""') or stripped.startswith("'''")): if stripped.count('"""') >= 2 or stripped.count("'''") >= 2: # Single line docstring comment_lines += 1 else: in_docstring = True docstring_delim = '"""' if '"""' in stripped else "'''" continue elif in_docstring: if docstring_delim in stripped: in_docstring = False comment_lines += 1 continue if not stripped: blank_lines += 1 elif stripped.startswith('#'): comment_lines += 1 else: code_lines += 1 return { "valid": True, "file": filepath, "total_lines": total, "code_lines": code_lines, "comment_lines": comment_lines, "blank_lines": blank_lines, "sum": code_lines + comment_lines + blank_lines } def main(): if len(sys.argv) < 2: print("Usage: py_loc.py [file2.py...]", file=sys.stderr) sys.exit(1) files = sys.argv[1:] for filepath in files: result = count_python_loc(filepath) if "valid" in result and result["valid"]: print(f"{filepath}: {result['code_lines']} code, {result['comment_lines']} comments, {result['blank_lines']} blank, {result['total_lines']} total") else: print(f"ERROR: {filepath} - {result.get('error', 'unknown')}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()