just_dna_enricher.pharmvar¶
just_dna_enricher.pharmvar ¶
Live PharmVar queries — star-allele definitions and allele function (0.5).
PharmVar is the naming authority for the CYP star alleles, and the only PGx source here that is not
part of the ClinPGx merger. It supplies two things this workspace has no other route to:
haplotypes.csv material (which variants define *2) and allele_function.csv material (what that
allele does), both with GRCh38 genomic coordinates and dbSNP ids already attached.
Access changed under us and the failure mode is silent. The API now requires a key, and every
endpoint returns the same 401 {"errorMessage": "API Key is invalid or missing"} whether the key is
absent, malformed, or passed under the wrong parameter name — so a wrong header looks exactly like a
bad key. The parameter is Api-Key, a plain header with no X- prefix, documented per-endpoint
in the service's own OpenAPI document (docs/vendor/pharmvar_api_docs.json) rather than in a
securityDefinitions block, which is why it is easy to miss.
The key is personal. PharmVar's terms §2 make an account non-transferable, so the key is read
from the environment and never written anywhere: not into a module, a recorded fixture, a log line,
or a snapshot. PharmVarClient keeps it in the header only.
Rate limit: 2 requests/second. Low enough that per-allele fetching is hopeless and coarse
endpoints are the only sane shape — /variants/gene/{symbol} returns a gene's whole variant set in
one call. The unfiltered /alleles collection is ~25 MB and ignores a geneSymbol query parameter it
does not define, so it is never used here; reference-sequence filtering is applied server-side
instead.
Coordinates arrive as HGVS g. strings against several reference sequences at once — a transcript
(NM_…:c.), a gene region (NG_…:g.) and the chromosome (NC_…:g.). Only the NC_ form is a
genomic coordinate, and it is 1-based, which matches what this pipeline already stores (see the
resolution table): the instinctive conversion to 0-based would introduce an off-by-one.
PharmVarError ¶
Bases: RuntimeError
A PharmVar request failed in a way the caller must see (auth, or a broken query).
PharmVarVariant
dataclass
¶
PharmVarVariant(
rsid: str | None,
chrom: str | None,
start: int | None,
ref: str | None,
alt: str | None,
)
One defining variant of a star allele, at a genomic coordinate.
PharmVarAllele
dataclass
¶
PharmVarAllele(
gene: str,
allele: str,
function: str | None = None,
activity_value: float | None = None,
evidence_level: str | None = None,
is_core: bool = True,
variants: list[PharmVarVariant] = list(),
)
One star allele: its identity, its function, and the variants that define it.
PharmVarClient ¶
PharmVarClient(
endpoint: str = DEFAULT_PHARMVAR_ENDPOINT,
*,
api_key: str | None = None,
client: Client | None = None,
gate: PacingGate | None = None,
timeout: float = 60.0,
)
Paced PharmVar REST client. The API key comes from the environment and is never persisted.
Source code in enricher/src/just_dna_enricher/pharmvar.py
configured
property
¶
Whether a key is available. Absent is a skip, not an error — the pass degrades.
alleles_for_gene ¶
Every allele PharmVar defines for one gene, with its defining variants.
Sub-alleles (*2.001) are excluded by default: the core star is the identity this workspace
keys on (AlleleFunctionRow.allele), and including them multiplies the table roughly threefold
without changing any lookup.
Source code in enricher/src/just_dna_enricher/pharmvar.py
all_genes ¶
Every gene PharmVar defines, with its alleles — one request for the whole database.
/genes returns the same shape as /genes/{symbol} for all fifteen genes at once (1,173 core
alleles, ~5 MB), which at 2 rps is the difference between one call and fifteen. That matters
only for the snapshot builder, which is its only caller; the runtime passes stay gene-scoped
because a module's own tables say which genes it is about.
Source code in enricher/src/just_dna_enricher/pharmvar.py
PharmVarSnapshotClient ¶
alleles_for_gene answered from an operator-built snapshot (RM38).
Duck-typed against PharmVarClient so enrich_pgx needs no branch, and configured is True
because the question it answers — "can this leg run?" — is about reachability, not about a key. A
snapshot exists or it does not, and locations.resolve_pharmvar_reference already decided that
before this is constructed.
There is no all_genes here: that endpoint exists to build a snapshot, and a snapshot does not
build itself. Read with duckdb (core) rather than polars ([dev], builder-only).
Source code in enricher/src/just_dna_enricher/pharmvar.py
configured
property
¶
True: a resolved snapshot is the credential's stand-in, and it is already in hand.
genome_build
property
¶
The assembly the stored coordinates are on. Recorded by the builder, never assumed.
close ¶
alleles_for_gene ¶
Every allele the snapshot holds for one gene, with its defining variants.
include_sub_alleles is accepted for signature parity and cannot be honoured: the builder
fetches core alleles only, so asking for sub-alleles here would be answered with a subset while
looking like a full answer. It raises instead — a wrong answer that looks right is the one
outcome worth refusing.
Source code in enricher/src/just_dna_enricher/pharmvar.py
chrom_from_accession ¶
NC_000010 → 10, NC_000023 → X. None for anything off the primary assembly.
RefSeq numbers the chromosomes 1–22 then X (23), Y (24), MT (12920 as a special case). Returning None rather than guessing keeps an unplaced contig out of the coordinate space entirely.
Source code in enricher/src/just_dna_enricher/pharmvar.py
parse_genomic_variant ¶
parse_genomic_variant(
payload: dict[str, Any],
*,
build: str = PHARMVAR_GENOME_BUILD,
) -> PharmVarVariant
One variants[] entry → a coordinate, when it names one on build.
A variant appears several times per allele, once per reference sequence. Only the NC_ genomic
row yields a position; the transcript rows still carry the rsID, so the caller merges by rsID
rather than discarding them.
And only the NC_ row whose referenceCollections names build does — PharmVar publishes both
assemblies and lists GRCh37 first, so accepting any NC_ row stores the wrong coordinate for most
variants. See PHARMVAR_GENOME_BUILD.
Source code in enricher/src/just_dna_enricher/pharmvar.py
parse_allele ¶
One /alleles entry → a PharmVarAllele.
activityScore is absent for most alleles and non-numeric for some ("n/a"), so it is parsed
defensively rather than trusted — a non-numeric score becomes None instead of failing the allele.