Coverage for little_loops / package_data.py: 0%
15 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-29 00:54 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-06-29 00:54 -0500
1"""Package data registry — declarative manifest of runtime-read assets.
3Every file the little_loops package reads at runtime must appear in
4PACKAGE_DATA_ASSETS. The completeness check (test_package_data_manifest.py)
5asserts each entry is accessible via importlib.resources.files("little_loops"),
6so adding a new asset read without registering it here will not be caught —
7but registering it and omitting it from the package source will fail the test.
9Usage in the completeness check::
11 from little_loops.package_data import PACKAGE_DATA_ASSETS, list_missing_assets
13 missing = list_missing_assets()
14 assert not missing, f"Assets not accessible: {missing}"
15"""
17from __future__ import annotations
19import importlib.resources
20from typing import Final
22_PACKAGE: Final[str] = "little_loops"
24# Declarative manifest: each entry is a tuple of path parts relative to
25# the little_loops package root. Add an entry here whenever new package
26# data is referenced at runtime. Omitting an entry gives a false-green
27# completeness result — the check won't catch a missing asset it doesn't know about.
28PACKAGE_DATA_ASSETS: Final[tuple[tuple[str, ...], ...]] = (
29 ("assets", "ll-cli-logo.txt"),
30 ("hooks", "prompts", "optimize-prompt-hook.md"),
31 ("hooks", "adapters", "codex", "hooks.json"),
32 ("templates", "bug-sections.json"),
33 ("templates", "enh-sections.json"),
34 ("templates", "feat-sections.json"),
35 ("templates", "epic-sections.json"),
36 ("templates", "ll-goals-template.md"),
37 ("templates", "generic.json"),
38 ("templates", "python-generic.json"),
39 ("templates", "javascript.json"),
40 ("templates", "typescript.json"),
41 ("templates", "rust.json"),
42 ("templates", "go.json"),
43 ("templates", "java-maven.json"),
44 ("templates", "java-gradle.json"),
45 ("templates", "dotnet.json"),
46)
49def check_asset_accessible(parts: tuple[str, ...]) -> bool:
50 """Return True if the asset is reachable via importlib.resources."""
51 try:
52 traversable = importlib.resources.files(_PACKAGE)
53 for part in parts:
54 traversable = traversable.joinpath(part)
55 return traversable.is_file() # type: ignore[return-value]
56 except Exception:
57 return False
60def list_missing_assets() -> list[tuple[str, ...]]:
61 """Return registered assets not accessible in the current installation."""
62 return [parts for parts in PACKAGE_DATA_ASSETS if not check_asset_accessible(parts)]