• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openvax / varcode / 9781214627

03 Jul 2024 04:04PM UTC coverage: 88.215% (-6.5%) from 94.753%
9781214627

push

github

web-flow
Merge pull request #253 from openvax/default-ucsc-contig-rename-true

Defaulting convert_ucsc_contig_names to True for consistency with past behavior

1 of 1 new or added line in 1 file covered. (100.0%)

144 existing lines in 17 files now uncovered.

1542 of 1748 relevant lines covered (88.22%)

2.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.91
/varcode/effects/effect_classes.py
1
# Licensed under the Apache License, Version 2.0 (the "License");
2
# you may not use this file except in compliance with the License.
3
# You may obtain a copy of the License at
4
#
5
#     http://www.apache.org/licenses/LICENSE-2.0
6
#
7
# Unless required by applicable law or agreed to in writing, software
8
# distributed under the License is distributed on an "AS IS" BASIS,
9
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
# See the License for the specific language governing permissions and
11
# limitations under the License.
12

13

14
from memoized_property import memoized_property
3✔
15
from serializable import Serializable
3✔
16

17
from .common import bio_seq_to_str
3✔
18

19

20
class MutationEffect(Serializable):
3✔
21
    """
22
    Base class for mutation effects.
23
    """
24

25
    def __init__(self, variant):
3✔
26
        self.variant = variant
3✔
27

28
    def __str__(self):
3✔
29
        return "%s(variant=%s)" % (self.__class__.__name__, self.variant)
×
30

31
    def __repr__(self):
3✔
32
        return str(self)
3✔
33

34
    def __lt__(self, other):
3✔
35
        """
36
        Effects are ordered by their associated variants, which have
37
        comparison implement in terms of their chromosomal locations.
38
        """
39
        return self.variant < other.variant
×
40

41
    @property
3✔
42
    def short_description(self):
3✔
43
        """
44
        A short but human-readable description of the effect.
45
        Defaults to class name for most of the non-coding effects,
46
        but is more informative for coding ones.
47
        """
48
        return self.__class__.__name__.lower()
×
49

50
    transcript = None
3✔
51
    gene = None
3✔
52

53
    @property
3✔
54
    def original_protein_sequence(self):
3✔
55
        """Amino acid sequence of a coding transcript (without the nucleotide
56
        variant/mutation)
57
        """
58
        if self.transcript:
3✔
59
            return self.transcript.protein_sequence
3✔
60
        else:
61
            return None
×
62

63
    @property
3✔
64
    def gene_name(self):
3✔
65
        if self.gene:
3✔
66
            return self.gene.name
3✔
67
        else:
68
            return None
×
69

70
    @property
3✔
71
    def gene_id(self):
3✔
72
        if self.gene:
3✔
73
            return self.gene.id
3✔
74
        else:
75
            return None
×
76

77
    @property
3✔
78
    def transcript_name(self):
3✔
79
        if self.transcript:
3✔
80
            return self.transcript.name
3✔
81
        else:
82
            return None
×
83

84
    @property
3✔
85
    def transcript_id(self):
3✔
86
        if self.transcript:
3✔
87
            return self.transcript.id
3✔
88
        else:
89
            return None
×
90

91
    # It's convenient to have a property which tells us:
92
    # 1) is this a variant effect overlapping a transcript?
93
    # 2) does that transcript have a coding sequence?
94
    # 3) does the variant affect the coding sequence?
95
    modifies_coding_sequence = False
3✔
96

97
    # Additionally:
98
    # 4) Does the change to the coding sequence result in a change to the
99
    # protein sequence?
100
    modifies_protein_sequence = False
3✔
101

102
    mutant_protein_sequence = None
3✔
103

104
    # which residues in the mutant protein sequence are different from
105
    # the original?
106
    aa_mutation_start_offset = None
3✔
107
    aa_mutation_end_offset = None
3✔
108

109

110
class Intergenic(MutationEffect):
3✔
111
    """Variant has unknown effect if it occurs between genes"""
112
    short_description = "intergenic"
3✔
113

114

115
class Intragenic(MutationEffect):
3✔
116
    """
117
    Variant within boundaries of a gene but does not overlap
118
    introns or exons of any transcript. This seems very peculiar but
119
    apparently does happen sometimes, maybe some genes have two distinct sets
120
    of exons which are never simultaneously expressed?
121
    """
122
    short_description = "intragenic"
3✔
123

124
    def __init__(self, variant, gene):
3✔
125
        MutationEffect.__init__(self, variant)
3✔
126
        self.gene = gene
3✔
127

128

129
class TranscriptMutationEffect(Intragenic):
3✔
130
    def __init__(self, variant, transcript):
3✔
131
        Intragenic.__init__(self, variant, gene=transcript.gene)
3✔
132
        self.transcript = transcript
3✔
133

134
    def __str__(self):
3✔
135
        return "%s(variant=%s, transcript_name=%s, transcript_id=%s)" % (
3✔
136
            self.__class__.__name__,
137
            self.variant,
138
            self.transcript.name,
139
            self.transcript.id)
140

141

142
class Failure(TranscriptMutationEffect):
3✔
143
    """
144
    Special placeholder effect for when we want to suppress errors but still
145
    need to create a non-empty list of effects for each variant.
146
    """
147
    pass
3✔
148

149

150
class NoncodingTranscript(TranscriptMutationEffect):
3✔
151
    """
152
    Any mutation to a transcript with a non-coding biotype
153
    """
154
    short_description = "non-coding-transcript"
3✔
155

156

157
class IncompleteTranscript(TranscriptMutationEffect):
3✔
158
    """
159
    Any mutation to an incompletely annotated transcript with a coding biotype
160
    """
161
    short_description = "incomplete"
3✔
162

163

164
class FivePrimeUTR(TranscriptMutationEffect):
3✔
165
    """
166
    Any mutation to the 5' untranslated region (before the start codon) of
167
    coding transcript.
168
    """
169
    short_description = "5' UTR"
3✔
170

171

172
class ThreePrimeUTR(TranscriptMutationEffect):
3✔
173
    """
174
    Any mutation to the 3' untranslated region (after the stop codon) of
175
    coding transcript.
176
    """
177
    short_description = "3' UTR"
3✔
178

179

180
class Intronic(TranscriptMutationEffect):
3✔
181
    """
182
    Mutation in an intronic region of a coding transcript
183
    """
184
    def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
3✔
185
        TranscriptMutationEffect.__init__(self, variant, transcript)
3✔
186
        self.nearest_exon = nearest_exon
3✔
187
        self.distance_to_exon = distance_to_exon
3✔
188

189
    short_description = "intronic"
3✔
190

191

192
class SpliceSite(object):
3✔
193
    """
194
    Parent class for all splice site mutations.
195
    """
196
    pass
3✔
197

198

199
class IntronicSpliceSite(Intronic, SpliceSite):
3✔
200
    """
201
    Mutations near exon boundaries, excluding the first two and last two
202
    nucleotides in an intron, since those are  known to more confidently
203
    affect splicing and are given their own effect classes below.
204
    """
205
    def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
3✔
206
        Intronic.__init__(
3✔
207
            self, variant, transcript, nearest_exon, distance_to_exon)
208

209
    short_description = "intronic-splice-site"
3✔
210

211

212
class SpliceDonor(IntronicSpliceSite):
3✔
213
    """
214
    Mutation in the first two intron residues.
215
    """
216
    def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
3✔
UNCOV
217
        IntronicSpliceSite.__init__(
×
218
            self, variant, transcript, nearest_exon, distance_to_exon)
219

220
    short_description = "splice-donor"
3✔
221

222

