Resolve gene symbols to genomic regions¶
resolve_gene_regions is the canonical entry point for any analysis that
starts from a list of genes and needs the corresponding genomic regions to
query for variants. It accepts a GeneQuery (genes by symbol, file, or
inheritance filter), resolves each gene against a GeneRepository, and
returns per-gene regions with splice buffers applied.
Minimal example¶
from pathlib import Path
from iris.adapters.driven.genomic_analysis.biocpy import BiocpyRangeOperations
from iris.adapters.driven.readers.providers.gencode import GencodeGeneRepository
from iris.applications.genomics.use_cases import resolve_gene_regions
from iris.domain.genomics.objects import GeneQuery
from iris.domain.genomics.services import GenomicRangeService
repo = GencodeGeneRepository(
"/data/gencode.v49.annotation.gff3.gz",
assembly="GRCh38",
tags={"MANE_Select", "Ensembl_canonical"},
)
service = GenomicRangeService(ops=BiocpyRangeOperations())
result = resolve_gene_regions(
GeneQuery(symbols=("BRCA1", "CFTR", "HEXA")),
gene_repository=repo,
range_service=service,
splice_buffer_bp=8,
)
# Flat list of GenomicPosition — pass directly to a pipeline:
regions = result.all_regions()
# Gene cache keyed by symbol and Ensembl ID — for VepFlightAnnotator:
gene_cache = result.gene_cache()
Reading symbols from a file¶
One gene symbol per line; lines starting with # and blank lines are ignored.
result = resolve_gene_regions(
GeneQuery.from_file(Path("recgenes.txt")),
gene_repository=repo,
range_service=service,
)
You can combine a file with extra explicit symbols — duplicates are removed, file symbols come first:
Filtering by inheritance mode¶
GeneQuery.recessive() and GeneQuery.dominant() are convenience constructors
that set inheritance_modes to AR and AD respectively.
# All autosomal-recessive genes in the repository:
result = resolve_gene_regions(
GeneQuery.recessive(),
gene_repository=repo,
range_service=service,
)
# AR genes from a curated panel:
result = resolve_gene_regions(
GeneQuery.recessive(symbols_file=Path("panel.txt")),
gene_repository=repo,
range_service=service,
)
Note
inheritance_modes filtering requires GeneAnnotation.diseases to carry
MendelianInheritance data from a rich source (OMIM, Orphanet). Until
that data is available, the filter is a no-op.
Filtering by disease name¶
disease_names is a case-insensitive substring filter applied against all
disease names and aliases in GeneAnnotation.diseases:
result = resolve_gene_regions(
GeneQuery(symbols=("CFTR", "BRCA1"), disease_names=("fibrosis", "breast cancer")),
gene_repository=repo,
range_service=service,
)
Genes with no diseases are excluded when disease_names is set.
Skipping the splice buffer¶
Pass splice_buffer_bp=0 to use raw exon coordinates. range_service is
not required when the buffer is zero:
result = resolve_gene_regions(
GeneQuery(symbols=("TP53",)),
gene_repository=repo,
splice_buffer_bp=0,
)
Inspecting results¶
for rg in result.resolved:
method = rg.resolution_method # "transcript" | "gene_body"
ts = rg.transcript_used # TranscriptStructure | None
print(f"{rg.gene.symbol}: {len(rg.regions)} region(s) via {method}")
if ts:
print(f" transcript: {ts.name} (MANE={ts.is_mane_select})")
if result.unresolved_symbols:
print("Unresolved:", result.unresolved_symbols)
Resolution strategy¶
For each gene symbol, the use case applies the following strategy in order:
- Direct lookup —
gene_repository.get_annotation(symbol). - HGNC alias fallback (only when
nomenclature_resolveris supplied) — resolves the input symbol to its approved HGNC name and retries the lookup. Useful for clinical gene lists that use aliases ("MLL"→"KMT2A"). - Transcript selection — MANE Select > Ensembl canonical > first available; exons of the chosen transcript with splice buffer applied.
- Gene body fallback — if no transcript has exons, the full gene span is used as a single region (no splice buffer applied).
Genes that cannot be resolved by any strategy are collected in
result.unresolved_symbols.
Reference¶
iris.applications.genomics.use_cases.resolve_gene_regions
¶
Use case: resolve gene symbols to genomic query regions via a GeneRepository.
This is the canonical entry point for any analysis that starts from a list of genes (by name, by inheritance mode, or by disease) and needs the corresponding genomic regions to query for variants.
The use case is intentionally free of infrastructure
- It receives a
GeneRepositoryport, not aGencodeGeneRepository. - It receives a
GenomicRangeServicefor the splice buffer operation. - Any caller — CLI command, API endpoint, notebook, or pipeline script — uses the same function with different concrete dependencies injected.
Example
from iris.adapters.driven.genomic_analysis.biocpy import BiocpyRangeOperations
from iris.adapters.driven.readers.providers.gencode import GencodeGeneRepository
from iris.applications.genomics.use_cases.resolve_gene_regions import resolve_gene_regions
from iris.domain.genomics.objects import GeneQuery
from iris.domain.genomics.services import GenomicRangeService
repo = GencodeGeneRepository(
"/data/gencode.v49.annotation.gff3.gz", assembly="GRCh38", tags={"MANE_Select", "Ensembl_canonical"}
)
service = GenomicRangeService(ops=BiocpyRangeOperations())
result = resolve_gene_regions(
GeneQuery.from_file(Path("genes.txt")), gene_repository=repo, range_service=service, splice_buffer_bp=8
)
# Flat list of GenomicPosition for VariantExtractionPipeline:
regions = result.all_regions()
# Gene cache for VepFlightAnnotator:
gene_cache = result.gene_cache()
resolve_gene_regions
¶
resolve_gene_regions(
query,
feature_region,
*,
gene_repository,
range_service=None,
splice_buffer_bp=2,
nomenclature_resolver=None,
constraint_store=None,
log_every=500,
)
Resolve a GeneQuery to per-gene genomic regions ready for variant extraction.
Resolution strategy per gene (first success wins):
1. Direct lookup: gene_repository.get_annotation(symbol).
2. HGNC alias fallback (only when nomenclature_resolver is supplied):
resolve the input symbol to its approved HGNC symbol via
nomenclature_resolver.resolve_symbol(), then retry the
repository lookup with the canonical name. Handles aliases,
previous symbols, and capitalisation differences common in curated
clinical gene lists (e.g. "MLL" → "KMT2A").
3. Transcript selection: MANE Select > Ensembl canonical > first available;
exons of the chosen transcript with splice buffer applied.
4. Gene body fallback: if no transcript has exons, the full gene
genomic span is used as a single region (no splice buffer).
Post-load filters from query (inheritance_modes, disease_names)
are applied after fetching annotations, so they work regardless of
whether the backend supports native filtering.
| PARAMETER | DESCRIPTION |
|---|---|
query
|
Specification of which genes to resolve and how.
TYPE:
|
gene_repository
|
Any GeneRepository implementation (e.g. GencodeGeneRepository).
TYPE:
|
range_service
|
GenomicRangeService for the splice buffer operation.
Required when
TYPE:
|
splice_buffer_bp
|
Bases to extend each exon into flanking introns to capture splice-donor/acceptor variants. Default 2. 0 = no buffer.
TYPE:
|
nomenclature_resolver
|
Optional GeneNomenclatureResolver (e.g.
TYPE:
|
constraint_store
|
Optional GeneConstraintStore (e.g.
TYPE:
|
log_every
|
Log a progress message every N genes resolved.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ResolvedGeneRegions
|
ResolvedGeneRegions with one ResolvedGeneRegion per resolved gene |
ResolvedGeneRegions
|
and a tuple of symbols that could not be resolved. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/iris/applications/genomics/use_cases/resolve_gene_regions.py
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | |