Skip to content

Fix CI: don't require uninstalled Ensembl release 115 - #399

Merged
iskandr merged 5 commits into
mainfrom
fix-ci-release-115
Jul 8, 2026
Merged

Fix CI: don't require uninstalled Ensembl release 115#399
iskandr merged 5 commits into
mainfrom
fix-ci-release-115

Conversation

@iskandr

@iskandr iskandr commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

main is red (since dead346, the #396 merge). Three tests depend on Ensembl release 115, which the openvax/ensembl-data mirror doesn't publish — it tops out at GRCh38.95 — so CI can't install it. The failures were masked on developer machines that happen to have release 115 cached locally.

FAILED tests/test_frameshift_cterminus_regression.py::test_protein_diff_frameshift_keeps_cterminus_matching_reference_suffix - ValueError: GTF database needs to be created, run: pyensembl install --release 115
FAILED tests/test_frameshift_cterminus_regression.py::test_fast_and_protein_diff_agree_on_frameshift_tail        - ValueError: ... release 115
FAILED tests/test_timings.py::test_effect_timing                                                                  - ValueError: ... release 115

Fixes

Verification

  • The three affected tests pass locally (release 115 present); the skip-guard fires with the exact ValueError: GTF database needs to be created CI hit when a release is absent.
  • ruff clean.

Note

Root cause is pyensembl-release drift: genome_for_reference_name("GRCh38") now points at 115. Worth a durable follow-up — e.g. pin the default release used by random_variants, or pin the pyensembl version in requirements.txt — so a future "latest" bump doesn't silently break CI again.

https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c

iskandr added 5 commits July 8, 2026 16:10
CI (and any env without release 115) was failing three tests because they
depend on Ensembl 115, which the openvax/ensembl-data mirror doesn't publish
(it tops out at GRCh38.95):

* test_frameshift_cterminus_regression.py hardcodes cached_release(115) for
  the ATM p.F61fs regression from #396. Guard it: skip cleanly at module
  level when release 115 isn't installed. The same bug CLASS is covered on
  release 81 by test_annotator_divergence_scenarios.py and
  test_protein_diff_parity.py, so CI coverage of #396/#397 is unaffected.

* test_timings.py used random_variants(), whose genome_name='GRCh38' default
  resolves via genome_for_reference_name to pyensembl's LATEST release (now
  115) rather than an installed one. Add a backward-compatible ensembl= param
  to random_variants and pin the timing test to cached_release(81).

This unbreaks main, which went red at dead346 (#396 merge) — the failures
were masked locally on machines that happen to have release 115 cached.

Claude-Session: https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c
Pinning test_timings to release 81 surfaced a latent bug: random_variants
draws from all of ensembl.transcript_ids(), which on release 81 includes
transcripts on alternate/patch scaffolds (e.g. CHR_HSCHR19LRC_COX1_CTG3_1)
that Variant() rejects as non-standard for GRCh38, raising ValueError and
failing the whole generator. It was flaky rather than deterministic because
the timing test's warmup draw is unseeded.

Wrap the per-draw body in try/except ValueError and skip to the next
transcript, so an unlucky pick no longer fails generation. The count*100
iteration budget and the terminal 'Unable to generate' error still catch a
systematically-broken genome. Verified: 40 seeds x 50 variants on release 81
now all succeed.

Claude-Session: https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c
The previous commit wrapped Variant construction in try/except, but the
'Invalid contig name' ValueError is raised LAZILY -- from the .transcripts /
.genes properties via _check_that_genome_has_contig, not from __init__ -- so
construction succeeded and the error only surfaced later in .effects(). That
made the earlier fix (and its stress test, which never called .effects())
ineffective; CI kept failing on a different alt scaffold (CHR_HSCHR11_2_CTG1).

Filter transcripts up front to those whose contig is in set(ensembl.contigs())
-- the exact validity set _check_that_genome_has_contig uses -- so every
returned variant is annotatable. Variant preserves the scaffold contig name
(verified), so the filter aligns exactly with the validator. Note contigs()
differs by environment (locally it includes alt scaffolds, CI excludes them);
because filter and validator both call contigs(), they stay consistent either
way. Verified under a simulated-CI contigs() (272 scaffolds excluded):
25 seeds x 30 variants generate + annotate with zero failures and no leaked
contigs.

Claude-Session: https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c
Third time's the charm. The prior filter used set(ensembl.contigs()), but the
lazy validator (_check_that_genome_has_contig) checks self.genome.contigs(),
and self.genome = infer_genome(ensembl) is NOT guaranteed to be the same object
as the passed ensembl -- in CI their contig sets differed, so the filter
excluded nothing and CI kept failing on CHR_HSCHR11_2_CTG1.

Validate each built variant against variant.genome.contigs() -- the exact same
object and call the validator uses -- so the check is consistent with effect
prediction by construction, regardless of how infer_genome resolves ensembl or
how contigs() varies across environments. valid_contigs is computed once from
the first successfully-built variant (all share a genome).

Verified by patching the resolved genome's contigs() to exclude its 272 alt
scaffolds (simulating CI): 25 seeds x 30 variants now generate AND annotate
with zero failures and no leaked contigs.

Claude-Session: https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c
… path)

