152 lines
4.1 KiB
Python
152 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
# pyright: reportUnknownMemberType=false
|
|
"""
|
|
Caching wrapper for pyright that only runs if files have changed.
|
|
This significantly speeds up repeated pyright runs during development.
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional
|
|
|
|
|
|
def get_file_hash(file_path: Path) -> str:
|
|
"""Get SHA256 hash of a file."""
|
|
try:
|
|
with open(file_path, "rb") as f:
|
|
return hashlib.sha256(f.read()).hexdigest()
|
|
except (OSError, IOError):
|
|
return ""
|
|
|
|
|
|
def get_directory_hash(directory: Path, extensions: Optional[List[str]] = None) -> str:
|
|
"""Get combined hash of all files in a directory."""
|
|
if extensions is None:
|
|
extensions = [".py"]
|
|
|
|
hashes: List[str] = []
|
|
if directory.exists():
|
|
for ext in extensions:
|
|
for file_path in directory.rglob(f"*{ext}"):
|
|
if file_path.is_file():
|
|
hashes.append(
|
|
f"{file_path.relative_to(directory)}:{get_file_hash(file_path)}"
|
|
)
|
|
|
|
hashes.sort() # Ensure consistent ordering
|
|
return hashlib.sha256("\n".join(hashes).encode()).hexdigest()
|
|
|
|
|
|
def get_cache_file() -> Path:
|
|
"""Get the cache file path."""
|
|
cache_dir = Path(".cache/pyright")
|
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
return cache_dir / "file_hashes.json"
|
|
|
|
|
|
def load_cache() -> Dict[str, str]:
|
|
"""Load the cache file."""
|
|
cache_file = get_cache_file()
|
|
if cache_file.exists():
|
|
try:
|
|
with open(cache_file, "r") as f:
|
|
return json.load(f)
|
|
except (json.JSONDecodeError, IOError):
|
|
pass
|
|
return {}
|
|
|
|
|
|
def save_cache(cache: Dict[str, str]) -> None:
|
|
"""Save the cache file."""
|
|
cache_file = get_cache_file()
|
|
try:
|
|
with open(cache_file, "w") as f:
|
|
json.dump(cache, f, indent=2)
|
|
except IOError:
|
|
pass # Ignore cache save errors
|
|
|
|
|
|
def has_files_changed() -> bool:
|
|
"""Check if any relevant files have changed since last run."""
|
|
cache = load_cache()
|
|
changed = False
|
|
|
|
# Files and directories to check
|
|
files_to_check = [
|
|
Path("pyrightconfig.json"),
|
|
Path(".pyrightconfig.json"),
|
|
Path("pyproject.toml"),
|
|
]
|
|
|
|
directories_to_check = [
|
|
Path("ci"),
|
|
]
|
|
|
|
# Check individual files
|
|
for file_path in files_to_check:
|
|
if file_path.exists():
|
|
current_hash = get_file_hash(file_path)
|
|
if cache.get(str(file_path)) != current_hash:
|
|
cache[str(file_path)] = current_hash
|
|
changed = True
|
|
|
|
# Check directories
|
|
for dir_path in directories_to_check:
|
|
if dir_path.exists():
|
|
current_hash = get_directory_hash(dir_path)
|
|
cache_key = f"dir:{dir_path}"
|
|
if cache.get(cache_key) != current_hash:
|
|
cache[cache_key] = current_hash
|
|
changed = True
|
|
|
|
# Save cache if any changes were detected
|
|
if changed:
|
|
save_cache(cache)
|
|
|
|
return changed
|
|
|
|
|
|
def run_pyright(args: List[str]) -> int:
|
|
"""Run pyright with the given arguments."""
|
|
env = os.environ.copy()
|
|
# env["PYRIGHT_PYTHON_FORCE_VERSION"] = "latest"
|
|
env["PYRIGHT_PYTHON_CACHE_DIR"] = ".cache/pyright"
|
|
|
|
cmd = ["uv", "run", "pyright"] + args
|
|
|
|
try:
|
|
result = subprocess.run(cmd, env=env)
|
|
return result.returncode
|
|
except KeyboardInterrupt:
|
|
return 1
|
|
except Exception as e:
|
|
print(f"Error running pyright: {e}", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
def main() -> int:
|
|
"""Main entry point."""
|
|
args = sys.argv[1:] # Remove script name
|
|
|
|
# If --force is passed, skip cache check
|
|
if "--force" in args:
|
|
args.remove("--force")
|
|
print("Forcing pyright run (--force flag detected)")
|
|
return run_pyright(args)
|
|
|
|
# Check if files have changed
|
|
if not has_files_changed():
|
|
print("No changes detected, skipping pyright (use --force to override)")
|
|
return 0
|
|
|
|
print("Changes detected, running pyright...")
|
|
return run_pyright(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|