varcode 9.2.7__tar.gz → 9.2.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {varcode-9.2.7/varcode.egg-info → varcode-9.2.8}/PKG-INFO +1 -1
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_documentation_examples.py +13 -0
- varcode-9.2.8/tests/test_fusion_partner_candidates.py +184 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_nonlocal_sv_transcripts.py +1 -1
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_classes.py +6 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/structural.py +44 -28
- varcode-9.2.8/varcode/version.py +1 -0
- {varcode-9.2.7 → varcode-9.2.8/varcode.egg-info}/PKG-INFO +1 -1
- {varcode-9.2.7 → varcode-9.2.8}/varcode.egg-info/SOURCES.txt +1 -0
- varcode-9.2.7/varcode/version.py +0 -1
- {varcode-9.2.7 → varcode-9.2.8}/LICENSE +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/MANIFEST.in +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/README.md +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/pyproject.toml +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/requirements.txt +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/setup.cfg +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_annotator_divergence_scenarios.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_annotator_parity_adversarial.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_annotators.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_bnd_retained_segments.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_cli_effects.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_cli_genes.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_cli_without_pkg_resources.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_collection_filtering.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_collection_variants_attr_consistency.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_common.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_cosmic_mutations.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_cryptic_exons.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_csv_roundtrip.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_dbnsfp_validation.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_default_structural_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_annotation_errors.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_candidates.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_classes.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_collection.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_collection_serialization.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_collection_sort_order.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effect_hypotheses.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_effects_from_mutagenix_variants.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_exacto_fusions.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_exonic_splice_site.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_fast_path.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_frameshift_cterminus_regression.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_frameshift_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_genome_contig_validation.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_genome_sequence.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_genomic_layout.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_genotype.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_genotype_from_vcf.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_germline_context.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_germline_effects.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_haplotype_effect.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_maf.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_mitochondrial.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_mm10_klf6_frameshift.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_mouse.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_mutant_transcript.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_mutate.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_no_duplicate_variants.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_osteosarc_fusions.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_partial_annotators.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_premature_stop_short_description.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_problematic_variants.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_protein_diff_parity.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_read_phase_resolver.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_real_world_callers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_reference.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_reference_mismatch_error.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_reference_segments.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_rna_evidence.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_rna_haplotype_sequences.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_rna_read_phasing_source.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_selenocysteine.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_silent_aa_pos.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_silent_hgvs_description.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_splice_graph.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_splice_outcomes.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_splice_site_effects.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_stop_codon_classification_bugs.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_string_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_structural_variant_annotator.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_structural_variants.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_sv_tabular_metadata.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_symbolic_alleles.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_symbolic_sv_padding.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_timings.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_transcript_layout.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_transcript_model.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_transforms_left_align.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_transforms_pair_breakends.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_variant.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_variant_collection.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_vcf.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_vcf_output.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_vcf_parsing.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_vcf_phase_resolver.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/tests/test_vcf_spec_examples.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/__init__.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/annotators/__init__.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/annotators/fast.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/annotators/protein_diff.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/annotators/registry.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/__init__.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/effects_script.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/genes_script.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/logging.conf +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/variant_args.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cli/version_info.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/common.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/cryptic_exons.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/csv_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effect_candidates.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effect_hypotheses.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/__init__.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/classify.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/codon_tables.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/common.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_collection.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_ordering.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_prediction.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_prediction_coding.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_prediction_coding_frameshift.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/effect_prediction_coding_in_frame.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/fast_path.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/mutate.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/transcript_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/effects/translate.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/errors.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/exacto.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/genome.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/genome_sequence.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/genomic_layout.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/genotype.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/germline.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/maf.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/mutant_transcript.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/nucleotides.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/phasing.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/realized_effects.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/reference.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/rna_evidence.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/rna_read_phasing.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/splice_graph.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/splice_outcomes.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/string_helpers.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/structural_variant.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/sv_allele_parser.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/transcript_layout.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/transcript_model.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/transforms/__init__.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/ucsc_reference_names.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/util.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/variant.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/variant_collection.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/vcf.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/vcf_output.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode/vcf_parsing.py +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode.egg-info/dependency_links.txt +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode.egg-info/entry_points.txt +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode.egg-info/requires.txt +0 -0
- {varcode-9.2.7 → varcode-9.2.8}/varcode.egg-info/top_level.txt +0 -0
|
@@ -103,6 +103,19 @@ def test_documentation_germline_with_vcf_phase(input_directory):
|
|
|
103
103
|
assert cftr[0].short_description == "p.S159T"
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def test_documentation_fusion_protein_candidates(capsys):
|
|
107
|
+
genome = cached_release(95)
|
|
108
|
+
transcript = genome.transcript_by_id("ENST00000538197")
|
|
109
|
+
variant = StructuralVariant(
|
|
110
|
+
"4", 15012987, "BND", alt="N[4:2664478[",
|
|
111
|
+
mate_contig="4", mate_start=2664478, genome=genome)
|
|
112
|
+
namespace = {"effect": variant.effect_on_transcript(transcript)}
|
|
113
|
+
_run(_blocks("docs/structural_variants.md", "## Fusion protein candidates")[:1], namespace)
|
|
114
|
+
output = capsys.readouterr().out
|
|
115
|
+
assert "ENST00000324666" in output
|
|
116
|
+
assert "ENST00000637812" in output
|
|
117
|
+
|
|
118
|
+
|
|
106
119
|
def test_documentation_effect_families_keep_all_classes():
|
|
107
120
|
source = ast.parse((ROOT / "varcode/effects/effect_classes.py").read_text())
|
|
108
121
|
classes = {node.name for node in source.body if isinstance(node, ast.ClassDef)}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Fusion proteins must not depend on discarding all but the first isoform."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from pyensembl import cached_release
|
|
7
|
+
|
|
8
|
+
from varcode import EffectCandidate, StructuralVariant
|
|
9
|
+
from varcode.effects import GeneFusion, LargeDeletion, TranslocationToIntergenic
|
|
10
|
+
from varcode.effects import structural
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def fusion_case(monkeypatch):
|
|
15
|
+
genome = cached_release(95)
|
|
16
|
+
transcript = genome.transcript_by_id("ENST00000538197") # CPEB2, forward
|
|
17
|
+
variant = StructuralVariant(
|
|
18
|
+
"4", 15012987, "BND", alt="N[4:2664478[",
|
|
19
|
+
mate_contig="4", mate_start=2664478, genome=genome)
|
|
20
|
+
# These tests isolate junction/isoform enumeration. Dedicated existing
|
|
21
|
+
# tests cover the splice and cryptic layers added after it.
|
|
22
|
+
monkeypatch.setattr(structural, "_enumerate_and_attach_cryptics", lambda *args: None)
|
|
23
|
+
monkeypatch.setattr(structural, "_enumerate_and_attach_splice_outcomes", lambda *args: None)
|
|
24
|
+
return genome, transcript, variant
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _fusions(effect):
|
|
28
|
+
return [candidate.effect for candidate in effect.candidates
|
|
29
|
+
if isinstance(candidate.effect, GeneFusion)]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_audited_cpeb2_fam193a_proteins_are_both_candidates(fusion_case):
|
|
33
|
+
genome, transcript, variant = fusion_case
|
|
34
|
+
effect = variant.effect_on_transcript(transcript)
|
|
35
|
+
candidates = _fusions(effect)
|
|
36
|
+
expected = [t.id for t in genome.transcripts_at_locus("4", 2664478, 2664478)
|
|
37
|
+
if t.is_protein_coding and t.gene_name == "FAM193A" and t.strand == "+"]
|
|
38
|
+
assert [c.partner_transcript.id for c in candidates] == expected
|
|
39
|
+
assert len(candidates) == 7
|
|
40
|
+
assert candidates[0] is effect # existing primary remains compatible
|
|
41
|
+
proteins = {c.partner_transcript.id: c.mutant_protein_sequence for c in candidates}
|
|
42
|
+
assert len(proteins["ENST00000324666"]) == 1541
|
|
43
|
+
assert len(proteins["ENST00000637812"]) == 1500
|
|
44
|
+
assert proteins["ENST00000324666"] != proteins["ENST00000637812"]
|
|
45
|
+
partner = genome.transcript_by_id("ENST00000637812")
|
|
46
|
+
assert proteins[partner.id] == transcript.protein_sequence[:678] + partner.protein_sequence[693:]
|
|
47
|
+
for candidate in candidates:
|
|
48
|
+
assert candidate.transcript is transcript
|
|
49
|
+
assert candidate.five_prime_transcript is transcript
|
|
50
|
+
assert candidate.three_prime_transcript is candidate.partner_transcript
|
|
51
|
+
model = candidate.mutant_transcript
|
|
52
|
+
assert model.cdna_sequence == "".join(
|
|
53
|
+
segment.source.sequence[segment.start:segment.end]
|
|
54
|
+
for segment in model.reference_segments)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_same_transcript_pair_has_same_protein_from_either_end(fusion_case):
|
|
58
|
+
genome, transcript, variant = fusion_case
|
|
59
|
+
partner = genome.transcript_by_id("ENST00000637812")
|
|
60
|
+
mate = StructuralVariant(
|
|
61
|
+
"4", 2664478, "BND", alt="]4:15012987]N",
|
|
62
|
+
mate_contig="4", mate_start=15012987, genome=genome)
|
|
63
|
+
from_five = next(c for c in _fusions(variant.effect_on_transcript(transcript))
|
|
64
|
+
if c.partner_transcript.id == partner.id)
|
|
65
|
+
from_three = next(c for c in _fusions(mate.effect_on_transcript(partner))
|
|
66
|
+
if c.partner_transcript.id == transcript.id)
|
|
67
|
+
assert from_five.mutant_protein_sequence == from_three.mutant_protein_sequence
|
|
68
|
+
assert from_three.five_prime_transcript.id == transcript.id
|
|
69
|
+
assert from_three.three_prime_transcript.id == partner.id
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _Isoform:
|
|
73
|
+
"""Reuse a reference transcript while varying only ID or available sequence."""
|
|
74
|
+
def __init__(self, transcript, name, sequence):
|
|
75
|
+
self._transcript = transcript
|
|
76
|
+
self.id = name
|
|
77
|
+
self.sequence = sequence
|
|
78
|
+
|
|
79
|
+
def __getattr__(self, name):
|
|
80
|
+
return getattr(self._transcript, name)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_large_isoform_set_is_not_capped_or_merged_by_protein(fusion_case, monkeypatch):
|
|
84
|
+
genome, transcript, variant = fusion_case
|
|
85
|
+
partner = genome.transcript_by_id("ENST00000637812")
|
|
86
|
+
partners = [_Isoform(partner, "isoform_%03d" % i, partner.sequence) for i in range(257)]
|
|
87
|
+
monkeypatch.setattr(structural, "_coding_transcripts_at", lambda *args: partners)
|
|
88
|
+
candidates = _fusions(variant.effect_on_transcript(transcript))
|
|
89
|
+
assert [c.partner_transcript.id for c in candidates] == [p.id for p in partners]
|
|
90
|
+
assert len({c.mutant_protein_sequence for c in candidates}) == 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_duplicate_junction_and_partner_records_are_not_repeated(fusion_case, monkeypatch):
|
|
94
|
+
genome, transcript, variant = fusion_case
|
|
95
|
+
partner = genome.transcript_by_id("ENST00000637812")
|
|
96
|
+
monkeypatch.setattr(structural, "_coding_transcripts_at", lambda *args: [partner, partner])
|
|
97
|
+
duplicate = SimpleNamespace(junctions=variant.junctions * 2)
|
|
98
|
+
effect = structural._fusion_across_junction(duplicate, transcript, None)
|
|
99
|
+
assert len(_fusions(effect)) == 1
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@pytest.mark.parametrize("sequence", [None, ""])
|
|
103
|
+
def test_missing_partner_sequence_keeps_an_unresolved_candidate(fusion_case, monkeypatch, sequence):
|
|
104
|
+
genome, transcript, variant = fusion_case
|
|
105
|
+
partner = genome.transcript_by_id("ENST00000637812")
|
|
106
|
+
missing = _Isoform(partner, "missing_sequence", sequence)
|
|
107
|
+
monkeypatch.setattr(structural, "_coding_transcripts_at", lambda *args: [partner, missing])
|
|
108
|
+
candidates = _fusions(variant.effect_on_transcript(transcript))
|
|
109
|
+
assert len(candidates) == 2
|
|
110
|
+
assert candidates[0].mutant_protein_sequence is not None
|
|
111
|
+
assert candidates[1].partner_transcript is missing
|
|
112
|
+
assert candidates[1].mutant_transcript is None
|
|
113
|
+
assert candidates[1].mutant_protein_sequence is None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_supplied_assembly_and_external_candidates_are_preserved(fusion_case):
|
|
117
|
+
genome, transcript, variant = fusion_case
|
|
118
|
+
variant.alt_assembly = "ATG" + "GCC" * 20 + "TAA"
|
|
119
|
+
effect = variant.effect_on_transcript(transcript)
|
|
120
|
+
candidates = _fusions(effect)
|
|
121
|
+
assert len(candidates) == 7
|
|
122
|
+
assert all(c.mutant_transcript.cdna_sequence == variant.alt_assembly for c in candidates)
|
|
123
|
+
assert all(c.mutant_transcript.evidence == {"source": "alt_assembly"} for c in candidates)
|
|
124
|
+
observed = EffectCandidate(candidates[-1], source="rna", evidence={"read_count": 4})
|
|
125
|
+
effect._extra_candidates = (observed,)
|
|
126
|
+
assert effect.candidates[-1] is observed
|
|
127
|
+
assert len(effect.candidates) == 8
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_opposite_strand_partner_isoforms_are_all_kept(monkeypatch):
|
|
131
|
+
genome = cached_release(81)
|
|
132
|
+
transcript = genome.transcript_by_id("ENST00000003084") # CFTR, +
|
|
133
|
+
variant = StructuralVariant(
|
|
134
|
+
"7", 117485000, "BND", alt="N]17:43120000]",
|
|
135
|
+
mate_contig="17", mate_start=43120000, genome=genome)
|
|
136
|
+
monkeypatch.setattr(structural, "_enumerate_and_attach_cryptics", lambda *args: None)
|
|
137
|
+
effect = variant.effect_on_transcript(transcript)
|
|
138
|
+
expected = {t.id for t in genome.transcripts_at_locus("17", 43120000, 43120000)
|
|
139
|
+
if t.is_protein_coding and t.strand == "-"}
|
|
140
|
+
assert {c.partner_transcript.id for c in _fusions(effect)} == expected
|
|
141
|
+
assert len(expected) > 1
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_same_role_join_still_has_no_fusion_candidates(fusion_case):
|
|
145
|
+
genome, transcript, _ = fusion_case
|
|
146
|
+
variant = StructuralVariant(
|
|
147
|
+
"4", 15012987, "BND", alt="N]4:2664478]",
|
|
148
|
+
mate_contig="4", mate_start=2664478, genome=genome)
|
|
149
|
+
effect = variant.effect_on_transcript(transcript)
|
|
150
|
+
assert isinstance(effect, TranslocationToIntergenic)
|
|
151
|
+
assert not _fusions(effect)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_span_effect_is_retained_after_all_fusion_partners(fusion_case):
|
|
155
|
+
genome, _, _ = fusion_case
|
|
156
|
+
tmprss2 = max((t for t in genome.genes_by_name("TMPRSS2")[0].transcripts if t.is_protein_coding),
|
|
157
|
+
key=lambda t: len(t.sequence or ""))
|
|
158
|
+
erg = max((t for t in genome.genes_by_name("ERG")[0].transcripts if t.is_protein_coding),
|
|
159
|
+
key=lambda t: len(t.sequence or ""))
|
|
160
|
+
def midpoint(transcript, exon):
|
|
161
|
+
left, right = transcript.exons[exon - 1:exon + 1]
|
|
162
|
+
return (min(left.end, right.end) + max(left.start, right.start)) // 2
|
|
163
|
+
variant = StructuralVariant("21", midpoint(erg, 3), "DEL",
|
|
164
|
+
end=midpoint(tmprss2, 2) - 1, genome=genome)
|
|
165
|
+
effect = variant.effect_on_transcript(tmprss2)
|
|
166
|
+
assert len(_fusions(effect)) > 1
|
|
167
|
+
assert isinstance(effect.candidates[-1].effect, LargeDeletion)
|
|
168
|
+
assert effect.candidates[-1].effect.affected_exons
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_inversion_keeps_both_junction_directions_with_local_five_prime_first(fusion_case):
|
|
172
|
+
genome, _, _ = fusion_case
|
|
173
|
+
transcript = genome.transcript_by_id("ENST00000003084") # CFTR, +
|
|
174
|
+
partner_gene = genome.genes_by_name("CTTNBP2")[0] # downstream, -
|
|
175
|
+
far = (max(partner_gene.start, transcript.gene.end + 1) + partner_gene.end) // 2
|
|
176
|
+
variant = StructuralVariant("7", 117485000, "INV", end=far, genome=genome)
|
|
177
|
+
effect = variant.effect_on_transcript(transcript)
|
|
178
|
+
candidates = _fusions(effect)
|
|
179
|
+
assert effect.five_prime_transcript.id == transcript.id
|
|
180
|
+
directions = {(c.five_prime_transcript.gene_name, c.three_prime_transcript.gene_name)
|
|
181
|
+
for c in candidates}
|
|
182
|
+
assert directions == {("CFTR", "CTTNBP2"), ("CTTNBP2", "CFTR")}
|
|
183
|
+
keys = [(c.five_prime_transcript.id, c.three_prime_transcript.id) for c in candidates]
|
|
184
|
+
assert len(keys) == len(set(keys))
|
|
@@ -16,7 +16,7 @@ def transcript(request):
|
|
|
16
16
|
@pytest.fixture(autouse=True)
|
|
17
17
|
def no_optional_candidates(monkeypatch):
|
|
18
18
|
# Isolate the span fallback; fusion preservation also has real esvee tests.
|
|
19
|
-
monkeypatch.setattr(structural, "
|
|
19
|
+
monkeypatch.setattr(structural, "_fusion_partners", lambda *args: ())
|
|
20
20
|
monkeypatch.setattr(structural, "_enumerate_and_attach_cryptics", lambda *args: None)
|
|
21
21
|
monkeypatch.setattr(structural, "_enumerate_and_attach_splice_outcomes", lambda *args: None)
|
|
22
22
|
|
|
@@ -1549,6 +1549,12 @@ class GeneFusion(StructuralVariantEffect):
|
|
|
1549
1549
|
produce it, and so do deletions, duplications and inversions with
|
|
1550
1550
|
one end in each gene.
|
|
1551
1551
|
|
|
1552
|
+
The default annotator retains every compatible annotated partner isoform
|
|
1553
|
+
and junction in :attr:`candidates`, with no count cap. The first is the
|
|
1554
|
+
historical primary; producer order is not a probability ranking. Distinct
|
|
1555
|
+
transcript pairs remain separate even when their proteins match. Missing
|
|
1556
|
+
sequence leaves that candidate's transcript/protein unresolved.
|
|
1557
|
+
|
|
1552
1558
|
``transcript`` is the transcript being annotated and
|
|
1553
1559
|
``partner_transcript`` the other one; ``five_prime_transcript``
|
|
1554
1560
|
and ``three_prime_transcript`` say which is which (by default
|
|
@@ -473,8 +473,14 @@ def _build_fusion_mutant_transcript(
|
|
|
473
473
|
segments. The annotator assigns the 5' / 3' roles from breakend
|
|
474
474
|
orientation and strand before calling this.
|
|
475
475
|
"""
|
|
476
|
-
five_prime_cdna =
|
|
477
|
-
three_prime_cdna =
|
|
476
|
+
five_prime_cdna = five_prime_transcript.sequence
|
|
477
|
+
three_prime_cdna = three_prime_transcript.sequence
|
|
478
|
+
if not five_prime_cdna or not three_prime_cdna:
|
|
479
|
+
# The partner is still a candidate when sequence is unavailable.
|
|
480
|
+
# In particular, str(None) is not an observed or predicted cDNA.
|
|
481
|
+
return None
|
|
482
|
+
five_prime_cdna = str(five_prime_cdna)
|
|
483
|
+
three_prime_cdna = str(three_prime_cdna)
|
|
478
484
|
five_prime_end = _cdna_cut(
|
|
479
485
|
five_prime_transcript, five_prime_position, True)
|
|
480
486
|
three_prime_start = _cdna_cut(
|
|
@@ -798,8 +804,11 @@ def _intronic_or_intergenic(variant, transcript):
|
|
|
798
804
|
|
|
799
805
|
|
|
800
806
|
def _fusion_across_junction(variant, transcript, assembly):
|
|
801
|
-
"""
|
|
802
|
-
|
|
807
|
+
"""Fusion candidates across all compatible junctions and partner isoforms.
|
|
808
|
+
|
|
809
|
+
Return the first :class:`GeneFusion`, carrying all other fusions in its
|
|
810
|
+
primary candidates, or ``None``. Annotation order is not a likelihood
|
|
811
|
+
ranking; no compatible partner is discarded by a candidate-count limit.
|
|
803
812
|
|
|
804
813
|
A junction end belongs to this transcript when the transcript
|
|
805
814
|
spans it and the transcript's *gene* doesn't span the other
|
|
@@ -827,34 +836,42 @@ def _fusion_across_junction(variant, transcript, assembly):
|
|
|
827
836
|
continue
|
|
828
837
|
ends.append((near, far))
|
|
829
838
|
|
|
839
|
+
fusions = []
|
|
840
|
+
seen = set()
|
|
830
841
|
for near, far in sorted(
|
|
831
842
|
ends, key=lambda pair: not keeps_five_prime(pair[0])):
|
|
832
843
|
five_prime = keeps_five_prime(near)
|
|
833
|
-
partner
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
844
|
+
for partner in _fusion_partners(
|
|
845
|
+
variant, transcript, near, far, five_prime):
|
|
846
|
+
key = (near, far, partner.id)
|
|
847
|
+
if key in seen:
|
|
848
|
+
continue
|
|
849
|
+
seen.add(key)
|
|
850
|
+
if five_prime:
|
|
851
|
+
five, five_position = transcript, near.position
|
|
852
|
+
three, three_position = partner, far.position
|
|
853
|
+
else:
|
|
854
|
+
five, five_position = partner, far.position
|
|
855
|
+
three, three_position = transcript, near.position
|
|
856
|
+
fusions.append(GeneFusion(
|
|
857
|
+
variant=variant,
|
|
858
|
+
transcript=transcript,
|
|
859
|
+
partner_transcript=partner,
|
|
860
|
+
five_prime_transcript=five,
|
|
861
|
+
three_prime_transcript=three,
|
|
862
|
+
mutant_transcript=assembly or _build_fusion_mutant_transcript(
|
|
863
|
+
transcript, five, five_position, three, three_position)))
|
|
864
|
+
if not fusions:
|
|
865
|
+
return None
|
|
866
|
+
primary = fusions[0]
|
|
867
|
+
primary._attach_primary_effects(fusions[1:])
|
|
868
|
+
return primary
|
|
852
869
|
|
|
853
870
|
|
|
854
|
-
def
|
|
871
|
+
def _fusion_partners(
|
|
855
872
|
variant, transcript, near, far, transcript_is_five_prime):
|
|
856
|
-
"""
|
|
857
|
-
``transcript``,
|
|
873
|
+
"""Yield every protein-coding transcript at ``far`` that fuses with
|
|
874
|
+
``transcript``, in annotation order.
|
|
858
875
|
|
|
859
876
|
A partner is in another gene, keeps the end opposite to
|
|
860
877
|
``transcript``'s, and — like ``transcript`` — belongs to a gene
|
|
@@ -870,8 +887,7 @@ def _fusion_partner(
|
|
|
870
887
|
if far.keeps is None or (
|
|
871
888
|
_retains_five_prime_end(candidate, far.keeps)
|
|
872
889
|
!= transcript_is_five_prime):
|
|
873
|
-
|
|
874
|
-
return None
|
|
890
|
+
yield candidate
|
|
875
891
|
|
|
876
892
|
|
|
877
893
|
def _coding_transcripts_at(variant, contig, position):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "9.2.8"
|
|
@@ -32,6 +32,7 @@ tests/test_exonic_splice_site.py
|
|
|
32
32
|
tests/test_fast_path.py
|
|
33
33
|
tests/test_frameshift_cterminus_regression.py
|
|
34
34
|
tests/test_frameshift_helpers.py
|
|
35
|
+
tests/test_fusion_partner_candidates.py
|
|
35
36
|
tests/test_genome_contig_validation.py
|
|
36
37
|
tests/test_genome_sequence.py
|
|
37
38
|
tests/test_genomic_layout.py
|
varcode-9.2.7/varcode/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "9.2.7"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|