223
class SpliceAcceptor(IntronicSpliceSite):
3✔
224
    """
225
    Mutation in the last two intron residues.
226
    """
227
    short_description = "splice-acceptor"
3✔
228

229

230
class Exonic(TranscriptMutationEffect):
3✔
231
    """
232
    Any mutation which affects the contents of an exon (coding region or UTRs)
233
    """
234
    pass
3✔
235

236

237
class ExonLoss(Exonic):
3✔
238
    """
239
    Deletion of one or more exons in a transcript.
240
    """
241
    def __init__(self, variant, transcript, exons):
3✔
242
        Exonic.__init__(self, variant, transcript)
3✔
243
        self.exons = exons
3✔
244

245
    def __str__(self):
3✔
246
        return "ExonLoss(%s, %s, %s)" % (
3✔
247
            self.variant,
248
            self.transcript,
249
            "+".join(str(exon) for exon in self.exons))
250

251
    short_description = "exon-loss"
3✔
252

253
    @property
3✔
254
    def modifies_protein_sequence(self):
3✔
255
        # TODO: distinguish between exon loss in the CDS and UTRs
256
        return True
3✔
257

258
    @property
3✔
259
    def modifies_coding_sequence(self):
3✔
260
        # TODO: distinguish between exon loss in the CDS and UTRs
261
        return True
3✔
262

263

264
class ExonicSpliceSite(Exonic, SpliceSite):
3✔
265
    """
266
    Mutation in the last three nucleotides before an intron
267
    or in the first nucleotide after an intron.
268
    """
269
    def __init__(self, variant, transcript, exon, alternate_effect):
3✔
270
        Exonic.__init__(self, variant, transcript)
3✔
271
        self.exon = exon
3✔
272
        self.alternate_effect = alternate_effect
3✔
273

274
    def __str__(self):
3✔
275
        return "ExonicSpliceSite(exon=%s, alternate_effect=%s)" % (
3✔
276
            self.exon,
277
            self.alternate_effect)
278

279
    short_description = "exonic-splice-site"
3✔
280

281
    @property
3✔
282
    def mutant_protein_sequence(self):
3✔
283
        """
284
        TODO: determine when exonic splice variants cause exon skipping
285
        vs. translation of the underlying modified coding sequence.
286

287
        For now just pretending like there is no effect on splicing.
288
        """
289
        return self.alternate_effect.mutant_protein_sequence
3✔
290

291
    @property
3✔
292
    def modifies_protein_sequence(self):
3✔
293
        return self.alternate_effect.modifies_protein_sequence
3✔
294

295
    @property
3✔
296
    def modifies_coding_sequence(self):
3✔
297
        return self.alternate_effect.modifies_coding_sequence
3✔
298

299

300
class CodingMutation(Exonic):
3✔
301
    """
302
    Base class for all mutations which result in a modified coding sequence.
303
    """
304
    def __str__(self):
3✔
305
        fields = [
3✔
306
            ("variant", self.variant),
307
            ("transcript_name", self.transcript.name),
308
            ("transcript_id", self.transcript.id),
309
            ("effect_description", self.short_description)
310
        ]
311
        fields_str = ", ".join([
3✔
312
            "%s=%s" % (field_name, field_value)
313
            for (field_name, field_value)
314
            in fields
315
        ])
316
        return "%s(%s)" % (self.__class__.__name__, fields_str)
3✔
317

318
    @property
3✔
319
    def modifies_coding_sequence(self):
3✔
320
        return True
3✔
321

322

323
class Silent(CodingMutation):
3✔
324
    """Mutation to an exon of a coding region which doesn't change the
325
    amino acid sequence.
326
    """
327
    def __init__(
3✔
328
            self,
329
            variant,
330
            transcript,
331
            aa_pos,
332
            aa_ref):
