Skip to content

feat: add an opt-in MyGene.info backend for the Ensembl pull - #976

Open
SkyeAv wants to merge 3 commits into
mainfrom
feat/ensembl-mygene-access
Open

feat: add an opt-in MyGene.info backend for the Ensembl pull#976
SkyeAv wants to merge 3 commits into
mainfrom
feat/ensembl-mygene-access

Conversation

@SkyeAv

@SkyeAv SkyeAv commented Jul 28, 2026

Copy link
Copy Markdown
Member

Adds an opt-in MyGene.info backend for the Ensembl pull. The existing BioMart harvest
(src/datahandlers/ensembl.py) queries every Ensembl species dataset (200+), which is slow,
flaky (config already skips a "stuck" dataset), and impractical on a machine that cannot run a long
network harvest. The new backend pulls only the well-covered model organisms from the MyGene.info
BioThings API — a registered Translator Knowledge Provider — via a scrolling query, with no bulk
download.

MyGene backend

  • New module: src/datahandlers/ensembl_mygene.py (pull_ensembl_via_mygene) pages MyGene with
    fetch_all/scroll_id and writes the same ENSEMBL/<dataset>/BioMart.tsv artifacts (BioMart
    display-header columns), so write_ensembl_gene_ids / write_ensembl_protein_ids work unchanged.
  • Opt-in wiring: selected by config.yaml ensembl_source (default biomart, unchanged) and
    ensembl_mygene_taxa; dispatched in the get_ensembl rule (src/snakefiles/datacollect.snakefile).
    Production behavior is untouched unless the default is flipped.
  • Coverage: human/mouse/rat/zebrafish/fly/worm/xenopus (verified live). Yeast (~2 genes) and dicty
    (0) are near-empty in MyGene and stay on BioMart; MyGene has no SGD field.

Correctness traps handled

  • str-or-list normalization: MyGene returns ensembl.protein/ensembl.gene as a scalar or a list,
    and ensembl itself can be a list of dicts (zebrafish) — proteins are kept scoped to their own
    gene entry, not pooled.
  • MGI double-prefix: MyGene returns MGI pre-prefixed (MGI:3704398) but the downstream consumer
    prepends MGI: itself; the handler strips a leading MGI: to avoid MGI:MGI:3704398.
  • Atomic writes: each dataset is written to BioMart.tsv.part then os.replaced onto
    BioMart.tsv only after the whole taxon is pulled, so a mid-pull failure can never leave a partial
    file that a later run mistakes for complete (a reviewer reproduced that data-loss path; it is
    regression-tested).

Design

  • Default unchanged on purpose: flipping ensembl_source changes gene/protein clique membership
    and should be validated with a source-impact report + clique diff on a cluster build first.
    Deferred: switching the default; a full species pull smoke-test on the cluster.
  • Testable offline: every network touch is behind an injectable http_get(url, params) -> dict,
    so the parsing/paging core runs against committed real fixtures (tests/data/ensembl_mygene/,
    captured verbatim, each named by its gene id).

Docs

  • Rewrites docs/sources/ENSEMBL/Download.md: documents both backends and the MyGene field
    mapping/coverage, corrects the attribute-limit description (the limit is on external-reference
    attributes), and adds the Ensembl release-116 / July-2026 legacy-platform retirement caveat.

Notes (not fixed here)

  • gene.build_gene_ensembl_relationships renders prefixes as Python sets ({'NCBIGene'}:1017),
    but this is dormant: gene/concords/ENSEMBL is not in config.yaml gene_concords, so
    gene_compendia never consumes it (the live Ensembl↔NCBIGene linkage comes from NCBIGeneENSEMBL /
    gene2ensembl.gz). Pinned by a test with invert instructions; worth its own issue if it should go live.

Testing

  • uv run pytest -m unit tests/datahandlers/test_ensembl_mygene.py -q38 passed, 2 deselected
    (includes a round-trip through the real write_ensembl_gene_ids / write_ensembl_protein_ids /
    build_gene_ensembl_relationships).
  • uv run pytest -m network --network tests/datahandlers/test_ensembl_mygene.py -q2 passed
    (live MyGene smoke tests, incl. a real fetch_allscroll_id handshake).
  • uv run pytest -m unit -q (full CI gate) → 440 passed.
  • uv run ruff check / uv run ruff format --check / uv run snakefmt --check / uv run rumdl check
    → all clean.
  • uv run snakemake -c1 get_ensembl -n → DAG builds (exit 0).

Questions for the reviewer

  • Default flip. Should ensembl_source: mygene ever become the default, or stay a dev/partial
    option (it cannot cover yeast/dicty)? I left it opt-in pending a cluster impact report.

AI-drafted: This PR was drafted by an AI coding agent (pi) at the maintainer's direction and is
pending human review. Verification commands above were run by the agent.

SkyeAv added 2 commits July 27, 2026 18:11
The BioMart harvest (src/datahandlers/ensembl.py) queries every Ensembl species
dataset (200+), which is slow, flaky, and impractical on a machine that cannot run
a long network harvest. Add src/datahandlers/ensembl_mygene.py, which pulls only the
well-covered model organisms from the MyGene.info BioThings API (a registered
Translator KP) via a scrolling query - no bulk download.

It writes the same ENSEMBL/<dataset>/BioMart.tsv artifacts (BioMart display-header
columns) so write_ensembl_gene_ids / write_ensembl_protein_ids work unchanged.
Handles MyGene's str-or-list fields, the dict-or-list-of-dicts ensembl shape
(proteins scoped per entry), and strips the MGI: prefix MyGene adds (avoiding a
MGI:MGI:... double prefix). Each dataset is written atomically (.part + os.replace)
so a mid-pull failure can never poison a later run.

Selected by config ensembl_source (default biomart, unchanged) + ensembl_mygene_taxa.
Yeast/dicty stay on BioMart (MyGene coverage is near-zero). 38 offline unit tests
(incl. a round-trip through the real consumers) over committed real fixtures, plus 2
network smoke tests.
Document both backends and the MyGene field mapping/coverage, correct the
attribute-limit description (the limit is on external-reference attributes), add the
Ensembl release-116 / July-2026 legacy-platform retirement caveat, and record two
findings: the MGI double-prefix trap, and that build_gene_ensembl_relationships
renders prefixes as Python sets but is dormant (gene/concords/ENSEMBL is not in
config gene_concords, so it is never consumed).
@SkyeAv SkyeAv self-assigned this Jul 28, 2026
@SkyeAv SkyeAv added documentation Improvements or additions to documentation enhancement New feature or request Priority: Medium Data Source: Ensembl discussion needed New mappings testing Related to the test suite or testing infrastructure labels Jul 28, 2026
astral-sh/ruff-action@v3 floats to the latest ruff, now 0.16.0 (pyproject
pins only a floor, >=0.14.14). Ruff 0.16.0 formats Python code fences inside
*.md, which overlaps with the dedicated rumdl Markdown check and was failing
'Check Python formatting with ruff' on every open PR. Exclude *.md from ruff
format so ruff stays scoped to Python and Markdown formatting CI is
deterministic across ruff releases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Data Source: Ensembl discussion needed documentation Improvements or additions to documentation enhancement New feature or request New mappings Priority: Medium testing Related to the test suite or testing infrastructure

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant