just_dna_enricher.caches¶
just_dna_enricher.caches ¶
The cache lanes as a registry, and the one endpoint that rebuilds them — RM176.
Every snapshot this tier knows about, all but one of them built here. Each is supposed to have three
stages: acquire (a download, a file the operator supplies, or — since RM171 — its parent lanes
being on disk), build (the conversion into the snapshot a check reads), and publish (the
upload a deployment then pulls with cache pull). Before this module the three stages existed as
eleven independent CLI commands and one hand-kept four-tuple list inside cli.py, and the gaps that
arrangement hid were all of the same kind: something true of a lane that no code anywhere asserted.
The count is deliberately not stated here. It was "twelve snapshots, eleven of them built here",
which is the counted-prose failure this workspace keeps re-learning: a sentence no test reads, true
on the day it was written and quietly wrong the first time the registry grew correctly
(@counted-prose-needs-a-fixed-field). test_cache_lanes.py asserts the equality that sentence was
gesturing at, over the walked set.
- Three lanes were missing from the roster entirely, so
cache statusreported nine caches on a machine that has twelve andcache pullcould not be asked about them. - Three had a licence permitting publication and no publish command.
- One had no way to be found at all without a flag on every invocation.
So the registry is the point, not the rebuild command. A list is only as complete as whoever last
edited it; a registry can be walked, and test_cache_lanes.py walks it against the *_build modules
on disk. That is the shape this repository keeps re-learning (@registry-completeness): assert an
equality over a walked set, never a floor and never a count in prose.
Every absence carries its reason as a field, not as a comment. A lane with no ensure states why
in unpublished, and the three reasons are genuinely different — PharmVar's and PubMind's are
refusals (a personal key; terms nobody publishes), ACMG's and MANE's are unestablished permissions,
and Ensembl's is that it is built elsewhere. A comment saying so speaks to whoever opens this file;
a field says it in cache status, in cache pull and in cache rebuild, which is where somebody
actually asks the question.
The rebuild outcome is three-valued (@tri-state). Built, failed, and could not run
unattended — ACMG needs a workbook that is Elsevier supplementary material, PharmVar needs a
personal key, CIViC needs a release date to pin. Folding the third into "failed" would have a nightly
rebuild report errors for lanes behaving exactly as designed; folding it into "built" would be a
lie. It is printed, never silently skipped.
RebuildRequest
dataclass
¶
RebuildRequest(
out_dir: Path,
declared_use: str = "unstated",
pin: str | None = None,
source: Path | None = None,
parents: dict[str, Path] = dict(),
)
What one lane's rebuild is given. The same fields for every lane, so the loop has no branches.
pin is the release to build — a MANE version, a CIViC release date, a STRchive tag. A lane that
has no notion of one ignores it; a lane that needs one and did not get it reports
could-not-run rather than quietly building the moving default, because a snapshot whose reference
was "whatever was upstream that afternoon" cannot be compared against twice.
source is the acquire stage's input when the operator already holds it — ACMG's workbook, a
ClinVar VCF, a ClinPGx archive, a STRchive catalogue. For ACMG it is the only way, because the
workbook is Elsevier supplementary material and nothing may fetch it on the author's behalf; for
the rest it is the off-switch that lets a rebuild run with no network, which is the shape
@off-switch-needs-a-probe asks for. MANE and CIViC take three input files each and accept no
source: two of three is not a build for either of them, and a flag that can only ever supply
one would be a flag that cannot do its job.
parents is where each parent lane's snapshot is, for the one lane that has any (RM171). Filled
by the caller rather than resolved inside the adapter, because a rebuild run has an answer the
registry cannot give: cache rebuild writes every lane into out/<lane>/, so the ClinVar the
miss lane should join against is the one this run just cut, not whatever is in the live cache.
A parent the caller does not name falls back to that lane's own resolver, which is what
cache prepare and a bare --only mitomap_miss want.
RebuildOutcome
dataclass
¶
Built / failed / could-not-run, and the reason in every case.
built is the tri-state: True the snapshot is on disk, False something went wrong, None
this lane cannot be rebuilt unattended and that is by design. The third is not a failure and must
not be counted as one — a nightly rebuild reporting an error for PharmVar would be reporting the
licence working as intended.
CacheLane
dataclass
¶
CacheLane(
name: str,
subdir: str,
serves: str,
build_command: str | None,
resolve: Callable[..., Path | None],
default_dir: Callable[..., Path],
env_var: str,
rebuild: RebuildAdapter | None,
ensure: Callable[..., Path] | None,
publish_repo: str | None,
terms: SourceTerms | None,
unpublished: str | None = None,
unbuilt: str | None = None,
release_label: Callable[
[Path], str | None
] = _dataset_label,
publish_command: str | None = None,
parents: tuple[str, ...] = (),
approx_mb: int | None = None,
)
One snapshot, all three of its stages, and the reason for each stage it does not have.
LaneStatus
dataclass
¶
LaneStatus(
lane: CacheLane,
state: str,
looked_in: Path,
path: Path | None = None,
release: str | None = None,
release_unreadable: bool = False,
size_bytes: int | None = None,
)
One lane as it stands on this machine — the projection cache status renders (S91, RM204).
state is three-valued. present: resolve() found a snapshot at path. absent: nothing at
the place the lane looks. occupied: the place the lane looks exists and is non-empty and holds
no snapshot — a build that failed after its downloads, a payload deleted beside its
release.json, a stray .part, a foreign parquet. It used to render as absent, which tells an
operator to run a pull that prepare is going to refuse: provisioning never deletes, so it stops
in front of a non-empty target rather than building over it. The two states want different hands
— absent wants cache pull or the lane's build command, occupied wants the directory moved
aside or cache prune.
looked_in is the directory the verdict is about: the lane's env_var if set, else its default
directory. prepare's own refusal is about the default directory specifically, so an override
pointing at a junk directory reads occupied here while prepare would build into an empty
default that the override then hides — say which directory, and the operator can see it.
release is what the snapshot names, None when it does not say; release_unreadable is the
present-and-unreadable case, a release.json that exists and does not parse, which is a
provenance failure and not a data failure — the snapshot is still usable and the line says so
rather than hiding it. Both are None/False unless present.
PrepareOutcome
dataclass
¶
One lane's provisioning result, and which route answered — not merely whether it worked.
ready is the tri-state: True the snapshot is on disk and usable, False the attempt failed,
None this lane offers no route on this machine and that is by design rather than an error.
route is the half a caller cannot reconstruct from ready, and it is the whole point of the
command: present (already there, nothing touched), pulled (downloaded from HuggingFace),
built (built locally because nothing publishes it), none. A deployment auditing its own
caches has to be able to tell a snapshot it fetched from one it made — they are different
artifacts with different provenance, and release.json says which release but not which route.
lane_name ¶
A lane name as the registry declares it, or None when nothing answers to that spelling.
Accepts a hyphen where the declared member has an underscore and returns the declared member,
never the caller's spelling — the rule every closed vocabulary in this workspace follows, and the
reason it is a function is that a caller who merely calls a normalizer and keeps its own string
has done nothing (@vocab-separator-slip). drug_labels and mitomap_miss are the two members
it matters for: both are commonly written with a hyphen, and mitomap-miss is the spelling the
design note and draft-panel --source use.
Source code in enricher/src/just_dna_enricher/caches.py
parent_snapshots ¶
Where each parent's snapshot is, and the parents that are not anywhere.
The caller's request.parents wins, then the parent lane's own resolver. Both halves are needed
and they answer different questions: a cache rebuild run has just cut a fresh ClinVar into
out/clinvar and the child must join that one, while a lone --only mitomap_miss has no such
run behind it and means the caches on this machine.
Source code in enricher/src/just_dna_enricher/caches.py
parents_from_rebuild_dir ¶
A derived lane's parents as this rebuild run just cut them, under out/<parent>/.
Two callers — rebuild_caches and the cache rebuild command — and one derivation, because the
two producing different answers is precisely the fork that would give a child pinned to one
ClinVar and sitting beside another. A parent this run did not build is left out and falls back to
the registry's resolver inside rebuild_lane, which is the --only <child> case.
Built means the payload is there, not that the directory is. A parent whose download was
cut leaves an empty out/<parent>/ behind (every adapter mkdirs first), and is_dir() handed
that to the child as a parent — so the child's join failed on a parquet that did not exist and
the child was the lane reported FAILED. Judged by the parent lane's resolver now.
Source code in enricher/src/just_dna_enricher/caches.py
rebuild_lane ¶
Run one lane's three stages, or say why it did not run.
A lane with no adapter is a None outcome carrying unbuilt — Ensembl is the only one, and its
snapshot is provisioned rather than rebuilt, so the honest answer to "rebuild everything" is that
this one is somebody else's build.
A derived lane whose parents are not on disk is the third state too, and naming the parent is
the whole point (RM171). The two wrong answers are both available and both silent: an empty miss
set reads as "MITOMAP publishes nothing ClinVar lacks", which is a claim about the world derived
from a comparison that never ran, and a False would file the absence as this lane failing when
what is absent belongs to another one. The guard is here rather than in the adapter because it is
registry-driven — it is lane.parents that decides, so a second derived lane inherits it.
Source code in enricher/src/just_dna_enricher/caches.py
snapshot_bytes ¶
Bytes on disk under a provisioned snapshot — every regular file, or the one file itself.
Source code in enricher/src/just_dna_enricher/caches.py
provisioning_closure ¶
The lanes a blank box has to hold for lane to build: its transitive parents, then itself.
In registry order, each lane once, parents before children — the order prepare already walks.
A derived lane's approx_mb prices the increment it stores; the closure prices what provisioning
it actually costs (S97): mitomap_miss is under a megabyte and its closure is a ClinVar download.
Source code in enricher/src/just_dna_enricher/caches.py
lane_status ¶
Where every lane stands, read-only — nothing is downloaded and nothing is written.
The registry's status half, beside its provisioning half (prepare_caches). It existed only as
the loop inside cache status until a consumer serving the same answer over HTTP wrote the loop a
second time (S91), which is two projections of one registry — the shape RM176 exists to end. In
registry order, one entry per lane, so a renderer that iterates it renders the whole registry.
Source code in enricher/src/just_dna_enricher/caches.py
prepare_lane ¶
Provision one lane by whichever route it has: pull it, or build it, or say why neither.
The route is a property of the lane, never a flag. A lane with an ensure is published, so
pulling is right and building would spend an operator's bandwidth re-deriving bytes somebody
already made. A lane without one is unpublished for a recorded reason — PharmVar's personal
key, PubMind's absent terms, NCBI's policy, ACMG's supplementary material — and building locally
is the only route there will ever be. Asking the caller to choose would be asking them to restate
the licensing story as a flag.
A present cache is left alone, exactly as cache pull leaves one alone: provisioning is
idempotent and cheap to re-run, and re-deriving a snapshot somebody is reading is how a resolver
comes to see a half-written table. Re-cutting one is cache rebuild, which writes somewhere else
on purpose.
Source code in enricher/src/just_dna_enricher/caches.py
1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 | |
prepare_caches ¶
prepare_caches(
lanes: list[CacheLane] | None = None,
*,
declared_use: str = "unstated",
pins: dict[str, str] | None = None,
sources: dict[str, Path] | None = None,
) -> list[PrepareOutcome]
Provision every cache by its own route — the Python half of cache prepare.
The counterpart to rebuild_caches, and a real API rather than a loop the CLI happens to own: a
deployment's own provisioning step is Python far more often than it is a shell script, and the
alternative is every caller re-deriving which lane pulls and which builds from the licensing
story. That derivation is the command.
Source code in enricher/src/just_dna_enricher/caches.py
rebuild_caches ¶
rebuild_caches(
lanes: list[CacheLane] | None = None,
*,
out: Path,
declared_use: str = "unstated",
pins: dict[str, str] | None = None,
sources: dict[str, Path] | None = None,
) -> list[RebuildOutcome]
Rebuild every lane into out/<lane>/ — the Python half of cache rebuild.
Separate from prepare_caches because the two answer different questions and a flag joining them
would hide that. Rebuild re-derives, into a directory nothing is reading, so a deployment can
cut a new set and adopt it deliberately. Prepare provisions, into the live cache locations,
and leaves a lane that already has a snapshot alone. A caller wanting "make sure every cache
exists" wants prepare; one wanting "cut a fresh set from today's sources" wants rebuild.