My prior two attempts compared the variant's contig against a locally computed
contig set (ensembl.contigs(), then variant.genome.contigs()). Both were
inconsistent with the validator, because Variant._check_that_genome_has_contig
reads a PROCESS-WIDE cache (_reference_name_to_valid_contig_names, keyed by
reference name) that an earlier test populates with a scaffold-free set. So my
set could include scaffolds the cached validator set excluded, and CI kept
failing on a different alt scaffold each unseeded run.

Skip via the exact path the caller uses: after constructing the variant, access
variant.transcripts (which runs _check_that_genome_has_contig against the shared
cache) inside the try/except. A scaffold variant raises there and is skipped;
what survives is guaranteed annotatable by the same cache .effects() will read.
Also drop variants that overlap no transcript.

Verified by seeding _reference_name_to_valid_contig_names['GRCh38'] with a
scaffold-free set (as CI's earlier tests do) and generating + annotating across
20 seeded and 10 unseeded draws: zero failures, no leaked scaffolds.

Claude-Session: https://claude.ai/code/session_01VNtZRyKZ7u9jiMGPQEbx4c
@iskandr
iskandr merged commit ac6b392 into main Jul 8, 2026
8 checks passed
iskandr added a commit that referenced this pull request Jul 9, 2026
test_collection_filtering.py calls variants.effects() at module import
using the db_snp_variants fixtures from tests/data.py. Those Variants
defaulted to reference_name "GRCh38", which pyensembl resolves to its
LATEST known release (currently 115) rather than an installed one. The
CI mirror (openvax/ensembl-data) tops out at GRCh38.95, so collection
intermittently died with "GTF database needs to be created" whenever
the latest-release GTF wasn't present -- a pre-existing flake on main's
default run, not something the annotator switch introduced.

Pin the three snps to cached_release(81) -- the installed release the
downstream transcript-id assertions (ENST00000371321, ...371763,
...464755, ...613244) were written against. Same class of fix as #399.
iskandr added a commit that referenced this pull request Jul 9, 2026
test_collection_filtering.py calls variants.effects() at module import
using the db_snp_variants fixtures from tests/data.py. Those Variants
defaulted to reference_name "GRCh38", which pyensembl resolves to its
LATEST known release (currently 115) rather than an installed one. The
CI mirror (openvax/ensembl-data) tops out at GRCh38.95, so collection
intermittently died with "GTF database needs to be created" whenever
the latest-release GTF wasn't present -- a pre-existing flake on main's
default run, not something the annotator switch introduced.

Pin the three snps to cached_release(81) -- the installed release the
downstream transcript-id assertions (ENST00000371321, ...371763,
...464755, ...613244) were written against. Same class of fix as #399.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant