Changed
pharmacogenomicsrefuses--include-gwas,--gwas-all,
--gwas-min-magnitude,--exclude-snpediaand--snpedia-min-magnitude
with a usage error (exit 2), even at their default values, before it reads
the input or refreshes a cache. Its report keeps ClinPGx findings only, so
these options decided whether the GWAS Catalog and SNPedia ran but could
never add or remove a finding. The error names the option, for example:
"Error: --include-gwas never applies to pharmacogenomics: its report shows
only ClinPGx findings, so no GWAS Catalog or SNPedia finding can reach it.
Leave the option out, or use analyze for those sources." A mistyped
option is not answered with a suggestion of one of these five. To switch
SNPedia off for every command, setsources.snpediato false in the
config. ADR-0024 is amended.
analyzeandmethylationkeep these options as they were.- The PyPI upload now checks the release itself before it uploads. The
publish workflow's first job checks out the release tag and stops unless
the tag names the released commit and equalsvplus the package
version; the CI workflow's run for that tag push on that commit has
completed with every job passed, the version check and both test jobs
among them (it waits up to 30 minutes while CI is still running); and
the wheel and the sdist it builds each install in a clean virtual
environment of their own and report that version throughallelix --versionand the package metadata. It
runs no tests itself. The upload job then publishes exactly the files
that job built and installed, without a checkout or a rebuild, and only
that job can request the PyPI token. Every action the publish workflow
uses is pinned to a commit. Before, the upload built and published from
the release without any check of its own. - New report files are private: a JSON or HTML report (
--outputof
analyze, methylation and pharmacogenomics), an annotated VCF
(--vcf-out) and the.bed,.bimand.famofexport plinkare
created readable and writable by their owner alone (0600), whatever the
umask. They hold personal genetic data; before, a new file got the mode
the umask gives (0644under the usual022, readable by every user of
the machine). A file that already exists keeps its mode, so a report you
made readable stays so.config.tomland the caches keep the umask's
mode. Terminal output redirected to a file is created by the shell, as
before. - In the HTML report, the ⚠ beside a flagged genotype explains itself as
soon as the pointer is on it, or when it has keyboard focus. The
explanation was the browser's own tooltip, which waits about a second
and which some viewers (embedded previews) never show; it is now drawn by
the page, from the same text screen readers read, beside the mark and
inside the window (above the mark near the bottom edge). Touch screens have no
hover: tapping the row opens the panel, whose Plausibility line explains
the warning, as before. The wording is unchanged. - Downloads and freshness checks follow a redirect only to an HTTPS
address. Every source's download, its freshness check and CPIC's
allele-function fetch went through urllib's default redirect handling,
which follows a redirect from HTTPS to plain HTTP (or FTP); a server, or
anyone who could alter its reply, could send Allelix to fetch the file
unencrypted. Such a redirect is now refused at any hop, before anything
is fetched from it. A download that an upstream redirects to plain HTTP
now fails, with "refused a redirect from to a non-HTTPS URL"
(db updatereports the source as failed and keeps the cache it has),
and a freshness check says the source cannot be verified:db update,
and the automatic checkanalyze,methylationandpharmacogenomics
run on a cache over 7 days old, which said "Runallelix db updatewhen
online" whenever the signal could not be fetched and now says the
cache is used anddb updateretries. HTTPS redirects work as before,
and a redirected request keeps its method on every Python version: a
freshness check stays a HEAD request at every hop, where Python 3.12's
urllib turned it into a GET. This protects the transport only: the GWAS
Catalog and ClinPGx still publish no checksum for their downloads, so
their content is checked only by HTTPS, its size and the row floor. - A downloaded ClinPGx or GWAS Catalog archive is checked for what it
would expand to before anything is unpacked. ClinPGx's archive was
unpacked whole and its tables read into memory to compute its content
hash, and the GWAS Catalog's table was unpacked, with no limit on the
expanded size, so a damaged or crafted archive could fill the disk or
the memory. The members an update unpacks are checked: for ClinPGx
every member of the archive, before its content hash reads either table,
so also when the tables are unchanged; for the GWAS Catalog its one
table, the only member it reads. They may now expand to 4 GiB in all;
today's ClinPGx archive expands to about 11 MB and the GWAS Catalog's
table to about 710 MB. A larger archive is refused before any member is
read: "the archive would expand to N bytes, over the size limit; nothing
was unpacked",db updatereports the source as failed, and the cache
it has is kept. Only stored and deflated members are unpacked (both
sources use deflate): a checked BZIP2 or LZMA member, whose decoder
decompresses before the size can stop it, is refused the same way,
before any member is read. ClinPGx's content hash now reads its two
tables in 1 MiB chunks; its value is unchanged, so no cache rebuilds. - JSON reports use schema 14 (2.6.3 wrote 13), and
--diffreads
baselines in schemas 1 to 14.report_warningsadds the codes
split_site_calls,clinvar_rsid_placed_elsewhereand
clinpgx_ambiguous_strand, andhigh_value_uncheckedadds the reason
placed_elsewhere.
Fixed
- The HTML report shows an rsID read at two places as two rows. A VCF can
give one rsID to two records; with the same genotype at both, the report
put them in one row and one detail panel. The panel then showed the
gnomAD frequency, CADD and AlphaMissense values of only one of them, the
warning mark of either, and a source count, gene and summary pooled from
both, and the Good and Bad counts counted them once. Each VCF record (on
a chip file, each position) is now its own row. When rows share an rsID,
each panel shows the position, and so does the terminal table under the
rsID. Other rows, the JSON report and panel coverage are unchanged. --diffpairs a finding only with the baseline finding at the same
chromosome and position. It paired findings by source, rsID, condition
and allele alone, so when one rsID had findings at two sites the diff
could pair them across sites: two sites that swapped classifications
showed no change, and a finding that moved from one site to the other
showed as changed. Each site is now its own finding, so the swap is two
changes, and a finding whose position changed between the reports (a
placement fix, for example) shows as removed and new. When one rsID is
at two or more sites in the diff, the terminal's New, Changed and Removed
tables show each row's position under the rsID, and so does the HTML
report's list of changed findings, so a moved finding's two rows can be
told apart. A baseline entry
without a position (from a report before 2.5.0, JSON schema 9, or a call
the report could not place) is paired as before.--vcf-outnever replaces a file it did not write. It refused an
existing file only at start-up, then moved its result into place over
whatever had the name by then: a file another program created during the
run was replaced, and a dangling symlink counted as no file, so the link
itself was replaced. A dangling link is now refused at start-up like a
file, and the annotated VCF is committed with a hard link, which fails
if anything has taken the name meanwhile; that file or link is left as it
is, and the run stops with an error naming the path. This holds from
start-up to the end of the run. A file system without hard links (FAT,
exFAT, some network shares) cannot publish the complete file atomically
without replacing an existing name, so--vcf-outthere stops with an
error naming the path and leaves nothing at it: write the annotated VCF
to a local disk and move it.--outputreports still replace an existing
report, as before.- A chip file that writes its chromosomes as
chr1,CHR1orChr1
(23andMe, AncestryDNA, Family Tree DNA, MyHeritage, Living DNA,
MyHappyGenes and GenomeStudio files keep the file's own names) is placed
on its build like one that writes1. Build detection compared the
file's name with the bare names of its known-SNP table, so such a file
matched none of the supported builds and was refused, and with--build
the warning said none of its known-SNP positions matched. The vote now
compares the standard name. The names the reports and PLINK output show
are unchanged. - A VCF or gVCF whose header names a testing company is read as a VCF. The
vendor formats were recognized before VCF, and the MyHeritage one by the
company's name in the first lines, so a VCF converted from a MyHeritage
file (##source=bcftools convert MyHeritage_raw_dna_data.csv, as
bcftools convert --tsv2vcfwrites) was read as a MyHeritage file, gave
no records and was refused. A file whose header says##fileformat=VCF
is now recognized as VCF first, plain or gzipped, whatever its name.
MyHeritage's own exports, its low-pass##fileformat=MyHeritagefiles
included, are read as before. - A VCF call whose GQ or DP is not a finite number (
inf,-inf,
Infinity, or an exponent too large such as1e999) is read as if the
value were missing, like.. Before, the run stopped with an unexplained
OverflowError. The value is not taken as zero reads or as a high
quality: the call's other quality field still applies (a GQ below
--min-gqor a DP of 0 still marks it low-confidence). --vcf-outrefuses a name ending in.gzor.bgz(any case) before
the analysis starts, and so does the annotated-VCF writer. The annotated
VCF is written uncompressed; before,--vcf-out out.vcf.gzexited 0 and
wrote plain text under a gzip name, whichgzipcannot read andtabix
cannot index. Use a.vcfname and compress the file afterwards
withbgzip. Compressed input is read as before.- A GWAS Catalog association no longer takes its risk allele from another
SNP. The catalog's "strongest SNP-risk allele" field names a SNP and its
risk allele (rs123-A), and Allelix read only the allele after the last
hyphen, so a row for rs220826 whose field saysrs220818-Gwas loaded as
rs220826 with risk allele G. A risk allele is now read only when that
field names the row's own rsID (in any letter case). A row whose field
names another rsID (other_snp) or something that is not an rsID
(unattributed) is skipped before the lowest p-value row is chosen for
its rsID and trait, so another row of the same association can be
chosen. Allelix has no rsID merge history, so a row that names a merged
rsID is skipped as well.db updateanddb statusshow how many
source rows the cached release skipped for each reason, apart from the
record count. Rows whose field has no single-base allele (rs123-?)
load without a risk allele, as before. The GWAS cache is rebuilt on the
nextdb update, from the retained catalog table when it matches the
cached release, otherwise by a new download. - ClinVar findings for a pseudo-autosomal variant reach a call on either X
or Y. ClinVar lists such a variant under one VariationID on both
chromosomes, and the cache kept only the first of the two rows, which in
practice was X: a VCF that writes a male's pseudo-autosomal calls on Y
coordinates got no ClinVar match there. Every placement is now kept, each
with the variant's submissions; a variant's submitter split still counts
each submission once. In the retained public variant_summary source,
about 2,400 X/Y VariationIDs per build have two placements, 93 of them
classified Pathogenic or Likely pathogenic on GRCh38 (94 on GRCh37).
Both ClinVar
caches are rebuilt on the nextdb update, which downloads ClinVar
again and can take about 20 minutes. ADR-0039 is amended. - ClinPGx finds a homozygous carrier of the variant allele at a site whose
ClinPGx genotypes are written on the other strand from the input's
build. A GRCh37 G/A call at such a site matched its ClinPGx row by the
complement, but an A/A call did not, because the complement match asked
for the complement of the call's REF in the row's genotype, which a
homozygous row of the other allele never holds. Now, where all of the
rsID's ClinPGx genotypes (rows not reported as findings included) fit the
rsID identity table's alleles at the call's locus (for an rsID-list row
without a position, at the rsID's one site) only as their complement, a
single-base call (both carried alleles single bases, REF one base or not
known) matches a row by its complement, homozygotes included. An indel
call is not matched this way: its trimmed or padded anchor letter can
spell another variant's allele at the site. There the complement is the
call's one reading: a row spelled as the call, which at a three-allele
site is the other homozygote's genotype, is not also matched. Where the
genotypes fit the site only as written, the exact match is the one
reading, and the call's REF no longer adds a complement match (a G/T call
at a G>A,C site met the AC genotype). Where that
site evidence is missing or uncertain (no identity table, the rsID not
placed at the call or placed more than once, a palindromic site, or
alleles that fit both ways or neither way), and for an indel
call, the exact match and the complement match by the call's REF apply
as before, except that a call one row meets as written and another as
its complement meets neither: ClinPGx cannot tell which reading is the
person's, and the report counts such calls inreport_warnings
(clinpgx_ambiguous_strand). rs28364032 (CRHR1) is one such site on GRCh37: a
G/A call already met its CT row, and an A/A call now meets its TT row
(evidence level 3). ADR-0035 and ADR-0066 are amended. - GWAS Catalog and SNPedia findings no longer go missing where the rsID
identity table stores a single-base variant padded with a shared base
(CG>CAone position beforeG>A, orGT>AT). The strand check found
no forward alleles at such a site and counted the call as
not_in_identity_table, and an unnamed call there could not recover its
rsID. The table's rows are now read as their trimmed variant, as the
input already is, so these sites give the same findings as a table that
storesG>A. Genuine indels are unchanged. The installed table needs no
rebuild; the current one has 20 such rows on GRCh37 and 70 on GRCh38.
ADR-0066 is amended. - A
0/0call inside a symbolic deletion the sample carries is no longer
read as a reference call. A VCF record with ALT<DEL>(or<DEL:...>)
and a genotype carrying it removes the bases after its POS through its
INFOEND, but only a literal deletion's REF span blocked the reference
calls inside it: a0/0SNV ten bases into a called<DEL>still gave
its GWAS, SNPedia, ClinPGx and ClinVar reference-carried findings. The
deletion's span now blocks them as a literal deletion does, for the
selected sample only. It also blocks them where the genotype may carry
the deletion: an allele is unknown, or the quality gate rejects the call
(FILTER or FT, a GQ below the floor, DP 0), even with a0/0genotype.
Other symbolic ALTs, a deletion the genotype confidently does not carry
(a passing call of REF or of another ALT), and records without a valid
ENDare unchanged. ADR-0064 is amended. - A two-ALT VCF call split over two records gives the person's genotype.
G>A 1/0plusG>T 0/1(howbcftools norm -m-writes a1/2call)
gave the SNPedia and ClinPGx rows forAGand forGT, neither of
them this person's, and missed theATrow the merged record gets.
analyze,methylationandpharmacogenomicsnow read that exact
complementary pair as the merged call: one ALT on each haplotype,
unphased (1/0and0/1) or phased in one phase set (1|0and
0|1). Each allele's findings stay with its own record in
--vcf-out, and the HTML report shows the call as one row, with one
panel and one count, as it does the merged record. Any other mix of single-base records of one REF that name
different ALTs at one position (the same GT twice, both ALTs on one
haplotype, different phase sets, a homozygote with a heterozygote, a
half call, a record the quality gate rejects, a haploid with a diploid
call, a third ALT, two IDs) gives no call there, and the report counts
those records assplit_site_calls, including in a file where they
are the only calls, which is analyzed rather than refused as empty or
structural-only. Before, each record gave its own genotype. Records that all name one ALT, records that disagree on REF,
indel or symbolic records at the same position, and records of one
position that are not next to each other are unchanged;compare,
extract,statsandexport plinkstill read every record on its
own. ADR-0062 is amended. - The report says when ClinVar leaves out an exact match because it
places the call's rsID elsewhere. A call whose rsID ClinVar holds only
at another position is, rightly, not matched to the row of another
variant at its own position, but nothing said so, and with the rsID
identity table switched off nothing else noticed. Where the call
carries that row's exact allele (the reference base included, for a row
whose allele the reference carries), it is now counted in
report_warnings(clinvar_rsid_placed_elsewhere),
attributed to ClinVar, and named inhigh_value_unchecked
(placed_elsewhere) at a high-value rsID or one ClinVar classifies
Pathogenic or Likely pathogenic itself. The match itself is unchanged,
except for an explicit VCF reference call (0/0, haploid0) at a site
where the reference carries a ClinVar row's allele: it was called under
that row's rsID whatever rsID its record named, so it matched the row of
another variant, uncounted. It now keeps the rsID its record names where
ClinVar places that rsID only elsewhere, and is withheld and counted as
a0/1or chip call is (or set aside asidentity_locus_conflictwhere
the identity table places it elsewhere). A reference block, or a record
whose REF is longer than one base, names no rsID for this, and its call
is made under the row's rsID as before. In--vcf-out, an explicit
single-base reference record withheld by this guard no longer carries
the other variant's finding. The decision is ClinVar's only: other
sources match the call by their own rules.
ADR-0051 is amended. - A ClinVar, GWAS Catalog or ClinPGx update no longer installs its new
cache beside the old cache's unrecovered journal. Those installs moved
the new file in with a plain rename, so a hot-journalor a-wal
left by an interrupted earlier write stayed beside it: the next
read-write open rolled the old content over the new file, read-only
opens failed whiledb updatestill said "ready", and ClinVar then
refused every later update. They now publish as the prebuilt caches
already did: the old cache settles its own side files first, or the
install stops withUnrecoveredJournalError(which namesdb clean)
and the old cache keeps its rows. ClinVar checks both builds' old caches
before replacing either; each build is then replaced on its own, and if
the second fails after the first was replaced, the error says which
build was already updated. The rsID identity table's writer and the
gnomAD, AlphaMissense and CADD cache builders do the same. The writers
that change a cache in place are not changed. ADR-0048 is amended. - An HTML report with no findings to show no longer stops its page script
with an error. The script read the page's finding data before it checked
for the findings table, and such a page has neither, so the browser
logged aTypeErrorwhen the page loaded. The page showed its notices
and empty-state text all the same, and pages with findings are unchanged.