333
        """
334
        Parameters
335
        ----------
336
        variant : Variant
337

338
        transcript : Transcript
339

340
        aa_pos : int
341
            Offset of first synonymous codon in protein sequence
342

343
        aa_ref : str or Bio.Seq
344
            Reference amino acid(s) at offset
345
        """
346
        CodingMutation.__init__(
3✔
347
            self,
348
            variant=variant,
349
            transcript=transcript)
350
        self.aa_pos = aa_pos
3✔
351
        self.aa_ref = bio_seq_to_str(aa_ref)
3✔
352

353
    @property
3✔
354
    def short_description(self):
3✔
355
        return "silent"
3✔
356

357

358
class AlternateStartCodon(Silent):
3✔
359
    """Change to the start codon (e.g. ATG>CTG) but without changing the
360
    starting amino acid from methionine.
361
    """
362
    def __init__(
3✔
363
            self,
364
            variant,
365
            transcript,
366
            ref_codon,
367
            alt_codon):
368
        Silent.__init__(
3✔
369
            self,
370
            variant=variant,
371
            transcript=transcript,
372
            aa_pos=0,
373
            aa_ref=transcript.protein_sequence[0])
374
        self.ref_codon = bio_seq_to_str(ref_codon)
3✔
375
        self.alt_codon = bio_seq_to_str(alt_codon)
3✔
376

377
    @property
3✔
378
    def short_description(self):
3✔
379
        return "alternate-start-codon (%s>%s)" % (
3✔
380
            self.ref_codon, self.alt_codon)
381

382

383
class NonsilentCodingMutation(CodingMutation):
3✔
384
    """
385
    All coding mutations other than silent codon substitutions
386
    """
387

388
    def __init__(
3✔
389
            self,
390
            variant,
391
            transcript,
392
            aa_mutation_start_offset,
393
            aa_mutation_end_offset,
394
            aa_ref):
395
        """
396
        variant : Variant
397

398
        transcript : Transcript
399

400
        aa_mutation_start_offset : int
401
            Offset of first modified amino acid in protein (starting from 0)
402

403
        aa_mutation_end_offset : int
404
            Offset after last mutated amino acid (half-open coordinates)
405

406
        aa_ref : str
407
            Amino acid string of what used to be at aa_mutation_start_offset
408
            in the wildtype (unmutated) protein.
409
        """
410
        CodingMutation.__init__(
3✔
411
            self,
412
            variant=variant,
413
            transcript=transcript)
414
        self.aa_mutation_start_offset = aa_mutation_start_offset
3✔
415
        self.aa_mutation_end_offset = aa_mutation_end_offset
3✔
416
        self.aa_ref = bio_seq_to_str(aa_ref)
3✔
417

418
    @property
3✔
419
    def modifies_protein_sequence(self):
3✔
420
        return True
3✔
421

422

423
class StartLoss(NonsilentCodingMutation):
3✔
424
    """
425
    When a start codon is lost it's difficult to determine if there is
426
    an alternative Kozak consensus sequence (either before or after the
427
    original) from which an alternative start codon can be inferred.
428

429
    TODO:
430
        - look for downstream alternative start codon to predict
431
          new coding sequence (probably also requires matching
432
          pattern of preceding ~6nt)
433
        - If an alternative start codon is changed to ATG then
434
          we should make a StrongerStartCodon effect which is effectively
435
          silent
436
        - If ATG is changed to the two common alternative codons then
437
          we should make a WeakerStartCodon effect which is also
438
          effectively silent.
439
    """
440
    def __init__(
3✔
441
            self,
442
            variant,
443
            transcript):
444
        NonsilentCodingMutation.__init__(
3✔
445
            self,
446
            variant=variant,
447
            transcript=transcript,
448
            aa_mutation_start_offset=0,
449
            aa_mutation_end_offset=1,
450
            aa_ref=transcript.protein_sequence[0])
451

452
    @property
3✔
453
    def mutant_protein_sequence(self):
3✔
454
        # TODO: scan downstream in the cDNA sequence to predict an alternate
