varcode 9.3.1__tar.gz → 9.3.2__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.3.1/varcode.egg-info → varcode-9.3.2}/PKG-INFO +1 -1
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_sv_sequence_changes.py +209 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/sequence_change.py +70 -2
- varcode-9.3.2/varcode/version.py +1 -0
- {varcode-9.3.1 → varcode-9.3.2/varcode.egg-info}/PKG-INFO +1 -1
- varcode-9.3.1/varcode/version.py +0 -1
- {varcode-9.3.1 → varcode-9.3.2}/LICENSE +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/MANIFEST.in +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/README.md +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/pyproject.toml +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/requirements.txt +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/setup.cfg +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_annotator_divergence_scenarios.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_annotator_parity_adversarial.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_annotators.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_bnd_retained_segments.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_cli_effects.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_cli_genes.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_cli_without_pkg_resources.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_collection_filtering.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_collection_variants_attr_consistency.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_common.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_cosmic_mutations.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_cryptic_exons.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_csv_roundtrip.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_dbnsfp_validation.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_default_structural_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_documentation_examples.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_annotation_errors.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_candidates.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_classes.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_collection.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_collection_serialization.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_collection_sort_order.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effect_hypotheses.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_effects_from_mutagenix_variants.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_exacto_fusions.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_exonic_splice_site.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_fast_path.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_frameshift_cterminus_regression.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_frameshift_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_fusion_partner_candidates.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_genome_contig_validation.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_genome_sequence.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_genomic_layout.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_genotype.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_genotype_from_vcf.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_germline_context.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_germline_effects.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_haplotype_effect.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_maf.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_mitochondrial.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_mm10_klf6_frameshift.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_mouse.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_mutant_transcript.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_mutate.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_no_duplicate_variants.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_nonlocal_sv_transcripts.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_osteosarc_fusions.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_partial_annotators.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_premature_stop_short_description.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_problematic_variants.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_protein_diff_parity.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_read_phase_resolver.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_real_world_callers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_reference.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_reference_mismatch_error.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_reference_segments.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_rna_evidence.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_rna_haplotype_sequences.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_rna_read_phasing_source.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_selenocysteine.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_silent_aa_pos.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_silent_hgvs_description.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_splice_graph.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_splice_outcomes.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_splice_site_effects.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_stop_codon_classification_bugs.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_string_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_structural_variant_annotator.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_structural_variants.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_sv_tabular_metadata.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_symbolic_alleles.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_symbolic_sv_padding.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_timings.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_transcript_layout.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_transcript_model.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_transforms_left_align.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_transforms_pair_breakends.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_variant.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_variant_collection.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_vcf.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_vcf_output.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_vcf_parsing.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_vcf_phase_resolver.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/tests/test_vcf_spec_examples.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/__init__.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/annotators/__init__.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/annotators/fast.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/annotators/protein_diff.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/annotators/registry.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/__init__.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/effects_script.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/genes_script.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/logging.conf +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/variant_args.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cli/version_info.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/common.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/cryptic_exons.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/csv_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effect_candidates.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effect_hypotheses.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/__init__.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/classify.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/codon_tables.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/common.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_classes.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_collection.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_ordering.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_prediction.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_prediction_coding.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_prediction_coding_frameshift.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/effect_prediction_coding_in_frame.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/fast_path.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/mutate.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/structural.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/transcript_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/effects/translate.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/errors.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/exacto.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/genome.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/genome_sequence.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/genomic_layout.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/genotype.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/germline.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/maf.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/mutant_transcript.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/nucleotides.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/phasing.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/realized_effects.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/reference.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/rna_evidence.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/rna_read_phasing.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/splice_graph.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/splice_outcomes.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/string_helpers.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/structural_variant.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/sv_allele_parser.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/transcript_layout.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/transcript_model.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/transforms/__init__.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/ucsc_reference_names.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/util.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/variant.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/variant_collection.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/vcf.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/vcf_output.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode/vcf_parsing.py +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode.egg-info/SOURCES.txt +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode.egg-info/dependency_links.txt +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode.egg-info/entry_points.txt +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode.egg-info/requires.txt +0 -0
- {varcode-9.3.1 → varcode-9.3.2}/varcode.egg-info/top_level.txt +0 -0
|
@@ -14,6 +14,7 @@ from varcode.effects import (
|
|
|
14
14
|
StructuralVariantEffect, TranslocationToIntergenic, Unresolved,
|
|
15
15
|
)
|
|
16
16
|
from varcode.effects import structural
|
|
17
|
+
from varcode.effects.codon_tables import codon_table_for_transcript
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
@pytest.fixture
|
|
@@ -128,6 +129,214 @@ def test_partial_fragment_and_unmapped_assembly_are_unknown(cftr):
|
|
|
128
129
|
assert effect.modifies_coding_sequence is None
|
|
129
130
|
|
|
130
131
|
|
|
132
|
+
@pytest.mark.parametrize("completeness", [
|
|
133
|
+
"partial_start", "partial_end", "partial_both", "unknown", None,
|
|
134
|
+
])
|
|
135
|
+
@pytest.mark.parametrize("protein", ["reference_fragment", "different", ""])
|
|
136
|
+
def test_unmapped_partial_proteins_do_not_establish_a_change(cftr, completeness, protein):
|
|
137
|
+
if protein == "reference_fragment":
|
|
138
|
+
protein = cftr.protein_sequence[:100]
|
|
139
|
+
effect = _effect(cftr)
|
|
140
|
+
effect.mutant_transcript = MutantTranscript.from_sequence(
|
|
141
|
+
cftr.sequence[:432], reference_transcript=cftr,
|
|
142
|
+
mutant_protein_sequence=protein,
|
|
143
|
+
evidence={"protein_completeness": completeness})
|
|
144
|
+
assert effect.modifies_protein_sequence is None
|
|
145
|
+
assert effect.modifies_coding_sequence is None
|
|
146
|
+
assert list(EffectCollection([effect]).drop_silent_and_noncoding()) == [effect]
|
|
147
|
+
assert len(EffectCollection([effect]).drop_silent_and_noncoding(False)) == 0
|
|
148
|
+
assert effect.mutant_protein_sequence == protein
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@pytest.mark.parametrize("completeness", ["partial_start", "partial_end", "partial_both"])
|
|
152
|
+
def test_partial_label_prevents_complete_orf_fallback(cftr, completeness):
|
|
153
|
+
# A complete-looking ORF must not override the producer's explicit partial
|
|
154
|
+
# interpretation (e.g. an internal alternative start in an observed fragment).
|
|
155
|
+
effect = _effect(cftr, cdna=cftr.sequence, segments=_identity_segments(cftr),
|
|
156
|
+
evidence={"protein_completeness": completeness})
|
|
157
|
+
assert effect.modifies_protein_sequence is None
|
|
158
|
+
assert effect.modifies_coding_sequence is None
|
|
159
|
+
evidence = dict(effect.mutant_transcript.evidence,
|
|
160
|
+
cds_start=min(cftr.start_codon_spliced_offsets),
|
|
161
|
+
cds_end=max(cftr.stop_codon_spliced_offsets) + 1)
|
|
162
|
+
effect.mutant_transcript = replace(effect.mutant_transcript, evidence=evidence)
|
|
163
|
+
assert effect.modifies_protein_sequence is None
|
|
164
|
+
assert effect.modifies_coding_sequence is None
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _mapped_partial(transcript, completeness, change="none"):
|
|
168
|
+
"""An observed CDS fragment mapped onto its reference, one codon changed.
|
|
169
|
+
|
|
170
|
+
Flags compare observed codons, so no protein is supplied.
|
|
171
|
+
"""
|
|
172
|
+
table = codon_table_for_transcript(transcript)
|
|
173
|
+
start = min(transcript.start_codon_spliced_offsets)
|
|
174
|
+
stop = max(transcript.stop_codon_spliced_offsets) + 1
|
|
175
|
+
left = start if completeness == "partial_end" else start + 300
|
|
176
|
+
right = stop if completeness == "partial_start" else left + 300
|
|
177
|
+
cdna = transcript.sequence[left:right]
|
|
178
|
+
if change == "stop_loss":
|
|
179
|
+
pos, codon = len(cdna) - 3, "TGG"
|
|
180
|
+
else:
|
|
181
|
+
pos = next(i for i in range(3, len(cdna), 3)
|
|
182
|
+
if table.forward_table.get(cdna[i:i + 3], "M") not in "MW")
|
|
183
|
+
original = cdna[pos:pos + 3]
|
|
184
|
+
synonyms = [c for c, aa in sorted(table.forward_table.items())
|
|
185
|
+
if aa == table.forward_table[original] and c != original]
|
|
186
|
+
codon = {"none": original, "synonymous": synonyms[0], "missense": "TGG",
|
|
187
|
+
"nonsense": "TAA", "ambiguous": original[:2] + "N"}[change]
|
|
188
|
+
cdna = cdna[:pos] + codon + cdna[pos + 3:]
|
|
189
|
+
return _effect(
|
|
190
|
+
transcript, cdna=cdna,
|
|
191
|
+
segments=(ReferenceSegment(transcript, left, right),),
|
|
192
|
+
evidence={"protein_completeness": completeness,
|
|
193
|
+
"cds_start": 0, "cds_end": len(cdna)}), pos
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@pytest.mark.parametrize("transcript_id", ["ENST00000003084", "ENST00000357654"])
|
|
197
|
+
@pytest.mark.parametrize("completeness", ["partial_start", "partial_end", "partial_both"])
|
|
198
|
+
@pytest.mark.parametrize("change", ["none", "synonymous", "missense", "nonsense", "ambiguous"])
|
|
199
|
+
def test_partial_observation_only_establishes_mapped_local_changes(
|
|
200
|
+
transcript_id, completeness, change):
|
|
201
|
+
transcript = cached_release(81).transcript_by_id(transcript_id)
|
|
202
|
+
effect, _ = _mapped_partial(transcript, completeness, change)
|
|
203
|
+
before = effect.mutant_transcript
|
|
204
|
+
assert effect.modifies_coding_sequence is (
|
|
205
|
+
True if change in ("synonymous", "missense", "nonsense") else None)
|
|
206
|
+
assert effect.modifies_protein_sequence is (
|
|
207
|
+
True if change in ("missense", "nonsense") else None)
|
|
208
|
+
assert effect.mutant_transcript == before
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.parametrize("transcript_id", ["ENST00000003084", "ENST00000357654"])
|
|
212
|
+
def test_mapped_stop_loss_is_a_protein_change(transcript_id):
|
|
213
|
+
transcript = cached_release(81).transcript_by_id(transcript_id)
|
|
214
|
+
effect, _ = _mapped_partial(transcript, "partial_start", "stop_loss")
|
|
215
|
+
assert effect.modifies_coding_sequence is True
|
|
216
|
+
assert effect.modifies_protein_sequence is True
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_mapped_reference_fragment_is_not_a_truncation(cftr):
|
|
220
|
+
# The #462 report, with reference coordinates: equal observed codons and a
|
|
221
|
+
# shorter supplied protein still leave the unobserved suffix unknown.
|
|
222
|
+
effect, _ = _mapped_partial(cftr, "partial_end")
|
|
223
|
+
effect.mutant_transcript = replace(
|
|
224
|
+
effect.mutant_transcript, mutant_protein_sequence=cftr.protein_sequence[:100])
|
|
225
|
+
assert effect.modifies_coding_sequence is None
|
|
226
|
+
assert effect.modifies_protein_sequence is None
|
|
227
|
+
assert len(EffectCollection([effect]).drop_silent_and_noncoding(False)) == 0
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_partial_uses_mitochondrial_table():
|
|
231
|
+
# TGA and TGG both encode tryptophan in vertebrate mitochondria.
|
|
232
|
+
transcript = cached_release(81).transcript_by_id("ENST00000361624")
|
|
233
|
+
effect, _ = _mapped_partial(transcript, "partial_both")
|
|
234
|
+
model = effect.mutant_transcript
|
|
235
|
+
cdna = model.cdna_sequence
|
|
236
|
+
pos = next(i for i in range(0, len(cdna), 3) if cdna[i:i + 3] in ("TGA", "TGG"))
|
|
237
|
+
swapped = "TGG" if cdna[pos:pos + 3] == "TGA" else "TGA"
|
|
238
|
+
effect.mutant_transcript = replace(
|
|
239
|
+
model, cdna_sequence=cdna[:pos] + swapped + cdna[pos + 3:])
|
|
240
|
+
assert effect.modifies_coding_sequence is True
|
|
241
|
+
assert effect.modifies_protein_sequence is None
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
@pytest.mark.parametrize("upstream", [False, True])
|
|
245
|
+
def test_alternative_start_codon_reads_as_met_only_where_the_orf_begins(cftr, upstream):
|
|
246
|
+
# A 3' partner's start codon is internal to a fused ORF, so CTG there is Leu.
|
|
247
|
+
brca1 = cached_release(81).transcript_by_id("ENST00000357654")
|
|
248
|
+
start = min(cftr.start_codon_spliced_offsets)
|
|
249
|
+
other = min(brca1.start_codon_spliced_offsets) + 3
|
|
250
|
+
segments = (ReferenceSegment(cftr, start, start + 300),)
|
|
251
|
+
cdna = "CTG" + cftr.sequence[start + 3:start + 300]
|
|
252
|
+
if upstream:
|
|
253
|
+
segments = (ReferenceSegment(brca1, other, other + 30),) + segments
|
|
254
|
+
cdna = brca1.sequence[other:other + 30] + cdna
|
|
255
|
+
effect = _effect(cftr, cdna=cdna, segments=segments, evidence={
|
|
256
|
+
"protein_completeness": "partial_both", "cds_start": 0, "cds_end": len(cdna)})
|
|
257
|
+
assert effect.modifies_coding_sequence is True
|
|
258
|
+
assert effect.modifies_protein_sequence is (True if upstream else None)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_partial_bounds_accept_numpy_integers(cftr):
|
|
262
|
+
import numpy as np
|
|
263
|
+
|
|
264
|
+
effect, _ = _mapped_partial(cftr, "partial_both", "missense")
|
|
265
|
+
evidence = effect.mutant_transcript.evidence
|
|
266
|
+
effect.mutant_transcript = replace(effect.mutant_transcript, evidence=dict(
|
|
267
|
+
evidence, cds_start=np.int64(0), cds_end=np.int64(evidence["cds_end"])))
|
|
268
|
+
assert effect.modifies_coding_sequence is True
|
|
269
|
+
assert effect.modifies_protein_sequence is True
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_partial_mapped_change_survives_split_reference_segments(cftr):
|
|
273
|
+
effect, pos = _mapped_partial(cftr, "partial_both", "missense")
|
|
274
|
+
segment, = effect.mutant_transcript.reference_segments
|
|
275
|
+
effect.mutant_transcript = replace(effect.mutant_transcript, reference_segments=(
|
|
276
|
+
replace(segment, end=segment.start + pos + 1),
|
|
277
|
+
replace(segment, start=segment.start + pos + 1)))
|
|
278
|
+
assert effect.modifies_coding_sequence is True
|
|
279
|
+
assert effect.modifies_protein_sequence is True
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@pytest.mark.parametrize("missing", [
|
|
283
|
+
"frame", "float_frame", "offset_frame", "mapping", "reverse", "length", "edits"])
|
|
284
|
+
def test_partial_local_change_requires_usable_coordinates(cftr, missing):
|
|
285
|
+
from varcode import TranscriptEdit
|
|
286
|
+
|
|
287
|
+
effect, _ = _mapped_partial(cftr, "partial_both", "missense")
|
|
288
|
+
model = effect.mutant_transcript
|
|
289
|
+
if missing == "frame":
|
|
290
|
+
model = replace(model, evidence={"protein_completeness": "partial_both"})
|
|
291
|
+
elif missing == "float_frame":
|
|
292
|
+
model = replace(model, evidence=dict(model.evidence, cds_start=0.0))
|
|
293
|
+
elif missing == "offset_frame":
|
|
294
|
+
# Codons out of frame with the reference CDS have no counterpart.
|
|
295
|
+
model = replace(model, evidence=dict(model.evidence, cds_start=1))
|
|
296
|
+
elif missing == "mapping":
|
|
297
|
+
model = replace(model, reference_segments=None)
|
|
298
|
+
elif missing == "reverse":
|
|
299
|
+
model = replace(model, reference_segments=(replace(model.reference_segments[0], strand="-"),))
|
|
300
|
+
elif missing == "length":
|
|
301
|
+
model = replace(model, cdna_sequence=model.cdna_sequence + "A")
|
|
302
|
+
else:
|
|
303
|
+
model = replace(model, edits=(TranscriptEdit(0, 0, "A"),))
|
|
304
|
+
effect.mutant_transcript = model
|
|
305
|
+
assert effect.modifies_coding_sequence is None
|
|
306
|
+
assert effect.modifies_protein_sequence is None
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
@pytest.mark.parametrize("completeness", ["partial_start", "partial_end", "partial_both"])
|
|
310
|
+
def test_imported_partial_peptide_without_reference_mapping_is_unknown(cftr, completeness):
|
|
311
|
+
# Exacto peptides carry observed ORF bounds but no reference coordinates.
|
|
312
|
+
variant = StructuralVariant(cftr.contig, cftr.start, "BND", genome=cftr.genome)
|
|
313
|
+
start = min(cftr.start_codon_spliced_offsets)
|
|
314
|
+
cdna = cftr.sequence[:start + 3] + "TGG" + cftr.sequence[start + 6:start + 300]
|
|
315
|
+
candidate = make_fusion_outcome(
|
|
316
|
+
variant, cftr, sequence=cdna, transcript_model_id="observed-model", source="exacto")
|
|
317
|
+
model = candidate.effect.mutant_transcript
|
|
318
|
+
candidate.effect.mutant_transcript = replace(
|
|
319
|
+
model, mutant_protein_sequence="MW" + cftr.protein_sequence[2:100],
|
|
320
|
+
evidence=dict(model.evidence or {}, protein_completeness=completeness,
|
|
321
|
+
cds_start=start, cds_end=start + 300))
|
|
322
|
+
assert candidate.effect.modifies_coding_sequence is None
|
|
323
|
+
assert candidate.effect.modifies_protein_sequence is None
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_complete_protein_and_partial_alternative_remain_unknown(cftr):
|
|
327
|
+
primary = _effect(cftr, protein=cftr.protein_sequence,
|
|
328
|
+
evidence={"protein_completeness": "start_to_stop"})
|
|
329
|
+
partial, _ = _mapped_partial(cftr, "partial_both", "none")
|
|
330
|
+
primary._extra_candidates = (EffectCandidate(partial, source="rna"),)
|
|
331
|
+
assert primary.modifies_protein_sequence is None
|
|
332
|
+
assert len(EffectCollection([primary]).drop_silent_and_noncoding()) == 1
|
|
333
|
+
assert len(EffectCollection([primary]).drop_silent_and_noncoding(False)) == 0
|
|
334
|
+
changed, _ = _mapped_partial(cftr, "partial_both", "missense")
|
|
335
|
+
primary._extra_candidates += (EffectCandidate(changed, source="rna"),)
|
|
336
|
+
assert primary.modifies_protein_sequence is True
|
|
337
|
+
assert len(EffectCollection([primary]).drop_silent_and_noncoding(False)) == 1
|
|
338
|
+
|
|
339
|
+
|
|
131
340
|
@pytest.mark.parametrize("primary_status", [False, None])
|
|
132
341
|
def test_later_changed_candidate_controls_flags_and_filter(cftr, primary_status):
|
|
133
342
|
primary = _effect(cftr, protein=cftr.protein_sequence if primary_status is False else None)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Sequence-change flags for existing SV predictions, not a new SV model."""
|
|
2
2
|
|
|
3
|
+
import operator
|
|
4
|
+
|
|
3
5
|
from .codon_tables import codon_table_for_transcript, translate_sequence
|
|
4
6
|
|
|
5
7
|
|
|
@@ -82,6 +84,67 @@ def _coding_sequence(model, initiator):
|
|
|
82
84
|
return None
|
|
83
85
|
|
|
84
86
|
|
|
87
|
+
def _residue(codon, table, initiator):
|
|
88
|
+
# As for a complete ORF, any start codon at the initiator reads as Met.
|
|
89
|
+
if initiator and codon in table.start_codons:
|
|
90
|
+
return "M"
|
|
91
|
+
return table.forward_table.get(codon, "*")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _partial_sequence_changes(model, transcript, table):
|
|
95
|
+
"""Compare only the mapped, in-frame codons of a partial observation.
|
|
96
|
+
|
|
97
|
+
``cds_start`` / ``cds_end`` place the producer's reading frame in the
|
|
98
|
+
observed cDNA; transcript segments give each base's reference offset.
|
|
99
|
+
A codon is compared only when its three bases map contiguously onto one
|
|
100
|
+
reference CDS codon. A difference there proves a local change. Equal
|
|
101
|
+
codons cannot show that unobserved sequence is unchanged, and missing
|
|
102
|
+
coverage is not a deletion or truncation, so this never returns False.
|
|
103
|
+
"""
|
|
104
|
+
evidence = model.evidence or {}
|
|
105
|
+
try:
|
|
106
|
+
# Any integer type (e.g. numpy); not missing or fractional bounds.
|
|
107
|
+
start = operator.index(evidence.get("cds_start"))
|
|
108
|
+
end = operator.index(evidence.get("cds_end"))
|
|
109
|
+
except TypeError:
|
|
110
|
+
return None, None
|
|
111
|
+
sequence = model.cdna_sequence
|
|
112
|
+
segments = model.reference_segments or ()
|
|
113
|
+
if (sequence is None or model.edits or not transcript.complete
|
|
114
|
+
or not 0 <= start < end <= len(sequence)
|
|
115
|
+
or sum(s.length for s in segments) != len(sequence)):
|
|
116
|
+
# Unmapped edits, or segments that don't render this sequence,
|
|
117
|
+
# leave observed bases without reference coordinates.
|
|
118
|
+
return None, None
|
|
119
|
+
reference_offsets = {}
|
|
120
|
+
offset = 0
|
|
121
|
+
for segment in segments:
|
|
122
|
+
if segment.source == transcript and segment.strand == "+":
|
|
123
|
+
for pos in range(max(start, offset), min(end, offset + segment.length)):
|
|
124
|
+
reference_offsets[pos] = segment.start + pos - offset
|
|
125
|
+
offset += segment.length
|
|
126
|
+
reference_start = min(transcript.start_codon_spliced_offsets)
|
|
127
|
+
reference_coding = transcript.coding_sequence.upper()
|
|
128
|
+
coding_status = None
|
|
129
|
+
for pos in range(start, end - 2, 3):
|
|
130
|
+
codon = sequence[pos:pos + 3].upper()
|
|
131
|
+
ref = reference_offsets.get(pos)
|
|
132
|
+
k = None if ref is None else ref - reference_start
|
|
133
|
+
if (k is not None and k % 3 == 0 and 0 <= k < len(reference_coding)
|
|
134
|
+
and reference_offsets.get(pos + 1) == ref + 1
|
|
135
|
+
and reference_offsets.get(pos + 2) == ref + 2):
|
|
136
|
+
reference_codon = reference_coding[k:k + 3]
|
|
137
|
+
if codon != reference_codon and not set(codon + reference_codon) - set("ACGT"):
|
|
138
|
+
coding_status = True
|
|
139
|
+
# Only an observed ORF that begins here can initiate here.
|
|
140
|
+
observed = _residue(codon, table, k == 0 and pos == start)
|
|
141
|
+
if observed != _residue(reference_codon, table, k == 0):
|
|
142
|
+
return True, True
|
|
143
|
+
if codon in table.stop_codons:
|
|
144
|
+
break # Later bases are not translated in this frame.
|
|
145
|
+
return coding_status, None
|
|
146
|
+
|
|
147
|
+
|
|
85
148
|
def structural_sequence_changes(effect):
|
|
86
149
|
"""Return local (coding, protein) changes as True / False / None.
|
|
87
150
|
|
|
@@ -93,15 +156,20 @@ def structural_sequence_changes(effect):
|
|
|
93
156
|
|
|
94
157
|
transcript = effect.transcript
|
|
95
158
|
model = effect.mutant_transcript
|
|
159
|
+
initiator = getattr(effect, "five_prime_transcript", transcript)
|
|
96
160
|
coding_status = protein_status = None
|
|
97
|
-
if model is not None
|
|
161
|
+
if model is not None and (model.evidence or {}).get(
|
|
162
|
+
"protein_completeness", "start_to_stop") != "start_to_stop":
|
|
163
|
+
# A fragment is not a full-length protein; a shorter one isn't truncated.
|
|
164
|
+
coding_status, protein_status = _partial_sequence_changes(
|
|
165
|
+
model, transcript, codon_table_for_transcript(initiator))
|
|
166
|
+
elif model is not None:
|
|
98
167
|
protein = model.mutant_protein_sequence
|
|
99
168
|
reference_protein = transcript.protein_sequence
|
|
100
169
|
if protein is not None and reference_protein is not None:
|
|
101
170
|
# An empty protein is a known absence, not missing data.
|
|
102
171
|
if "X" not in protein and "X" not in reference_protein:
|
|
103
172
|
protein_status = protein != reference_protein
|
|
104
|
-
initiator = getattr(effect, "five_prime_transcript", transcript)
|
|
105
173
|
coding = _coding_sequence(model, initiator)
|
|
106
174
|
if coding is not None:
|
|
107
175
|
reference_coding = transcript.coding_sequence
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "9.3.2"
|
varcode-9.3.1/varcode/version.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "9.3.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|