from __future__ import annotations import argparse import os import pickle from pathlib import Path from typing import Any CURRENT_CACHE_VERSIONS = { "next_step": 3, "all_future": 5, } def infer_cache_kind(path: Path) -> str | None: name = path.name for kind in CURRENT_CACHE_VERSIONS: marker = f"_{kind}_cache_" if marker in name: return kind return None def read_cache_version(path: Path) -> int | None: try: with path.open("rb") as f: payload: Any = pickle.load(f) except Exception: return None if not isinstance(payload, dict): return None version = payload.get("_cache_version") try: return int(version) except (TypeError, ValueError): return None def should_remove(path: Path, remove_all: bool) -> tuple[bool, str]: kind = infer_cache_kind(path) if kind is None: return False, "not a DeepHealth dataset cache" if remove_all: return True, "remove all dataset caches" version = read_cache_version(path) expected = CURRENT_CACHE_VERSIONS[kind] if version is None: return True, f"{kind} cache is unreadable or missing _cache_version" if version != expected: return True, f"{kind} cache version {version} != current {expected}" return False, f"{kind} cache version {version} is current" def iter_cache_files(root: Path, recursive: bool): pattern = "**/*_cache_*.pkl" if recursive else "*_cache_*.pkl" yield from root.glob(pattern) def main() -> None: parser = argparse.ArgumentParser( description=( "Remove obsolete DeepHealth dataset cache files. Defaults to dry-run; " "pass --apply to delete." ) ) parser.add_argument( "--data_dir", type=Path, default=Path("."), help="Directory containing dataset cache files, usually the data_prefix directory.", ) parser.add_argument( "--recursive", action="store_true", help="Search recursively under data_dir.", ) parser.add_argument( "--all", action="store_true", help="Remove all recognized DeepHealth dataset caches, including current ones.", ) parser.add_argument( "--apply", action="store_true", help="Actually delete files. Without this flag, only prints what would be removed.", ) args = parser.parse_args() root = args.data_dir.expanduser().resolve() if not root.exists() or not root.is_dir(): raise SystemExit(f"data_dir is not a directory: {root}") files = sorted(p for p in iter_cache_files(root, args.recursive) if p.is_file()) if not files: print(f"No *_cache_*.pkl files found under {root}") return kept = 0 removed = 0 candidates = 0 for path in files: remove, reason = should_remove(path, remove_all=bool(args.all)) rel = os.path.relpath(path, root) if remove: candidates += 1 action = "DELETE" if args.apply else "WOULD_DELETE" print(f"{action}\t{rel}\t{reason}") if args.apply: path.unlink() removed += 1 else: kept += 1 print(f"KEEP\t{rel}\t{reason}") if args.apply: print(f"Removed {removed} cache file(s); kept {kept}.") else: print( f"Dry run: {candidates} cache file(s) would be removed; kept {kept}. " "Re-run with --apply to delete." ) if __name__ == "__main__": main()