455
        # start codon using a Kozak sequence template
456
        return None
3✔
457

458
    @property
3✔
459
    def short_description(self):
3✔
460
        return "p.%s1? (start-loss)" % (self.transcript.protein_sequence[0],)
3✔
461

462

463
class KnownAminoAcidChange(NonsilentCodingMutation):
3✔
464
    """
465
    Coding mutations in which we can predict what the new/mutant protein
466
    sequence will be.
467
    """
468
    def __init__(
3✔
469
            self,
470
            variant,
471
            transcript,
472
            aa_mutation_start_offset,
473
            aa_ref,
474
            aa_alt):
475
        NonsilentCodingMutation.__init__(
3✔
476
            self,
477
            variant=variant,
478
            transcript=transcript,
479
            aa_mutation_start_offset=aa_mutation_start_offset,
480
            aa_mutation_end_offset=aa_mutation_start_offset + len(aa_alt),
481
            aa_ref=aa_ref)
482
        self.aa_alt = bio_seq_to_str(aa_alt)
3✔
483

484
    @property
3✔
485
    def short_description(self):
3✔
486
        if len(self.aa_ref) == 0:
3✔
487
            return "p.%dins%s" % (
3✔
488
                self.aa_mutation_start_offset,
489
                self.aa_alt)
490
        elif len(self.aa_alt) == 0:
3✔
491
            return "p.%s%ddel" % (
3✔
492
                self.aa_ref,
493
                self.aa_mutation_start_offset)
494
        else:
495
            return "p.%s%d%s" % (
3✔
496
                self.aa_ref,
497
                self.aa_mutation_start_offset + 1,
498
                self.aa_alt)
499

500
    @memoized_property
3✔
501
    def mutant_protein_sequence(self):
3✔
502
        original = self.original_protein_sequence
3✔
503
        prefix = original[:self.aa_mutation_start_offset]
3✔
504
        suffix = original[self.aa_mutation_start_offset + len(self.aa_ref):]
3✔
505
        return prefix + self.aa_alt + suffix
3✔
506

507

508
class Substitution(KnownAminoAcidChange):
3✔
509
    """
510
    Single amino acid substitution, e.g. BRAF-001 V600E
511
    """
512
    def __init__(
3✔
513
            self,
514
            variant,
515
            transcript,
516
            aa_mutation_start_offset,
517
            aa_ref,
518
            aa_alt):
519
        if len(aa_ref) != 1:
3✔
UNCOV
520
            raise ValueError(
×
521
                "Simple substitution can't have aa_ref='%s'" % (aa_ref,))
522
        if len(aa_alt) != 1:
3✔
UNCOV
523
            raise ValueError(
×
524
                "Simple substitution can't have aa_alt='%s'" % (aa_alt,))
525
        KnownAminoAcidChange.__init__(
3✔
526
            self,
527
            variant=variant,
528
            transcript=transcript,
529
            aa_mutation_start_offset=aa_mutation_start_offset,
530
            aa_ref=aa_ref,
531
            aa_alt=aa_alt)
532

533

534
class ComplexSubstitution(KnownAminoAcidChange):
3✔
535
    """
536
    In-frame substitution of multiple amino acids, e.g. TP53-002 p.391FY>QQQ
537
    Can change the length of the protein sequence but since it has
538
    non-empty ref and alt strings, is more complicated than an insertion or
539
    deletion alone.
540
    """
541
    def __init__(
3✔
542
            self,
543
            variant,
544
            transcript,
545
            aa_mutation_start_offset,
546
            aa_ref,
547
            aa_alt):
548
        if len(aa_ref) == 1 and len(aa_alt) == 1:
3✔
UNCOV
549
            raise ValueError(
×
550
                "ComplexSubstitution can't have aa_ref='%s' and aa_alt='%s'" % (
551
                    aa_ref, aa_alt))
