Two-layer file change detection using modification time and content hashing. Provides efficient change detection by: 1. Fast modification time comparison (microsecond performance) 2. Accurate content verification via MD5 hashing when needed NOTE: This is the legacy implementat
| 52 | |
| 53 | |
| 54 | class FingerprintCache: |
| 55 | """ |
| 56 | Two-layer file change detection using modification time and content hashing. |
| 57 | |
| 58 | Provides efficient change detection by: |
| 59 | 1. Fast modification time comparison (microsecond performance) |
| 60 | 2. Accurate content verification via MD5 hashing when needed |
| 61 | |
| 62 | NOTE: This is the legacy implementation. For new code, prefer TwoLayerFingerprintCache |
| 63 | which supports batch operations and the safe pre-computed fingerprint pattern. |
| 64 | """ |
| 65 | |
| 66 | def __init__(self, cache_file: Path, modtime_only: bool = False): |
| 67 | """ |
| 68 | Initialize fingerprint cache. |
| 69 | |
| 70 | Args: |
| 71 | cache_file: Path to JSON cache file |
| 72 | modtime_only: When True, disable hashing and rely solely on modtime. |
| 73 | """ |
| 74 | self.cache_file = cache_file |
| 75 | self._modtime_only: bool = modtime_only |
| 76 | self.cache = self._load_cache() |
| 77 | |
| 78 | def _load_cache(self) -> dict[str, CacheEntry]: |
| 79 | """ |
| 80 | Load cache from JSON file, return empty dict if file doesn't exist. |
| 81 | |
| 82 | Uses try-except instead of exists() check to avoid TOCTOU race condition. |
| 83 | This is the same pattern used in meson_runner.py for test_list_cache. |
| 84 | |
| 85 | Returns: |
| 86 | Dictionary mapping file paths to cache entries |
| 87 | """ |
| 88 | try: |
| 89 | with open(self.cache_file, "r") as f: |
| 90 | data = json.load(f) |
| 91 | |
| 92 | # Convert JSON dict to CacheEntry objects |
| 93 | cache: dict[str, CacheEntry] = {} |
| 94 | for file_path, entry_data in data.items(): |
| 95 | cache[file_path] = CacheEntry( |
| 96 | modification_time=entry_data["modification_time"], |
| 97 | md5_hash=entry_data["md5_hash"], |
| 98 | ) |
| 99 | return cache |
| 100 | except FileNotFoundError: |
| 101 | # Cache file doesn't exist yet - start fresh |
| 102 | return {} |
| 103 | except (json.JSONDecodeError, KeyError, TypeError, IOError, OSError): |
| 104 | # Cache corrupted or inaccessible - start fresh |
| 105 | return {} |
| 106 | |
| 107 | def _save_cache(self) -> None: |
| 108 | """Save current cache state to JSON file.""" |
| 109 | # Ensure cache directory exists |
| 110 | self.cache_file.parent.mkdir(parents=True, exist_ok=True) |
| 111 |
no outgoing calls