just_dna_enricher.clingen_allele¶
just_dna_enricher.clingen_allele ¶
The ClinGen Allele Registry — a CAID resolved to an identity this format can carry (RM153).
A ClinGen allele_registry_id (CA341482) is a build-independent name for an allele. CIViC
publishes one for most of the variants whose coordinates it only has on GRCh37, so the registry is the
route that places those rows without lifting anything over — which is the whole reason this module
exists rather than a liftover.
It returns an rs-number where it can, and a GRCh38 coordinate only otherwise. Both are usable, and the preference is not stylistic. An rsID is build-independent, so a drafted row carrying one is placed by the ordinary resolution chain rather than by us — and the value resolution then verifies came from dbSNP via ClinGen, while the chain that verifies it is Ensembl. Two authorities, so the check is real. That is the property RM48 refused a liftover for lacking: a lifted coordinate is the row's sole identity with nothing independent to check it against, and an rs-number recovered from the same service that will later be asked is the same defect wearing a different hat.
Three outcomes, never two. resolved / no_identity / unchecked. A registry that answers and
holds no placeable identity for a CAID (an indel whose alleles it cannot express as a substitution) is
a fact about the registry; a request that failed is not. Collapsing them is the S20 defect —
([], None) reading a failed request as an established absence — and it is the reason the outcome is a
dataclass rather than an optional tuple.
Terms could not be established, and that is recorded rather than assumed. The registry is run on
Baylor's Genboree infrastructure and its own /site/terms answers HTTP 200 with a generic
"broken link" page rather than a licence — the same shape as HPO's 404-behind-a-200, established by
probe on 2026-08-31. ClinGen's gene-curation surface is CC0, and this is a different surface, so that
grant is not evidence about these bytes. Unknown is not permissive: nothing here is redistributed, and
reading a public endpoint to place a row is a read rather than an acquisition anyone has gated
(@acquisition-gate-is-not-a-read-gate).
ClingenAlleleError ¶
Bases: RuntimeError
The registry could not be consulted in a way the caller must handle.
AlleleIdentity
dataclass
¶
AlleleIdentity(
caid: str,
outcome: str,
rsid: str | None = None,
coordinate: tuple[str, int, str, str] | None = None,
unanchored: tuple[str, int, str, str] | None = None,
)
What the registry holds for one CAID.
rsid and coordinate are both optional and at least one is set when outcome == "resolved";
coordinate is (chrom, start, ref, alt) with start the 1-based position this format's
start column means (@start-1based), converted from the registry's interbase start/end pair.
placeable
property
¶
True when this carries something a VariantRow can be identified by.
unanchored is deliberately not placeable: a half-stated allele is not a position
(@identity-whole-or-none), and it becomes one only once anchor_indel has read the base.
ClingenAlleleClient ¶
Resolve CAIDs, one at a time, paced, with a per-run cache.
A class rather than a function because the pacing gate and the cache are per-run state, and a module-level one would make two callers in one process share a clock they did not agree on.
Source code in enricher/src/just_dna_enricher/clingen_allele.py
resolve ¶
One CAID → an identity, an established absence, or nobody-asked.
Source code in enricher/src/just_dna_enricher/clingen_allele.py
anchor_indel ¶
anchor_indel(
unanchored: tuple[str, int, str, str], read_base
) -> tuple[str, int, str, str] | None
A one-sided indel plus one reference base → a VCF-style row, or None if the base is unknown.
This is VCF's anchored form at the registry's own position, which is not always the left-aligned
one (RM273). An insertion and a deletion each state one side of the change and leave the other
empty, which no ref/alts pair can hold; anchoring prefixes both sides with the single reference
base immediately before the event, so ref and alts are both non-empty and the row means exactly
what the registry said. The registry's interbase point follows HGVS's 3′ rule, so inside a repeat
the result sits right of VCF's left-aligned spelling (rs72613567: 4:87310241 A>AA, where VCF
writes 87310240 T>TA); a caller writing a row left-aligns it after (sequences._left_align):
deletion of `A` after base P → POS=P, REF=<base P> + "A", ALT=<base P>
insertion of `G` after base P → POS=P, REF=<base P>, ALT=<base P> + "G"
The registry's interbase start is already that P, for both shapes, which is why one rule covers
them and no per-shape arithmetic appears here.
read_base is injected — (chrom, one_based_pos) -> str | None — so this stays a pure function
with the network on the outside, and so a test can exercise both shapes without a sequence
service. None when the base cannot be read: an unknown anchor is withheld, never guessed, and a
guessed anchor would put a wrong ref on a right position, which is the mismatch class
sequences.RefMismatch exists to report.