varcode 9.2.2__tar.gz → 9.2.4__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.2/varcode.egg-info → varcode-9.2.4}/PKG-INFO +1 -1
- varcode-9.2.4/tests/test_bnd_retained_segments.py +111 -0
- varcode-9.2.4/tests/test_nonlocal_sv_transcripts.py +83 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_osteosarc_fusions.py +9 -1
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_symbolic_sv_padding.py +1 -1
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/structural.py +24 -12
- {varcode-9.2.2 → varcode-9.2.4}/varcode/mutant_transcript.py +4 -1
- {varcode-9.2.2 → varcode-9.2.4}/varcode/sv_allele_parser.py +11 -0
- varcode-9.2.4/varcode/version.py +1 -0
- {varcode-9.2.2 → varcode-9.2.4/varcode.egg-info}/PKG-INFO +1 -1
- {varcode-9.2.2 → varcode-9.2.4}/varcode.egg-info/SOURCES.txt +2 -0
- varcode-9.2.2/varcode/version.py +0 -1
- {varcode-9.2.2 → varcode-9.2.4}/LICENSE +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/MANIFEST.in +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/README.md +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/pyproject.toml +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/requirements.txt +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/setup.cfg +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_annotator_divergence_scenarios.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_annotator_parity_adversarial.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_annotators.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_cli_effects.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_cli_genes.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_cli_without_pkg_resources.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_collection_filtering.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_collection_variants_attr_consistency.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_common.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_cosmic_mutations.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_cryptic_exons.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_csv_roundtrip.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_dbnsfp_validation.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_default_structural_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_annotation_errors.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_candidates.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_classes.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_collection.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_collection_serialization.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_collection_sort_order.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effect_hypotheses.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_effects_from_mutagenix_variants.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_exacto_fusions.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_exonic_splice_site.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_fast_path.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_frameshift_cterminus_regression.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_frameshift_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_genome_contig_validation.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_genome_sequence.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_genomic_layout.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_genotype.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_genotype_from_vcf.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_germline_context.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_germline_effects.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_haplotype_effect.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_maf.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_mitochondrial.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_mm10_klf6_frameshift.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_mouse.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_mutant_transcript.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_mutate.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_no_duplicate_variants.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_partial_annotators.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_premature_stop_short_description.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_problematic_variants.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_protein_diff_parity.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_read_phase_resolver.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_real_world_callers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_reference.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_reference_mismatch_error.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_reference_segments.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_rna_evidence.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_rna_haplotype_sequences.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_rna_read_phasing_source.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_selenocysteine.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_silent_aa_pos.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_silent_hgvs_description.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_splice_graph.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_splice_outcomes.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_splice_site_effects.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_stop_codon_classification_bugs.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_string_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_structural_variant_annotator.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_structural_variants.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_sv_tabular_metadata.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_symbolic_alleles.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_timings.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_transcript_layout.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_transcript_model.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_transforms_left_align.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_transforms_pair_breakends.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_variant.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_variant_collection.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_vcf.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_vcf_output.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_vcf_parsing.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_vcf_phase_resolver.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/tests/test_vcf_spec_examples.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/__init__.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/annotators/__init__.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/annotators/fast.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/annotators/protein_diff.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/annotators/registry.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/__init__.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/effects_script.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/genes_script.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/logging.conf +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/variant_args.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cli/version_info.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/common.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/cryptic_exons.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/csv_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effect_candidates.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effect_hypotheses.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/__init__.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/classify.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/codon_tables.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/common.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_classes.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_collection.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_ordering.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_prediction.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_prediction_coding.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_prediction_coding_frameshift.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/effect_prediction_coding_in_frame.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/fast_path.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/mutate.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/transcript_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/effects/translate.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/errors.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/exacto.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/genome.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/genome_sequence.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/genomic_layout.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/genotype.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/germline.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/maf.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/nucleotides.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/phasing.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/realized_effects.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/reference.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/rna_evidence.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/rna_read_phasing.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/splice_graph.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/splice_outcomes.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/string_helpers.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/structural_variant.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/transcript_layout.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/transcript_model.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/transforms/__init__.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/ucsc_reference_names.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/util.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/variant.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/variant_collection.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/vcf.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/vcf_output.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode/vcf_parsing.py +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode.egg-info/dependency_links.txt +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode.egg-info/entry_points.txt +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode.egg-info/requires.txt +0 -0
- {varcode-9.2.2 → varcode-9.2.4}/varcode.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""A BND fallback describes only its retained reference fragment (#447)."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pyensembl import cached_release
|
|
5
|
+
|
|
6
|
+
from varcode import StructuralVariant, parse_symbolic_alt
|
|
7
|
+
from varcode.effects import TranslocationToIntergenic
|
|
8
|
+
from varcode.effects.structural import _build_translocation_mutant_transcript
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture(params=["ENST00000003084", "ENST00000357654"])
|
|
12
|
+
def transcript(request):
|
|
13
|
+
return cached_release(81).transcript_by_id(request.param)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def expected_offsets(transcript, position, side):
|
|
17
|
+
"""Independent per-base oracle in transcript order, including the anchor."""
|
|
18
|
+
positions = [p for exon in transcript.exons
|
|
19
|
+
for p in (range(exon.start, exon.end + 1) if transcript.strand == "+"
|
|
20
|
+
else range(exon.end, exon.start - 1, -1))]
|
|
21
|
+
kept = [i for i, p in enumerate(positions)
|
|
22
|
+
if (p <= position if side == "left" else p >= position)]
|
|
23
|
+
return min(kept), max(kept) + 1
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.parametrize("alt,side", [
|
|
27
|
+
("N[22:15500000[", "left"), ("N]22:15500000]", "left"),
|
|
28
|
+
("[22:15500000[N", "right"), ("]22:15500000]N", "right"),
|
|
29
|
+
("ACGT.", "left"), (".ACGT", "right"),
|
|
30
|
+
])
|
|
31
|
+
@pytest.mark.parametrize("location", ["exonic", "exon_start", "exon_end", "intronic"])
|
|
32
|
+
def test_both_strands_and_local_sides(transcript, alt, side, location):
|
|
33
|
+
# An interior exon also permits meaningful suffix/prefix comparisons.
|
|
34
|
+
exon = transcript.exons[2]
|
|
35
|
+
if location == "exonic":
|
|
36
|
+
position = (exon.start + exon.end) // 2
|
|
37
|
+
elif location == "exon_start":
|
|
38
|
+
position = exon.start
|
|
39
|
+
elif location == "exon_end":
|
|
40
|
+
position = exon.end
|
|
41
|
+
else:
|
|
42
|
+
other = transcript.exons[3]
|
|
43
|
+
position = ((exon.end + other.start) // 2 if transcript.strand == "+"
|
|
44
|
+
else (other.end + exon.start) // 2)
|
|
45
|
+
sv = parse_symbolic_alt(transcript.contig, position, "N", alt,
|
|
46
|
+
genome=transcript.genome)
|
|
47
|
+
effect = sv.effect_on_transcript(transcript)
|
|
48
|
+
assert isinstance(effect, TranslocationToIntergenic)
|
|
49
|
+
mt = effect.mutant_transcript
|
|
50
|
+
assert mt is not None
|
|
51
|
+
(segment,) = mt.reference_segments
|
|
52
|
+
assert segment.source is transcript
|
|
53
|
+
assert (segment.start, segment.end) == expected_offsets(transcript, position, side)
|
|
54
|
+
assert segment.strand == "+" # source is already oriented transcript cDNA
|
|
55
|
+
five_prime = (side == "left") == (transcript.strand == "+")
|
|
56
|
+
assert segment.label == ("translocation_5p" if five_prime else "translocation_3p")
|
|
57
|
+
assert segment.length < len(transcript.sequence)
|
|
58
|
+
assert mt.cdna_sequence is None
|
|
59
|
+
assert mt.mutant_protein_sequence is None
|
|
60
|
+
assert mt.evidence["sequence_status"] == "retained_reference_fragment"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_cftr_regression_keeps_51_bases_not_entire_transcript():
|
|
64
|
+
tx = cached_release(81).transcript_by_id("ENST00000003084")
|
|
65
|
+
sv = parse_symbolic_alt("7", tx.exons[0].start + 50, "N", "N]22:15500000]",
|
|
66
|
+
genome=tx.genome)
|
|
67
|
+
mt = sv.effect_on_transcript(tx).mutant_transcript
|
|
68
|
+
(segment,) = mt.reference_segments
|
|
69
|
+
assert (segment.start, segment.end) == (0, 51)
|
|
70
|
+
assert segment.source.sequence[segment.start:segment.end] == tx.sequence[:51]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pytest.mark.parametrize("alt", [None, "<BND>", "N[?:?[", "N[22:15500000]", ".", "N.N"])
|
|
74
|
+
def test_unknown_local_orientation_is_unresolved(transcript, alt):
|
|
75
|
+
sv = StructuralVariant(transcript.contig, transcript.exons[0].start + 5,
|
|
76
|
+
"BND", alt=alt, genome=transcript.genome)
|
|
77
|
+
assert sv.effect_on_transcript(transcript).mutant_transcript is None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.parametrize("orientation", ["[[", "]]"])
|
|
81
|
+
def test_mate_orientation_does_not_determine_local_side(transcript, orientation):
|
|
82
|
+
sv = StructuralVariant(transcript.contig, transcript.exons[0].start + 5,
|
|
83
|
+
"BND", mate_contig="22", mate_start=15_500_000,
|
|
84
|
+
mate_orientation=orientation, genome=transcript.genome)
|
|
85
|
+
with pytest.warns(UserWarning, match="can't read the breakend orientation"):
|
|
86
|
+
effect = sv.effect_on_transcript(transcript)
|
|
87
|
+
assert isinstance(effect, TranslocationToIntergenic)
|
|
88
|
+
assert effect.mutant_transcript is None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize("location", ["before", "after", "wrong_contig"])
|
|
92
|
+
def test_unrelated_transcript_does_not_supply_a_segment(transcript, location):
|
|
93
|
+
position = {"before": transcript.start - 1, "after": transcript.end + 1,
|
|
94
|
+
"wrong_contig": transcript.start + 5}[location]
|
|
95
|
+
contig = "1" if location == "wrong_contig" else transcript.contig
|
|
96
|
+
sv = StructuralVariant(contig, position, "BND", alt="N.", genome=transcript.genome)
|
|
97
|
+
assert _build_translocation_mutant_transcript(sv, transcript) is None
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@pytest.mark.parametrize("alt", ["N]22:15500000]", "]22:15500000]N", "N.", ".N", "<BND>"])
|
|
101
|
+
def test_supplied_assembly_is_unchanged(transcript, alt):
|
|
102
|
+
assembly = "ATGGCTTAA"
|
|
103
|
+
sv = StructuralVariant(transcript.contig, transcript.exons[0].start + 5,
|
|
104
|
+
"BND", alt=alt, alt_assembly=assembly, genome=transcript.genome)
|
|
105
|
+
mt = sv.effect_on_transcript(transcript).mutant_transcript
|
|
106
|
+
assert mt.cdna_sequence == assembly
|
|
107
|
+
assert mt.mutant_protein_sequence is None
|
|
108
|
+
assert mt.evidence == {"source": "alt_assembly"}
|
|
109
|
+
(segment,) = mt.reference_segments
|
|
110
|
+
assert segment.source.sequence == assembly
|
|
111
|
+
assert (segment.start, segment.end, segment.label) == (0, len(assembly), "alt_assembly")
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""A partial overlap cannot establish a local DUP/INV transcript (#405)."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from pyensembl import cached_release
|
|
5
|
+
|
|
6
|
+
from varcode import StructuralVariant, parse_symbolic_alt
|
|
7
|
+
from varcode.effects import Inversion, LargeDuplication
|
|
8
|
+
from varcode.effects import structural
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@pytest.fixture(params=["ENST00000003084", "ENST00000357654"])
|
|
12
|
+
def transcript(request):
|
|
13
|
+
return cached_release(81).transcript_by_id(request.param)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture(autouse=True)
|
|
17
|
+
def no_optional_candidates(monkeypatch):
|
|
18
|
+
# Isolate the span fallback; fusion preservation also has real esvee tests.
|
|
19
|
+
monkeypatch.setattr(structural, "_fusion_partner", lambda *args: None)
|
|
20
|
+
monkeypatch.setattr(structural, "_enumerate_and_attach_cryptics", lambda *args: None)
|
|
21
|
+
monkeypatch.setattr(structural, "_enumerate_and_attach_splice_outcomes", lambda *args: None)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def make_sv(transcript, kind, outside, symbolic=False, assembly=None):
|
|
25
|
+
start, end = transcript.start + 50, transcript.end - 50
|
|
26
|
+
if outside in ("left", "both"):
|
|
27
|
+
start = transcript.start - 100
|
|
28
|
+
if outside in ("right", "both"):
|
|
29
|
+
end = transcript.end + 100
|
|
30
|
+
if symbolic:
|
|
31
|
+
return parse_symbolic_alt(
|
|
32
|
+
transcript.contig, start, "N", "<%s>" % kind,
|
|
33
|
+
info={"END": end}, genome=transcript.genome)
|
|
34
|
+
return StructuralVariant(
|
|
35
|
+
transcript.contig, start, kind, end=end,
|
|
36
|
+
genome=transcript.genome, alt_assembly=assembly)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.mark.parametrize("kind,cls", [("DUP", LargeDuplication), ("INV", Inversion)])
|
|
40
|
+
@pytest.mark.parametrize("outside", ["left", "right", "both"])
|
|
41
|
+
@pytest.mark.parametrize("symbolic", [False, True])
|
|
42
|
+
def test_nonlocal_event_keeps_type_without_inventing_transcript(
|
|
43
|
+
transcript, kind, cls, outside, symbolic):
|
|
44
|
+
sv = make_sv(transcript, kind, outside, symbolic)
|
|
45
|
+
effect = sv.effect_on_transcript(transcript)
|
|
46
|
+
assert isinstance(effect, cls)
|
|
47
|
+
assert effect.mutant_transcript is None
|
|
48
|
+
assert all(c.effect.mutant_transcript is None for c in effect.candidates)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@pytest.mark.parametrize("kind", ["DUP", "INV"])
|
|
52
|
+
@pytest.mark.parametrize("outside", ["left", "right", "both"])
|
|
53
|
+
def test_nonlocal_event_preserves_supplied_assembly(transcript, kind, outside):
|
|
54
|
+
assembly = "ATG" + "GCT" * 10 + "TAA"
|
|
55
|
+
sv = make_sv(transcript, kind, outside, assembly=assembly)
|
|
56
|
+
effect = sv.effect_on_transcript(transcript)
|
|
57
|
+
assert effect.mutant_transcript.cdna_sequence == assembly
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@pytest.mark.parametrize("kind", ["DUP", "INV"])
|
|
61
|
+
def test_wholly_internal_event_retains_local_model(transcript, kind):
|
|
62
|
+
sv = make_sv(transcript, kind, outside=None)
|
|
63
|
+
effect = sv.effect_on_transcript(transcript)
|
|
64
|
+
assert effect.mutant_transcript is not None
|
|
65
|
+
if kind == "DUP":
|
|
66
|
+
assert len(effect.mutant_transcript.cdna_sequence) > len(transcript.sequence)
|
|
67
|
+
else:
|
|
68
|
+
assert any(s.strand == "-" for s in effect.mutant_transcript.reference_segments)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize("transcript_id", ["ENST00000439541", "ENST00000550411"])
|
|
72
|
+
@pytest.mark.parametrize("kind", ["DUP", "INV"])
|
|
73
|
+
def test_gene_containment_does_not_establish_short_isoform_structure(transcript_id, kind):
|
|
74
|
+
# CBX5's long isoform spans both ends, but these short isoforms do not.
|
|
75
|
+
genome = cached_release(95)
|
|
76
|
+
tx = genome.transcript_by_id(transcript_id)
|
|
77
|
+
sv = StructuralVariant("12", 54_259_010, kind, end=54_262_307, genome=genome)
|
|
78
|
+
ends = [end for junction in sv.junctions for end in junction]
|
|
79
|
+
assert all(structural._contains(tx.gene, end) for end in ends)
|
|
80
|
+
assert any(not structural._contains(tx, end) for end in ends)
|
|
81
|
+
effect = sv.effect_on_transcript(tx)
|
|
82
|
+
assert isinstance(effect, (LargeDuplication, Inversion))
|
|
83
|
+
assert effect.mutant_transcript is None
|
|
@@ -48,7 +48,7 @@ from varcode import StructuralVariant, FastEffectAnnotator, load_vcf
|
|
|
48
48
|
from varcode.effects.structural import (
|
|
49
49
|
_build_fusion_mutant_transcript,
|
|
50
50
|
)
|
|
51
|
-
from varcode.effects import GeneFusion, Intronic
|
|
51
|
+
from varcode.effects import GeneFusion, Intronic, Inversion, LargeDuplication
|
|
52
52
|
from varcode.transforms import pair_breakends
|
|
53
53
|
|
|
54
54
|
from .data import data_path
|
|
@@ -267,6 +267,10 @@ def test_esvee_vcf_otx1_kif3c_inversion_is_fusion_on_both_partners():
|
|
|
267
267
|
assert fusion.three_prime_transcript.id == kif3c.id
|
|
268
268
|
assert fusion.mutant_transcript.mutant_protein_sequence == (
|
|
269
269
|
"MMSYLKQPPYGMNGLGLAGPAMDLLHPSVGYPETS")
|
|
270
|
+
if sv.sv_type == "INV":
|
|
271
|
+
(span,) = [c.effect for c in fusion.candidates
|
|
272
|
+
if isinstance(c.effect, Inversion)]
|
|
273
|
+
assert span.mutant_transcript is None
|
|
270
274
|
|
|
271
275
|
|
|
272
276
|
def test_esvee_vcf_cpeb2_fam193a_duplication_is_fusion():
|
|
@@ -290,6 +294,10 @@ def test_esvee_vcf_cpeb2_fam193a_duplication_is_fusion():
|
|
|
290
294
|
assert isinstance(on_fam193a, GeneFusion)
|
|
291
295
|
assert on_fam193a.three_prime_transcript.id == fam193a.id
|
|
292
296
|
assert on_fam193a.five_prime_transcript.gene_name == "CPEB2"
|
|
297
|
+
for fusion in (on_cpeb2, on_fam193a):
|
|
298
|
+
(span,) = [c.effect for c in fusion.candidates
|
|
299
|
+
if isinstance(c.effect, LargeDuplication)]
|
|
300
|
+
assert span.mutant_transcript is None
|
|
293
301
|
|
|
294
302
|
|
|
295
303
|
def test_esvee_vcf_sema6a_parm1_fusion_from_either_record():
|
|
@@ -37,7 +37,7 @@ def test_no_nonempty_span_cannot_delete_or_copy_the_anchor(kind, info):
|
|
|
37
37
|
def test_exact_mutant_cdna_on_both_strands(kind, builder, reverse):
|
|
38
38
|
sequence = "ACGTTGCAAGCTTAGGCTAC"
|
|
39
39
|
tx = SimpleNamespace(exons=[SimpleNamespace(start=100, end=119)],
|
|
40
|
-
start=100, end=119, sequence=sequence,
|
|
40
|
+
contig="1", start=100, end=119, sequence=sequence,
|
|
41
41
|
on_backward_strand=reverse)
|
|
42
42
|
sv = parse_symbolic_alt("1", 109, "G", "<%s>" % kind, info={"END": 114})
|
|
43
43
|
a, b = (5, 10) if reverse else (10, 15)
|
|
@@ -35,7 +35,7 @@ from .effect_helpers import exon_length
|
|
|
35
35
|
from ..mutant_transcript import MutantTranscript, ReferenceSegment
|
|
36
36
|
# Existing assembled-SV pickles use this private module path.
|
|
37
37
|
from ..mutant_transcript import _AssembledAllele # noqa: F401
|
|
38
|
-
from ..sv_allele_parser import breakend_sides
|
|
38
|
+
from ..sv_allele_parser import breakend_sides, _breakend_local_side
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
# --------------------------------------------------------------------
|
|
@@ -126,9 +126,15 @@ def _cdna_ranges_kept_after_deletion(variant, transcript):
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
def _cdna_ranges_within_sv(variant, transcript):
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
"""Body ranges for a local DUP/INV model, or [] if not local.
|
|
130
|
+
|
|
131
|
+
Clipping a cross-boundary event to this transcript invents a local
|
|
132
|
+
rearrangement (#405). Every junction end must be in this transcript,
|
|
133
|
+
not merely in its gene. Fusion and supplied-assembly paths are separate.
|
|
134
|
+
"""
|
|
135
|
+
if any(not _contains(transcript, end)
|
|
136
|
+
for junction in variant.junctions for end in junction):
|
|
137
|
+
return []
|
|
132
138
|
sv_start, sv_end = _affected_span(variant)
|
|
133
139
|
inside = []
|
|
134
140
|
reverse = transcript.on_backward_strand
|
|
@@ -495,23 +501,29 @@ def _build_fusion_mutant_transcript(
|
|
|
495
501
|
|
|
496
502
|
|
|
497
503
|
def _build_translocation_mutant_transcript(variant, transcript):
|
|
498
|
-
"""
|
|
499
|
-
:class:`TranslocationToIntergenic`.
|
|
504
|
+
"""Describe only the retained local reference fragment of a BND.
|
|
500
505
|
|
|
501
|
-
|
|
502
|
-
breakpoint
|
|
503
|
-
|
|
504
|
-
with caller-supplied genomic intervals / long-read assemblies.
|
|
506
|
+
The other side and full mutant cDNA/protein remain unknown. Without
|
|
507
|
+
local orientation or a breakpoint on this transcript, no fragment can
|
|
508
|
+
be established. Supplied assemblies are handled by the caller.
|
|
505
509
|
"""
|
|
510
|
+
side = _breakend_local_side(variant.symbolic_alt)
|
|
511
|
+
if (side is None or str(variant.contig) != str(transcript.contig)
|
|
512
|
+
or not transcript.start <= variant.start <= transcript.end):
|
|
513
|
+
return None
|
|
514
|
+
keep_five_prime = _retains_five_prime_end(transcript, side)
|
|
515
|
+
cut = _cdna_cut(transcript, variant.start, keep_five_prime)
|
|
506
516
|
full_len = len(str(transcript.sequence))
|
|
517
|
+
start, end = (0, cut) if keep_five_prime else (cut, full_len)
|
|
507
518
|
segments = (
|
|
508
519
|
ReferenceSegment(
|
|
509
|
-
source=transcript, start=
|
|
510
|
-
|
|
520
|
+
source=transcript, start=start, end=end, strand="+",
|
|
521
|
+
label="translocation_5p" if keep_five_prime else "translocation_3p"),)
|
|
511
522
|
return MutantTranscript(
|
|
512
523
|
reference_transcript=transcript,
|
|
513
524
|
reference_segments=segments,
|
|
514
525
|
cdna_sequence=None,
|
|
526
|
+
evidence={"sequence_status": "retained_reference_fragment"},
|
|
515
527
|
annotator_name="structural_variant")
|
|
516
528
|
|
|
517
529
|
|
|
@@ -232,7 +232,10 @@ class MutantTranscript(DataclassSerializable):
|
|
|
232
232
|
strand segments), produce the mutant cDNA. ``None`` for the
|
|
233
233
|
point-variant shape; a fusion's segments would be
|
|
234
234
|
``(5p_partner_segment, 3p_partner_segment)``. Coordinates are
|
|
235
|
-
in each segment's own reference system.
|
|
235
|
+
in each segment's own reference system. A partial structural model may
|
|
236
|
+
describe only a retained reference fragment: its producer identifies this
|
|
237
|
+
in ``evidence`` and leaves the full ``cdna_sequence`` unknown. Concatenating
|
|
238
|
+
those partial segments does not establish a full allele."""
|
|
236
239
|
|
|
237
240
|
cdna_sequence: Optional[str] = None
|
|
238
241
|
"""The mutated spliced mRNA, when computed. ``None`` if the
|
|
@@ -78,6 +78,17 @@ def breakend_sides(alt):
|
|
|
78
78
|
return this_side, mate_side
|
|
79
79
|
|
|
80
80
|
|
|
81
|
+
def _breakend_local_side(alt):
|
|
82
|
+
"""The retained local side, including single breakends with no mate."""
|
|
83
|
+
sides = breakend_sides(alt)
|
|
84
|
+
if sides is not None:
|
|
85
|
+
return sides[0]
|
|
86
|
+
single = _SINGLE_BREAKEND_RE.match(alt or "")
|
|
87
|
+
if single is not None:
|
|
88
|
+
return "right" if single.group("joined_before") else "left"
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
81
92
|
def _extract_info(info, key):
|
|
82
93
|
"""Pull an INFO field value, handling list-of-ints from CIPOS /
|
|
83
94
|
CIEND and plain ints/strings otherwise. Returns ``None`` if
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "9.2.4"
|
|
@@ -6,6 +6,7 @@ requirements.txt
|
|
|
6
6
|
tests/test_annotator_divergence_scenarios.py
|
|
7
7
|
tests/test_annotator_parity_adversarial.py
|
|
8
8
|
tests/test_annotators.py
|
|
9
|
+
tests/test_bnd_retained_segments.py
|
|
9
10
|
tests/test_cli_effects.py
|
|
10
11
|
tests/test_cli_genes.py
|
|
11
12
|
tests/test_cli_without_pkg_resources.py
|
|
@@ -45,6 +46,7 @@ tests/test_mouse.py
|
|
|
45
46
|
tests/test_mutant_transcript.py
|
|
46
47
|
tests/test_mutate.py
|
|
47
48
|
tests/test_no_duplicate_variants.py
|
|
49
|
+
tests/test_nonlocal_sv_transcripts.py
|
|
48
50
|
tests/test_osteosarc_fusions.py
|
|
49
51
|
tests/test_partial_annotators.py
|
|
50
52
|
tests/test_premature_stop_short_description.py
|
varcode-9.2.2/varcode/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "9.2.2"
|
|
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
|