|
17 | 17 | from collections.abc import Callable |
18 | 18 | from concurrent.futures import ThreadPoolExecutor, as_completed |
19 | 19 | from pathlib import Path |
20 | | -from typing import Any |
| 20 | +from typing import Any, TypeVar |
21 | 21 |
|
22 | 22 | from utils.exclusion_rules import RuleTokens |
23 | 23 |
|
|
34 | 34 | INVALID_WORKSPACE_ALIASES_CACHE_FILE = CACHE_DIR / "invalid-workspace-aliases.json" |
35 | 35 | TAB_SUMMARIES_PREFIX = "tab-summaries-" |
36 | 36 |
|
| 37 | +T = TypeVar("T") |
| 38 | + |
37 | 39 |
|
38 | 40 | def nocache_enabled(*, request_nocache: bool = False) -> bool: |
39 | 41 | """Return whether summary-cache reads should be bypassed. |
@@ -166,11 +168,6 @@ def _read_cache_file_unlocked(path: Path | str) -> dict[str, Any] | None: |
166 | 168 | return None |
167 | 169 |
|
168 | 170 |
|
169 | | -def _read_cache_file(path: Path | str) -> dict[str, Any] | None: |
170 | | - with _summary_cache_lock: |
171 | | - return _read_cache_file_unlocked(path) |
172 | | - |
173 | | - |
174 | 171 | def _write_cache_file_unlocked(path: Path | str, payload: dict[str, Any]) -> None: |
175 | 172 | p = Path(path) |
176 | 173 | try: |
@@ -252,31 +249,52 @@ def set_cached_projects( |
252 | 249 | _set_cached_projects_unlocked(fingerprint, projects, warnings) |
253 | 250 |
|
254 | 251 |
|
255 | | -def get_or_build_cached_projects( |
| 252 | +def _get_or_build_cached( |
256 | 253 | workspace_path: str, |
257 | 254 | workspace_entries: list[dict[str, Any]], |
258 | 255 | rules: list[RuleTokens], |
259 | 256 | *, |
260 | | - build_fn: Callable[[], tuple[list[dict[str, Any]], list[dict[str, Any]]]], |
261 | | -) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: |
262 | | - """Return cached projects or build once under double-checked locking.""" |
| 257 | + build_fn: Callable[[], T], |
| 258 | + get_unlocked: Callable[[dict[str, Any]], T | None], |
| 259 | + set_unlocked: Callable[[dict[str, Any], T], None], |
| 260 | + should_cache: Callable[[T], bool] | None = None, |
| 261 | +) -> T: |
263 | 262 | with _summary_cache_lock: |
264 | 263 | fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
265 | | - hit = _get_cached_projects_unlocked(fingerprint) |
| 264 | + hit = get_unlocked(fingerprint) |
266 | 265 | if hit is not None: |
267 | 266 | return hit |
268 | 267 |
|
269 | 268 | built = build_fn() |
270 | 269 |
|
271 | 270 | with _summary_cache_lock: |
272 | 271 | fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
273 | | - hit = _get_cached_projects_unlocked(fingerprint) |
| 272 | + hit = get_unlocked(fingerprint) |
274 | 273 | if hit is not None: |
275 | 274 | return hit |
276 | | - _set_cached_projects_unlocked(fingerprint, built[0], built[1]) |
| 275 | + if should_cache is None or should_cache(built): |
| 276 | + set_unlocked(fingerprint, built) |
277 | 277 | return built |
278 | 278 |
|
279 | 279 |
|
| 280 | +def get_or_build_cached_projects( |
| 281 | + workspace_path: str, |
| 282 | + workspace_entries: list[dict[str, Any]], |
| 283 | + rules: list[RuleTokens], |
| 284 | + *, |
| 285 | + build_fn: Callable[[], tuple[list[dict[str, Any]], list[dict[str, Any]]]], |
| 286 | +) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]: |
| 287 | + """Return cached projects or build once under double-checked locking.""" |
| 288 | + return _get_or_build_cached( |
| 289 | + workspace_path, |
| 290 | + workspace_entries, |
| 291 | + rules, |
| 292 | + build_fn=build_fn, |
| 293 | + get_unlocked=_get_cached_projects_unlocked, |
| 294 | + set_unlocked=lambda fp, built: _set_cached_projects_unlocked(fp, built[0], built[1]), |
| 295 | + ) |
| 296 | + |
| 297 | + |
280 | 298 | def _get_cached_composer_id_to_ws_unlocked( |
281 | 299 | fingerprint: dict[str, Any], |
282 | 300 | ) -> dict[str, str] | None: |
@@ -341,21 +359,14 @@ def get_or_build_cached_composer_id_to_ws( |
341 | 359 | build_fn: Callable[[], dict[str, str]], |
342 | 360 | ) -> dict[str, str]: |
343 | 361 | """Return cached composer map or build once under double-checked locking.""" |
344 | | - with _summary_cache_lock: |
345 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
346 | | - hit = _get_cached_composer_id_to_ws_unlocked(fingerprint) |
347 | | - if hit is not None: |
348 | | - return hit |
349 | | - |
350 | | - built = build_fn() |
351 | | - |
352 | | - with _summary_cache_lock: |
353 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
354 | | - hit = _get_cached_composer_id_to_ws_unlocked(fingerprint) |
355 | | - if hit is not None: |
356 | | - return hit |
357 | | - _set_cached_composer_id_to_ws_unlocked(fingerprint, built) |
358 | | - return built |
| 362 | + return _get_or_build_cached( |
| 363 | + workspace_path, |
| 364 | + workspace_entries, |
| 365 | + rules, |
| 366 | + build_fn=build_fn, |
| 367 | + get_unlocked=_get_cached_composer_id_to_ws_unlocked, |
| 368 | + set_unlocked=_set_cached_composer_id_to_ws_unlocked, |
| 369 | + ) |
359 | 370 |
|
360 | 371 |
|
361 | 372 | def _get_cached_invalid_workspace_aliases_unlocked( |
@@ -435,21 +446,14 @@ def get_or_build_cached_invalid_workspace_aliases( |
435 | 446 | build_fn: Callable[[], dict[str, str]], |
436 | 447 | ) -> dict[str, str]: |
437 | 448 | """Return cached alias map or build once under double-checked locking.""" |
438 | | - with _summary_cache_lock: |
439 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
440 | | - hit = _get_cached_invalid_workspace_aliases_unlocked(fingerprint) |
441 | | - if hit is not None: |
442 | | - return hit |
443 | | - |
444 | | - built = build_fn() |
445 | | - |
446 | | - with _summary_cache_lock: |
447 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
448 | | - hit = _get_cached_invalid_workspace_aliases_unlocked(fingerprint) |
449 | | - if hit is not None: |
450 | | - return hit |
451 | | - _set_cached_invalid_workspace_aliases_unlocked(fingerprint, built) |
452 | | - return built |
| 449 | + return _get_or_build_cached( |
| 450 | + workspace_path, |
| 451 | + workspace_entries, |
| 452 | + rules, |
| 453 | + build_fn=build_fn, |
| 454 | + get_unlocked=_get_cached_invalid_workspace_aliases_unlocked, |
| 455 | + set_unlocked=_set_cached_invalid_workspace_aliases_unlocked, |
| 456 | + ) |
453 | 457 |
|
454 | 458 |
|
455 | 459 | def _tab_summaries_path(workspace_id: str) -> Path: |
@@ -536,19 +540,14 @@ def get_or_build_cached_tab_summaries( |
536 | 540 | build_fn: Callable[[], tuple[dict[str, Any], int]], |
537 | 541 | ) -> tuple[dict[str, Any], int]: |
538 | 542 | """Return cached tab summaries or build once under double-checked locking.""" |
539 | | - with _summary_cache_lock: |
540 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
541 | | - hit = _get_cached_tab_summaries_unlocked(fingerprint, workspace_id) |
542 | | - if hit is not None: |
543 | | - return hit |
544 | | - |
545 | | - payload, status = build_fn() |
546 | | - |
547 | | - with _summary_cache_lock: |
548 | | - fingerprint = _workspace_storage_fingerprint(workspace_path, workspace_entries, rules) |
549 | | - hit = _get_cached_tab_summaries_unlocked(fingerprint, workspace_id) |
550 | | - if hit is not None: |
551 | | - return hit |
552 | | - if status == 200: |
553 | | - _set_cached_tab_summaries_unlocked(fingerprint, workspace_id, payload, status) |
554 | | - return payload, status |
| 543 | + return _get_or_build_cached( |
| 544 | + workspace_path, |
| 545 | + workspace_entries, |
| 546 | + rules, |
| 547 | + build_fn=build_fn, |
| 548 | + get_unlocked=lambda fp: _get_cached_tab_summaries_unlocked(fp, workspace_id), |
| 549 | + set_unlocked=lambda fp, built: _set_cached_tab_summaries_unlocked( |
| 550 | + fp, workspace_id, built[0], built[1], |
| 551 | + ), |
| 552 | + should_cache=lambda built: built[1] == 200, |
| 553 | + ) |
0 commit comments