552
        KnownAminoAcidChange.__init__(
3✔
553
            self,
554
            variant=variant,
555
            transcript=transcript,
556
            aa_mutation_start_offset=aa_mutation_start_offset,
557
            aa_ref=aa_ref,
558
            aa_alt=aa_alt)
559

560

561
class Insertion(KnownAminoAcidChange):
3✔
562
    """
563
    In-frame insertion of one or more amino acids.
564
    """
565
    def __init__(
3✔
566
            self,
567
            variant,
568
            transcript,
569
            aa_mutation_start_offset,
570
            aa_alt):
571
        KnownAminoAcidChange.__init__(
3✔
572
            self,
573
            variant=variant,
574
            transcript=transcript,
575
            aa_mutation_start_offset=aa_mutation_start_offset,
576
            aa_ref="",
577
            aa_alt=aa_alt)
578

579

580
class Deletion(KnownAminoAcidChange):
3✔
581
    """
582
    In-frame deletion of one or more amino acids.
583
    """
584

585
    def __init__(
3✔
586
            self,
587
            variant,
588
            transcript,
589
            aa_mutation_start_offset,
590
            aa_ref):
591
        KnownAminoAcidChange.__init__(
3✔
592
            self,
593
            variant=variant,
594
            transcript=transcript,
595
            aa_mutation_start_offset=aa_mutation_start_offset,
596
            aa_ref=aa_ref,
597
            aa_alt="")
598

599

600
class PrematureStop(KnownAminoAcidChange):
3✔
601
    """In-frame insertion of codons containing a stop codon. May also involve
602
    insertion/deletion/substitution of other amino acids preceding the stop."""
603
    def __init__(
3✔
604
            self,
605
            variant,
606
            transcript,
607
            aa_mutation_start_offset,
608
            aa_ref="",
609
            aa_alt=""):
610
        """
611
        Insertion of premature stop codon, possibly preceded by a substitution
612
        of `aa_ref` amino acids for `aa_alt` alternative residues.
613
        """
614
        if "*" in aa_ref:
3✔
UNCOV
615
            raise ValueError(
×
616
                ("Unexpected aa_ref = '%s', should only include amino acids "
617
                 "before the new stop codon.") % aa_ref)
618
        if "*" in aa_alt:
3✔
UNCOV
619
            raise ValueError(
×
620
                ("Unexpected aa_ref = '%s', should only include amino acids "
621
                 "before the new stop codon.") % aa_alt)
622
        KnownAminoAcidChange.__init__(
3✔
623
            self,
624
            variant,
625
            transcript,
626
            aa_mutation_start_offset=aa_mutation_start_offset,
627
            aa_ref=aa_ref,
628
            aa_alt=aa_alt)
629
        self.stop_codon_offset = aa_mutation_start_offset + len(aa_alt)
3✔
630

631
        if self.stop_codon_offset >= len(transcript.protein_sequence):
3✔
UNCOV
632
            raise ValueError(
×
633
                ("Premature stop codon cannot be at position %d"
634
                 " since the original protein of %s has length %d") % (
635
                    self.stop_codon_offset,
636
                    transcript,
637
                    len(transcript.protein_sequence)))
638

639
    @property
3✔
640
    def short_description(self):
3✔
641
        return "p.%s%d%s*" % (
3✔
642
            self.aa_ref,
643
            self.aa_mutation_start_offset + 1,
644
            self.aa_alt)
645

646
    @memoized_property
3✔
647
    def mutant_protein_sequence(self):
3✔
648
        prefix = self.original_protein_sequence[:self.aa_mutation_start_offset]
3✔
649
        return prefix + self.aa_alt
3✔
650

651

652
class StopLoss(KnownAminoAcidChange):
3✔
653
    def __init__(
3✔
654
            self,
655
            variant,
656
            transcript,
657
            aa_ref,
658
            aa_alt):
