(self, file_path: Path, content: str)
| 336 | self._l1.popitem(last=False) # evict least-recently-used |
| 337 | |
| 338 | def _get_entry(self, file_path: Path, content: str) -> FileCacheEntry: |
| 339 | # Resolve once: L1 key and L2 hash must both use the canonical path. |
| 340 | file_path = file_path.resolve() |
| 341 | key = str(file_path) |
| 342 | |
| 343 | try: |
| 344 | mtime = file_path.stat().st_mtime |
| 345 | except OSError: |
| 346 | mtime = 0.0 |
| 347 | |
| 348 | # L1 – mtime guard (cheapest check: dict lookup + float compare) |
| 349 | l1 = self._l1_get(key) |
| 350 | if l1 and l1.mtime == mtime and l1.version == CACHE_VERSION: |
| 351 | return l1 |
| 352 | |
| 353 | file_hash = hashlib.sha256(content.encode()).hexdigest() |
| 354 | |
| 355 | # L1 – hash guard (file touched externally but content unchanged) |
| 356 | if l1 and l1.file_hash == file_hash and l1.version == CACHE_VERSION: |
| 357 | updated = dataclasses.replace(l1, mtime=mtime) |
| 358 | self._l1_put(key, updated) |
| 359 | return updated |
| 360 | |
| 361 | # L2 – disk (survive across process restarts) |
| 362 | l2 = self._disk_load(file_path, file_hash) |
| 363 | if l2: |
| 364 | updated_l2 = dataclasses.replace(l2, mtime=mtime) |
| 365 | self._l1_put(key, updated_l2) |
| 366 | return updated_l2 |
| 367 | |
| 368 | # L3 – build with chunk-level subtree reuse |
| 369 | old_chunks: Dict[str, AstChunk] = ( |
| 370 | l1.chunks if (l1 and l1.version == CACHE_VERSION) else {} |
| 371 | ) |
| 372 | entry = self._build(file_path, content, file_hash, mtime, old_chunks) |
| 373 | self._l1_put(key, entry) |
| 374 | self._disk_save(entry) |
| 375 | return entry |
| 376 | |
| 377 | def _build( |
| 378 | self, |
no test coverage detected