validate_python.py 1.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. #!/usr/bin/env python3
  2. import ast
  3. import concurrent.futures
  4. import pathlib
  5. import sys
  6. EXCLUDE_DIRS = {".git", "venv", ".venv"}
  7. def find_python_files(root="."):
  8. for path in pathlib.Path(root).rglob("*.py"):
  9. if any(part in EXCLUDE_DIRS for part in path.parts):
  10. continue
  11. yield path
  12. def check_file(path):
  13. try:
  14. source = path.read_text(encoding="utf-8")
  15. ast.parse(source, filename=str(path))
  16. return path, None
  17. except SyntaxError as error:
  18. location = f"line {error.lineno}, col {error.offset}"
  19. return path, f"{location}: {error.msg}"
  20. except UnicodeDecodeError as error:
  21. return path, f"encoding error: {error}"
  22. def main():
  23. files = sorted(find_python_files())
  24. if not files:
  25. print("No Python files found.")
  26. return 0
  27. status = 0
  28. with concurrent.futures.ThreadPoolExecutor() as executor:
  29. results = executor.map(check_file, files)
  30. for path, error in results:
  31. if error:
  32. print(f"Syntax error in: {path}")
  33. print(f" {error}")
  34. status = 1
  35. print(f"Checked {len(files)} Python file(s).")
  36. return status
  37. if __name__ == "__main__":
  38. sys.exit(main())