659
        # StopLoss assumes that we deleted some codons ending with a
660
        # stop codon
661
        if "*" in aa_ref:
3✔
UNCOV
662
            raise ValueError(
×
663
                "StopLoss aa_ref '%s' should not contain '*'" % (
664
                    aa_ref,))
665
        if len(aa_alt) == 0:
3✔
UNCOV
666
            raise ValueError(
×
667
                "If no amino acids added by StopLoss then it should be Silent")
668
        # subtract 1 for the stop codon
669
        n_ref_amino_acids = len(aa_ref)
3✔
670
        protein_length = len(transcript.protein_sequence)
3✔
671
        aa_mutation_start_offset = protein_length - n_ref_amino_acids
3✔
672
        KnownAminoAcidChange.__init__(
3✔
673
            self,
674
            variant,
675
            transcript,
676
            aa_mutation_start_offset=aa_mutation_start_offset,
677
            aa_alt=aa_alt,
678
            aa_ref=aa_ref)
679

680
    @property
3✔
681
    def extended_protein_sequence(self):
3✔
682
        """Deprecated name for aa_alt"""
683
        return self.aa_alt
3✔
684

685
    @property
3✔
686
    def short_description(self):
3✔
687
        return "p.%s*%d%s (stop-loss)" % (
3✔
688
            self.aa_ref,
689
            self.aa_mutation_start_offset + 1,
690
            self.extended_protein_sequence)
691

692

693
class FrameShift(KnownAminoAcidChange):
3✔
694
    def __init__(
3✔
695
            self,
696
            variant,
697
            transcript,
698
            aa_mutation_start_offset,
699
            shifted_sequence):
700
        """Frameshift mutation preserves all the amino acids up to
701
        aa_mutation_start_offset and then replaces the rest of the protein with
702
        new (frameshifted) sequence. Unlike an insertion, where we denote with
703
        aa_ref as the chracter before the variant sequence, a frameshift starts
704
        at aa_ref.
705
        """
706
        aa_ref = transcript.protein_sequence[aa_mutation_start_offset:]
3✔
707
        KnownAminoAcidChange.__init__(
3✔
708
            self,
709
            variant=variant,
710
            transcript=transcript,
711
            aa_mutation_start_offset=aa_mutation_start_offset,
712
            aa_ref=aa_ref,
713
            aa_alt=shifted_sequence)
714

715
    @property
3✔
716
    def shifted_sequence(self):
3✔
717
        return self.aa_alt
3✔
718

719
    @memoized_property
3✔
720
    def mutant_protein_sequence(self):
3✔
721
        original_aa_sequence = self.original_protein_sequence
3✔
722
        prefix = original_aa_sequence[:self.aa_mutation_start_offset]
3✔
723
        return prefix + self.shifted_sequence
3✔
724

725
    @property
3✔
726
    def short_description(self):
3✔
727
        return "p.%s%dfs" % (
3✔
728
            self.aa_ref[0],
729
            self.aa_mutation_start_offset + 1)
730

731

732
class FrameShiftTruncation(PrematureStop, FrameShift):
3✔
733
    """
734
    A frame-shift mutation which immediately introduces a stop codon.
735
    """
736
    def __init__(
3✔
737
            self,
738
            variant,
739
            transcript,
740
            stop_codon_offset,
741
            aa_ref=""):
742
        PrematureStop.__init__(
3✔
743
            self,
744
            variant=variant,
745
            transcript=transcript,
746
            aa_mutation_start_offset=stop_codon_offset,
747
            aa_ref=aa_ref,
748
            aa_alt="")
749

750
    @property
3✔
751
    def shifted_sequence(self):
3✔
UNCOV
752
        return self.aa_alt
×
753

754
    @property
3✔
755
    def short_description(self):
3✔
756
        return "p.%s%dfs*" % (
3✔
757
            self.aa_ref,
758
            self.aa_mutation_start_offset + 1)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc