mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
_fingerprint_mapping memoised digests under id(obj) while holding no reference to obj, so CPython reused the address of a freed mapping and served its digest for an unrelated live one. Measured 398/400 wrong digests for short-lived mappings; in-place mutation was invisible and the domain-index collision guard could be bypassed entirely. Two unit tests pinned that behaviour as the contract — test_id_cache_short_circuits_recompute asserted that mutating a dict in place leaves its fingerprint unchanged — so the defect shipped green. Both now assert the opposite. build_domain_index rebuilt the whole reverse index on every lookup, scanning every application's domains and aliases to answer one key. It is now memoised on that fingerprint, which is only sound because of the fix above. 4x on a 100-app payload. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
58 lines
2.2 KiB
Python
58 lines
2.2 KiB
Python
"""In-process caches for runtime data (applications, users, domains).
|
|
|
|
Domain modules:
|
|
|
|
- ``utils.cache.base`` — cross-cutting helpers (constants,
|
|
deep-merge, signatures, render machinery).
|
|
- ``utils.cache.applications`` — variants + ``get_merged_applications``
|
|
(ansible-free at import time, runner-host friendly).
|
|
- ``utils.cache.users`` — ``get_user_defaults``, ``get_merged_users``.
|
|
- ``utils.cache.domains`` — ``get_merged_domains``.
|
|
- ``utils.cache.yaml`` — process-wide ``load_yaml`` /
|
|
``load_yaml_any`` parser cache shared by everything that reads YAML
|
|
off-disk.
|
|
- ``utils.cache.files`` — process-wide project-tree walk +
|
|
file-content cache (``read_text``, ``iter_project_files``,
|
|
``iter_non_ignored_files``).
|
|
- ``utils.cache.gitignore`` — cached ``.gitignore`` pattern loader
|
|
+ path matcher used by lint/integration tests that need a
|
|
"files git would track" view without spawning git.
|
|
|
|
Use the domain module directly. The package-level
|
|
``_reset_cache_for_tests`` clears every domain's caches plus the shared
|
|
fingerprint memo in one call — preferred over hand-rolling N
|
|
``_reset()`` calls per test fixture.
|
|
|
|
``PROJECT_ROOT`` is the single source of truth for the repository root.
|
|
Every callsite that needs it — CLI tools, plugins, tests — MUST import
|
|
this value rather than re-derive it via ``Path(__file__).parents[N]`` or
|
|
``os.pardir`` chains; ``tests/lint/repository/test_project_root_import.py``
|
|
enforces that.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT: Path = Path(__file__).resolve().parents[2]
|
|
ROLES_DIR: Path = PROJECT_ROOT / "roles"
|
|
|
|
|
|
def _reset_cache_for_tests() -> None:
|
|
"""Orchestrate per-domain cache resets plus the shared fingerprint
|
|
memo. Test fixtures rely on a single entry point so they can stay
|
|
agnostic of how the cache is partitioned across modules."""
|
|
from utils.domains import application_domain_index
|
|
|
|
from . import applications, base, domains, files, gitignore, users
|
|
from . import yaml as _yaml_cache
|
|
|
|
application_domain_index._reset()
|
|
applications._reset()
|
|
users._reset()
|
|
domains._reset()
|
|
base._reset()
|
|
_yaml_cache._reset()
|
|
files._reset()
|
|
gitignore._reset()
|