Skip to content

Effect types

Every effect class varcode can attach to a (variant, transcript) pair, auto-generated from the source docstrings in varcode.effects.effect_classes — so this list never drifts from the code.

Two distinctions are worth keeping in mind while reading:

  • Effects that carry a protein consequence vs. location-only effects. Coding effects (Substitution, FrameShift, PrematureStop, …) and the splice mechanism effects (NormalSplicing, ExonSkipping, …) describe a change to the protein. The splice-signal disruption effects (SpliceDonor, SpliceAcceptor, IntronicSpliceSite, ExonicSpliceSite, all sharing the SpliceSite base) and the region effects (Intronic, FivePrimeUTR, …) describe where a variant landed and carry no protein consequence on their own.
  • Single effects vs. multi-outcome containers. Most effects are a single answer. MultiOutcomeEffect subclasses (SpliceOutcomeSet, ExonicSpliceSite, the structural-variant effects, HaplotypeEffect, PhaseCandidateSet) bundle several candidate effects when the protein-level outcome isn't deterministic; each exposes .candidates, .most_likely_effect, and .highest_priority_effect.

For a grouped quick-reference, see the Effect Types table in the README. Severity ordering across types is set by effect_priority.

varcode.effects.effect_classes

MutationEffect(variant)

Bases: Serializable

Base class for mutation effects.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant):
    self.variant = variant

short_description property

A short but human-readable description of the effect. Defaults to class name for most of the non-coding effects, but is more informative for coding ones.

original_protein_sequence property

Amino acid sequence of a coding transcript (without the nucleotide variant/mutation)

__lt__(other)

Effects are ordered by their associated variants, which have comparison implement in terms of their chromosomal locations.

Source code in varcode/effects/effect_classes.py
def __lt__(self, other):
    """
    Effects are ordered by their associated variants, which have
    comparison implement in terms of their chromosomal locations.
    """
    return self.variant < other.variant

MultiOutcomeEffect(variant)

Bases: MutationEffect

Marker base class for effects that represent a set of plausible outcomes rather than a single deterministic effect.

Subclasses must expose:

  • :attr:candidates — tuple of :class:~varcode.effect_candidates.EffectCandidate objects in producer order. Each entry pairs an inner :class:MutationEffect (concrete or placeholder) with its provenance — source (producer) and evidence dict. The :attr:effects helper unwraps to the inner Effects when callers don't need provenance.
  • :attr:priority_class — effect class whose priority this set adopts (read by :func:varcode.effects.effect_priority).

Downstream consumers filter for multi-outcome results with isinstance(effect, MultiOutcomeEffect), so new wrappers (RNA evidence #259, germline-aware #268, SV-at-breakpoint) implement the same protocol uniformly (#382).

External integrations (RNA evidence, SpliceAI scoring, etc.) attach extra candidates post-hoc via the _extra_candidates slot — subclasses that override :attr:candidates must include those extras in their returned tuple. The :meth:_combine_with_extra_candidates helper does the right thing.

Picking the candidate

Two orthogonal "best candidate" notions are available; pick the one that matches your question:

  • Most likely: the first candidate after producer ordering. Producers preserve their own deterministic order. :attr:most_likely_candidate returns the wrapped :class:EffectCandidate (provenance + inner effect); :attr:most_likely_effect returns just the inner :class:MutationEffect. Always equal to candidates[0] / effects[0].

  • Highest priority: top by varcode's effect-priority ordering (see :func:~varcode.effects.effect_priority) — the most protein-disruptive candidate regardless of producer order. :attr:highest_priority_candidate and :attr:highest_priority_effect are the analogous accessors. Use this for clinical / functional filtering ("flag if any candidate is at least a frameshift"), since a disruptive candidate sitting behind a less-disruptive primary candidate should still light up.

The two coincide when producer order and priority ranking agree, which is common but not guaranteed. Pick consciously.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant):
    self.variant = variant

effects property

Tuple of inner :class:MutationEffect objects, in :attr:candidates order. Convenience for callers that don't need per-candidate provenance — equivalent to tuple(c.effect for c in self.candidates).

most_likely_candidate property

The first :class:EffectCandidate in producer order. Pairs the inner effect with its source / evidence provenance.

For just the inner :class:MutationEffect, use :attr:most_likely_effect. For the most protein-disruptive candidate (independent of producer order), use :attr:highest_priority_candidate.

most_likely_effect property

The :class:MutationEffect of :attr:most_likely_candidate. Equivalent to most_likely_candidate.effect / effects[0] — given here so callers that don't need provenance don't have to reach through the wrapper.

highest_priority_candidate property

The :class:EffectCandidate whose inner effect has the highest :func:~varcode.effects.effect_priority (most protein-disruptive). Pure priority ranking — producer order deliberately doesn't factor in, so a frameshift sitting behind a less-disruptive primary candidate still surfaces here.

Ties on priority resolve to the first matching entry of :attr:candidates, preserving the subclass's candidate order.

Behavior is deterministic.

highest_priority_effect property

The inner :class:MutationEffect of :attr:highest_priority_candidate. Use when you want the worst-case effect for clinical / functional filtering and don't need provenance.

Intergenic(variant)

Bases: MutationEffect

Variant has unknown effect if it occurs between genes

Source code in varcode/effects/effect_classes.py
def __init__(self, variant):
    self.variant = variant

Intragenic(variant, gene)

Bases: MutationEffect

Variant within boundaries of a gene but does not overlap introns or exons of any transcript. This seems very peculiar but apparently does happen sometimes, maybe some genes have two distinct sets of exons which are never simultaneously expressed?

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, gene):
    MutationEffect.__init__(self, variant)
    self.gene = gene

Failure(variant, transcript)

Bases: TranscriptMutationEffect

Special placeholder effect for when we want to suppress errors but still need to create a non-empty list of effects for each variant.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

NoncodingTranscript(variant, transcript)

Bases: TranscriptMutationEffect

Any mutation to a transcript with a non-coding biotype

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

IncompleteTranscript(variant, transcript)

Bases: TranscriptMutationEffect

Any mutation to an incompletely annotated transcript with a coding biotype

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

FivePrimeUTR(variant, transcript)

Bases: TranscriptMutationEffect

Any mutation to the 5' untranslated region (before the start codon) of coding transcript.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

ThreePrimeUTR(variant, transcript)

Bases: TranscriptMutationEffect

Any mutation to the 3' untranslated region (after the stop codon) of coding transcript.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

Intronic(variant, transcript, nearest_exon, distance_to_exon)

Bases: TranscriptMutationEffect

Mutation in an intronic region of a coding transcript

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    self.nearest_exon = nearest_exon
    self.distance_to_exon = distance_to_exon

SpliceSite

Bases: object

Marker base for the four splice-signal disruption effects: :class:SpliceDonor, :class:SpliceAcceptor, :class:IntronicSpliceSite, and :class:ExonicSpliceSite.

A SpliceSite effect answers where a variant hit a splice signal — a position fact (which boundary, how close). It does not describe the protein consequence: from DNA alone you can't say how the spliceosome will respond. That downstream protein-level outcome is modeled separately by :class:SpliceMechanismEffect subclasses (exon skipping, intron retention, cryptic-site use, normal splicing).

So the two halves of splice handling are:

  • SpliceSitewhere the signal was disrupted.
  • SpliceMechanismEffectwhat the spliceosome does, and the protein change that results.

:func:varcode.splice_outcomes.enumerate_splice_outcomes bridges them: given a SpliceSite disruption it produces a :class:~varcode.splice_outcomes.SpliceOutcomeSet of plausible mechanisms.

IntronicSpliceSite(variant, transcript, nearest_exon, distance_to_exon)

Bases: Intronic, SpliceSite

Mutations near exon boundaries, excluding the first two and last two nucleotides in an intron, since those are known to more confidently affect splicing and are given their own effect classes below.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
    Intronic.__init__(
        self, variant, transcript, nearest_exon, distance_to_exon)

SpliceDonor(variant, transcript, nearest_exon, distance_to_exon)

Bases: IntronicSpliceSite

Mutation in the first two intron residues.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
    IntronicSpliceSite.__init__(
        self, variant, transcript, nearest_exon, distance_to_exon)

SpliceAcceptor(variant, transcript, nearest_exon, distance_to_exon)

Bases: IntronicSpliceSite

Mutation in the last two intron residues.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, nearest_exon, distance_to_exon):
    Intronic.__init__(
        self, variant, transcript, nearest_exon, distance_to_exon)

SpliceMechanismEffect(variant, transcript, splice_signal, protein_effect=None, mutant_transcript=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: TranscriptMutationEffect

Base for effects describing what the splice machinery does downstream of a disrupted splice signal.

Concrete subclasses: :class:NormalSplicing, :class:ExonSkipping, :class:IntronRetention, :class:CrypticDonor, :class:CrypticAcceptor. Class identity is the mechanism; instances of this base are not constructed directly.

Protein vocab fields are optional — None means "predicted mechanism, exact protein not computable from cached cDNA alone." When resolved, the fields follow the same conventions as :class:KnownAminoAcidChange so consumers can read them uniformly.

PARAMETER DESCRIPTION
variant

TYPE: Variant

transcript

TYPE: Transcript

splice_signal

The splice-signal-disruption Effect instance describing where the disruption was — a :class:SpliceSite (one of SpliceDonor / SpliceAcceptor / IntronicSpliceSite / ExonicSpliceSite). Lets consumers reach effect.splice_signal.nearest_exon, effect.splice_signal.distance_to_exon, etc. without carrying those fields redundantly on every mechanism subclass.

TYPE: SpliceSite

protein_effect

Classified protein-level consequence of this splice mechanism (e.g. Deletion, FrameShift, PrematureStop) when the protein sequence was resolved. The mechanism object keeps its own class identity while delegating severity metadata to this classified consequence.

TYPE: MutationEffect or None DEFAULT: None

mutant_transcript

TYPE: MutantTranscript or None DEFAULT: None

aa_ref

TYPE: str or None DEFAULT: None

aa_alt

TYPE: str or None DEFAULT: None

aa_mutation_start_offset

TYPE: int or None DEFAULT: None

aa_mutation_end_offset

TYPE: int or None DEFAULT: None

mutant_protein_sequence

TYPE: str or None DEFAULT: None

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript, splice_signal,
        protein_effect=None,
        mutant_transcript=None,
        aa_ref=None, aa_alt=None,
        aa_mutation_start_offset=None,
        aa_mutation_end_offset=None,
        mutant_protein_sequence=None):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    self.splice_signal = splice_signal
    self.protein_effect = protein_effect
    self.mutant_transcript = mutant_transcript
    self.aa_ref = aa_ref
    self.aa_alt = aa_alt
    self.aa_mutation_start_offset = aa_mutation_start_offset
    self.aa_mutation_end_offset = aa_mutation_end_offset
    self.mutant_protein_sequence = mutant_protein_sequence

resolved: bool property

True when the protein math succeeded for this mechanism — i.e. :attr:mutant_protein_sequence is populated. Convenience for consumers that want to branch on "do we have a concrete protein for this mechanism?"

NormalSplicing(variant, transcript, splice_signal, coding_effect=None)

Bases: SpliceMechanismEffect

Canonical splicing proceeds despite the disruption.

The splice signal was hit, but the spliceosome handles it; the protein consequence (if any) is whatever the underlying nucleotide change normally produces — a :class:Substitution for an :class:ExonicSpliceSite, no protein change for a purely intronic disruption. The underlying coding change is carried as :attr:coding_effect; when there isn't one (intronic variant), it's None.

Note: this class is special among :class:SpliceMechanismEffect subclasses — it doesn't itself transform the protein, so all the protein-vocab attributes (aa_ref, aa_alt, mutant_protein_sequence, mutant_transcript, aa_mutation_*_offset) are delegating descriptors that read through to coding_effect. Construction therefore calls :class:TranscriptMutationEffect's __init__ directly rather than :class:SpliceMechanismEffect's — the latter would set those names as plain instance attributes and shadow the descriptors. The other four mechanisms (skipping, retention, cryptic donor/acceptor) actually transform the protein and carry their own protein vocab as instance state.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal,
             coding_effect=None):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    self.splice_signal = splice_signal
    self.coding_effect = coding_effect

ExonSkipping(variant, transcript, splice_signal, affected_exon, in_frame, mutant_transcript=None, protein_effect=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: SpliceMechanismEffect

The affected exon is excluded from the mature transcript.

When in-frame (the exon's length is divisible by 3), the result is a clean amino-acid deletion plus an optional boundary-codon reshape. When out-of-frame, a frameshift propagates from the new exon junction through the rest of the transcript.

Parameters beyond the base class

affected_exon : pyensembl.Exon The skipped exon. in_frame : bool True when len(affected_exon) % 3 == 0 — the exon-skip is codon-aligned and the downstream sequence keeps its frame.

Note on aa_ref / aa_alt semantics

The AA-level fields come from :func:~varcode.effects.classify.classify_from_protein_diff, so what they mean depends on in_frame:

  • In-frame skip (in_frame=True): aa_ref is the contiguous run of amino acids removed from the reference protein (including the boundary-codon reshape when the exon boundary lands mid-codon). aa_alt is the reshaped boundary codon's translation (or "" for a clean codon-aligned skip).
  • Out-of-frame skip (in_frame=False): aa_ref is the reference AA at the divergence point (where the frame breaks after the new exon junction); aa_alt is the first frameshifted AA. The downstream protein continues in the new frame until a premature stop — read the full sequence from :attr:mutant_protein_sequence, not from aa_alt.
Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal, affected_exon,
             in_frame, mutant_transcript=None,
             protein_effect=None,
             aa_ref=None, aa_alt=None,
             aa_mutation_start_offset=None,
             aa_mutation_end_offset=None,
             mutant_protein_sequence=None):
    SpliceMechanismEffect.__init__(
        self, variant, transcript, splice_signal,
        protein_effect=protein_effect,
        mutant_transcript=mutant_transcript,
        aa_ref=aa_ref, aa_alt=aa_alt,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_end_offset,
        mutant_protein_sequence=mutant_protein_sequence)
    self.affected_exon = affected_exon
    self.in_frame = in_frame

IntronRetention(variant, transcript, splice_signal, retained_intron_start=None, retained_intron_end=None, side=None, mutant_transcript=None, protein_effect=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: SpliceMechanismEffect

The intron stays in the mature transcript; translation typically hits a premature stop inside the retained intron.

Parameters beyond the base class

retained_intron_start, retained_intron_end : int or None Genomic coordinates of the retained intron (1-based inclusive). Populated when the adjacent-exon geometry identifies which intron is being retained; None when that can't be determined (e.g. transcript missing the adjacent exon, splice boundary ambiguous). side : str or None "donor" or "acceptor" — which splice boundary failed, causing the intron to be retained. None when the side can't be inferred from the splice classification.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal,
             retained_intron_start=None, retained_intron_end=None,
             side=None,
             mutant_transcript=None,
             protein_effect=None,
             aa_ref=None, aa_alt=None,
             aa_mutation_start_offset=None,
             aa_mutation_end_offset=None,
             mutant_protein_sequence=None):
    SpliceMechanismEffect.__init__(
        self, variant, transcript, splice_signal,
        protein_effect=protein_effect,
        mutant_transcript=mutant_transcript,
        aa_ref=aa_ref, aa_alt=aa_alt,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_end_offset,
        mutant_protein_sequence=mutant_protein_sequence)
    self.retained_intron_start = retained_intron_start
    self.retained_intron_end = retained_intron_end
    self.side = side

CrypticSpliceSiteEffect(variant, transcript, splice_signal, affected_exon, cryptic_genomic_position=None, motif_score=None, exon_length_delta=None, mutant_transcript=None, protein_effect=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: SpliceMechanismEffect

Base for cryptic donor / acceptor effects. Carries the cryptic motif's position and score, plus the resulting exon length delta.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal, affected_exon,
             cryptic_genomic_position=None, motif_score=None,
             exon_length_delta=None,
             mutant_transcript=None,
             protein_effect=None,
             aa_ref=None, aa_alt=None,
             aa_mutation_start_offset=None,
             aa_mutation_end_offset=None,
             mutant_protein_sequence=None):
    SpliceMechanismEffect.__init__(
        self, variant, transcript, splice_signal,
        protein_effect=protein_effect,
        mutant_transcript=mutant_transcript,
        aa_ref=aa_ref, aa_alt=aa_alt,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_end_offset,
        mutant_protein_sequence=mutant_protein_sequence)
    self.affected_exon = affected_exon
    self.cryptic_genomic_position = cryptic_genomic_position
    self.motif_score = motif_score
    self.exon_length_delta = exon_length_delta

CrypticDonor(variant, transcript, splice_signal, affected_exon, cryptic_genomic_position=None, motif_score=None, exon_length_delta=None, mutant_transcript=None, protein_effect=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: CrypticSpliceSiteEffect

A cryptic GT donor is used instead of the disrupted canonical donor; the exon is extended (cryptic site downstream) or truncated (cryptic site upstream).

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal, affected_exon,
             cryptic_genomic_position=None, motif_score=None,
             exon_length_delta=None,
             mutant_transcript=None,
             protein_effect=None,
             aa_ref=None, aa_alt=None,
             aa_mutation_start_offset=None,
             aa_mutation_end_offset=None,
             mutant_protein_sequence=None):
    SpliceMechanismEffect.__init__(
        self, variant, transcript, splice_signal,
        protein_effect=protein_effect,
        mutant_transcript=mutant_transcript,
        aa_ref=aa_ref, aa_alt=aa_alt,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_end_offset,
        mutant_protein_sequence=mutant_protein_sequence)
    self.affected_exon = affected_exon
    self.cryptic_genomic_position = cryptic_genomic_position
    self.motif_score = motif_score
    self.exon_length_delta = exon_length_delta

CrypticAcceptor(variant, transcript, splice_signal, affected_exon, cryptic_genomic_position=None, motif_score=None, exon_length_delta=None, mutant_transcript=None, protein_effect=None, aa_ref=None, aa_alt=None, aa_mutation_start_offset=None, aa_mutation_end_offset=None, mutant_protein_sequence=None)

Bases: CrypticSpliceSiteEffect

A cryptic AG acceptor is used instead of the disrupted canonical acceptor; the exon is extended (cryptic site upstream) or truncated (cryptic site downstream).

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, splice_signal, affected_exon,
             cryptic_genomic_position=None, motif_score=None,
             exon_length_delta=None,
             mutant_transcript=None,
             protein_effect=None,
             aa_ref=None, aa_alt=None,
             aa_mutation_start_offset=None,
             aa_mutation_end_offset=None,
             mutant_protein_sequence=None):
    SpliceMechanismEffect.__init__(
        self, variant, transcript, splice_signal,
        protein_effect=protein_effect,
        mutant_transcript=mutant_transcript,
        aa_ref=aa_ref, aa_alt=aa_alt,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_end_offset,
        mutant_protein_sequence=mutant_protein_sequence)
    self.affected_exon = affected_exon
    self.cryptic_genomic_position = cryptic_genomic_position
    self.motif_score = motif_score
    self.exon_length_delta = exon_length_delta

Exonic(variant, transcript)

Bases: TranscriptMutationEffect

Any mutation which affects the contents of an exon (coding region or UTRs)

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

ExonLoss(variant, transcript, exons)

Bases: Exonic

Deletion of one or more exons in a transcript.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, exons):
    Exonic.__init__(self, variant, transcript)
    self.exons = exons

ExonicSpliceSite(variant, transcript, exon, alternate_effect)

Bases: Exonic, SpliceSite, MultiOutcomeEffect

Mutation in the last three nucleotides before an intron or in the first nucleotide after an intron.

Expresses the two plausible outcomes of a splice-adjacent exonic variant — splice-signal disruption vs. the underlying coding change if splicing proceeds normally — via the :class:MultiOutcomeEffect protocol (#299, #382). :attr:candidates yields a 2-tuple of :class:~varcode.effect_candidates.EffectCandidate objects; alternate_effect stays on the instance as a first-class field for back-compat with callers that depended on it.

Note: as of varcode 6.0, ExonicSpliceSite no longer surfaces as the top-level effect at the user-facing API — splice-disrupting variants are always wrapped in :class:~varcode.splice_outcomes.SpliceOutcomeSet. ExonicSpliceSite survives as splice_set.disrupted_signal_class (a type) and as the splice_signal reference on each candidate (an instance).

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript, exon, alternate_effect):
    Exonic.__init__(self, variant, transcript)
    self.exon = exon
    self.alternate_effect = alternate_effect

mutant_protein_sequence property

TODO: determine when exonic splice variants cause exon skipping vs. translation of the underlying modified coding sequence.

For now just pretending like there is no effect on splicing.

candidates property

The two plausible outcomes wrapped as :class:~varcode.effect_candidates.EffectCandidate:

Position [0] is the splice-disruption outcome (this effect itself); position [1] is the coding change if splicing proceeds. Returns a 1-tuple when alternate_effect is None. Extra candidates attached via :meth:_combine_with_extra_candidates (e.g. from RNA evidence) come after.

priority_class property

ExonicSpliceSite priority is used directly — no delegation, since this class IS the splice-adjacent effect rather than a wrapper around one.

CodingMutation(variant, transcript)

Bases: Exonic

Base class for all mutations which result in a modified coding sequence.

Source code in varcode/effects/effect_classes.py
def __init__(self, variant, transcript):
    Intragenic.__init__(self, variant, gene=transcript.gene)
    self.transcript = transcript

Silent(variant, transcript, aa_pos, aa_ref)

Bases: CodingMutation

Mutation to an exon of a coding region which doesn't change the amino acid sequence.

PARAMETER DESCRIPTION
variant

TYPE: Variant

transcript

TYPE: Transcript

aa_pos

Offset of first synonymous codon in protein sequence

TYPE: int

aa_ref

Reference amino acid(s) at offset

TYPE: str or Seq

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_pos,
        aa_ref):
    """
    Parameters
    ----------
    variant : Variant

    transcript : Transcript

    aa_pos : int
        Offset of first synonymous codon in protein sequence

    aa_ref : str or Bio.Seq
        Reference amino acid(s) at offset
    """
    CodingMutation.__init__(
        self,
        variant=variant,
        transcript=transcript)
    self.aa_pos = aa_pos
    self.aa_ref = bio_seq_to_str(aa_ref)

AlternateStartCodon(variant, transcript, ref_codon, alt_codon)

Bases: Silent

Change to the start codon (e.g. ATG>CTG) but without changing the starting amino acid from methionine.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        ref_codon,
        alt_codon):
    Silent.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_pos=0,
        aa_ref=transcript.protein_sequence[0])
    self.ref_codon = bio_seq_to_str(ref_codon)
    self.alt_codon = bio_seq_to_str(alt_codon)

NonsilentCodingMutation(variant, transcript, aa_mutation_start_offset, aa_mutation_end_offset, aa_ref)

Bases: CodingMutation

All coding mutations other than silent codon substitutions

variant : Variant

transcript : Transcript

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

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

aa_ref : str Amino acid string of what used to be at aa_mutation_start_offset in the wildtype (unmutated) protein.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_mutation_end_offset,
        aa_ref):
    """
    variant : Variant

    transcript : Transcript

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

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

    aa_ref : str
        Amino acid string of what used to be at aa_mutation_start_offset
        in the wildtype (unmutated) protein.
    """
    CodingMutation.__init__(
        self,
        variant=variant,
        transcript=transcript)
    self.aa_mutation_start_offset = aa_mutation_start_offset
    self.aa_mutation_end_offset = aa_mutation_end_offset
    self.aa_ref = bio_seq_to_str(aa_ref)

StartLoss(variant, transcript)

Bases: NonsilentCodingMutation

When a start codon is lost it's difficult to determine if there is an alternative Kozak consensus sequence (either before or after the original) from which an alternative start codon can be inferred.

TODO: - look for downstream alternative start codon to predict new coding sequence (probably also requires matching pattern of preceding ~6nt) - If an alternative start codon is changed to ATG then we should make a StrongerStartCodon effect which is effectively silent - If ATG is changed to the two common alternative codons then we should make a WeakerStartCodon effect which is also effectively silent.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript):
    NonsilentCodingMutation.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=0,
        aa_mutation_end_offset=1,
        aa_ref=transcript.protein_sequence[0])

KnownAminoAcidChange(variant, transcript, aa_mutation_start_offset, aa_ref, aa_alt)

Bases: NonsilentCodingMutation

Coding mutations in which we can predict what the new/mutant protein sequence will be.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_ref,
        aa_alt):
    NonsilentCodingMutation.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_mutation_end_offset=aa_mutation_start_offset + len(aa_alt),
        aa_ref=aa_ref)
    self.aa_alt = bio_seq_to_str(aa_alt)

Substitution(variant, transcript, aa_mutation_start_offset, aa_ref, aa_alt)

Bases: KnownAminoAcidChange

Single amino acid substitution, e.g. BRAF-001 V600E

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_ref,
        aa_alt):
    if len(aa_ref) != 1:
        raise ValueError(
            "Simple substitution can't have aa_ref='%s'" % (aa_ref,))
    if len(aa_alt) != 1:
        raise ValueError(
            "Simple substitution can't have aa_alt='%s'" % (aa_alt,))
    KnownAminoAcidChange.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref=aa_ref,
        aa_alt=aa_alt)

ComplexSubstitution(variant, transcript, aa_mutation_start_offset, aa_ref, aa_alt)

Bases: KnownAminoAcidChange

In-frame substitution of multiple amino acids, e.g. TP53-002 p.391FY>QQQ Can change the length of the protein sequence but since it has non-empty ref and alt strings, is more complicated than an insertion or deletion alone.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_ref,
        aa_alt):
    if len(aa_ref) == 1 and len(aa_alt) == 1:
        raise ValueError(
            "ComplexSubstitution can't have aa_ref='%s' and aa_alt='%s'" % (
                aa_ref, aa_alt))
    KnownAminoAcidChange.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref=aa_ref,
        aa_alt=aa_alt)

Insertion(variant, transcript, aa_mutation_start_offset, aa_alt)

Bases: KnownAminoAcidChange

In-frame insertion of one or more amino acids.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_alt):
    KnownAminoAcidChange.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref="",
        aa_alt=aa_alt)

Deletion(variant, transcript, aa_mutation_start_offset, aa_ref)

Bases: KnownAminoAcidChange

In-frame deletion of one or more amino acids.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_ref):
    KnownAminoAcidChange.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref=aa_ref,
        aa_alt="")

PrematureStop(variant, transcript, aa_mutation_start_offset, aa_ref='', aa_alt='')

Bases: KnownAminoAcidChange

In-frame insertion of codons containing a stop codon. May also involve insertion/deletion/substitution of other amino acids preceding the stop.

Insertion of premature stop codon, possibly preceded by a substitution of aa_ref amino acids for aa_alt alternative residues.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        aa_ref="",
        aa_alt=""):
    """
    Insertion of premature stop codon, possibly preceded by a substitution
    of `aa_ref` amino acids for `aa_alt` alternative residues.
    """
    if "*" in aa_ref:
        raise ValueError(
            ("Unexpected aa_ref = '%s', should only include amino acids "
             "before the new stop codon.") % aa_ref)
    if "*" in aa_alt:
        raise ValueError(
            ("Unexpected aa_ref = '%s', should only include amino acids "
             "before the new stop codon.") % aa_alt)
    KnownAminoAcidChange.__init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref=aa_ref,
        aa_alt=aa_alt)
    self.stop_codon_offset = aa_mutation_start_offset + len(aa_alt)

    if self.stop_codon_offset >= len(transcript.protein_sequence):
        raise ValueError(
            ("Premature stop codon cannot be at position %d"
             " since the original protein of %s has length %d") % (
                self.stop_codon_offset,
                transcript,
                len(transcript.protein_sequence)))

StopLoss(variant, transcript, aa_ref, aa_alt)

Bases: KnownAminoAcidChange

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_ref,
        aa_alt):
    # StopLoss assumes that we deleted some codons ending with a
    # stop codon
    if "*" in aa_ref:
        raise ValueError(
            "StopLoss aa_ref '%s' should not contain '*'" % (
                aa_ref,))
    if len(aa_alt) == 0:
        raise ValueError(
            "If no amino acids added by StopLoss then it should be Silent")
    # subtract 1 for the stop codon
    n_ref_amino_acids = len(aa_ref)
    protein_length = len(transcript.protein_sequence)
    aa_mutation_start_offset = protein_length - n_ref_amino_acids
    KnownAminoAcidChange.__init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_alt=aa_alt,
        aa_ref=aa_ref)

extended_protein_sequence property

Deprecated name for aa_alt

FrameShift(variant, transcript, aa_mutation_start_offset, shifted_sequence)

Bases: KnownAminoAcidChange

Frameshift mutation preserves all the amino acids up to aa_mutation_start_offset and then replaces the rest of the protein with new (frameshifted) sequence. Unlike an insertion, where we denote with aa_ref as the chracter before the variant sequence, a frameshift starts at aa_ref.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        aa_mutation_start_offset,
        shifted_sequence):
    """Frameshift mutation preserves all the amino acids up to
    aa_mutation_start_offset and then replaces the rest of the protein with
    new (frameshifted) sequence. Unlike an insertion, where we denote with
    aa_ref as the chracter before the variant sequence, a frameshift starts
    at aa_ref.
    """
    aa_ref = transcript.protein_sequence[aa_mutation_start_offset:]
    KnownAminoAcidChange.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=aa_mutation_start_offset,
        aa_ref=aa_ref,
        aa_alt=shifted_sequence)

FrameShiftTruncation(variant, transcript, stop_codon_offset, aa_ref='')

Bases: PrematureStop, FrameShift

A frame-shift mutation which immediately introduces a stop codon.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        stop_codon_offset,
        aa_ref=""):
    PrematureStop.__init__(
        self,
        variant=variant,
        transcript=transcript,
        aa_mutation_start_offset=stop_codon_offset,
        aa_ref=aa_ref,
        aa_alt="")

StructuralVariantEffect(variant, transcript, primary_effects=None, mutant_transcript=None)

Bases: TranscriptMutationEffect, MultiOutcomeEffect

Base class for effects of a :class:StructuralVariant on a specific transcript. Subclasses set short_description; the :attr:candidates tuple is what downstream callers read.

:attr:candidates always returns a tuple of :class:~varcode.effect_candidates.EffectCandidate objects. Primary candidates carry source="varcode"; cryptic-exon candidates attached via :meth:_attach_cryptic_candidates carry source="varcode_motif"; splice-outcome candidates attached via :meth:_attach_splice_outcomes carry source="varcode_splice".

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript,
        primary_effects=None, mutant_transcript=None):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    # Primary classifications: either the subclass's own effect
    # (``self``) or a caller-supplied tuple of
    # :class:`MutationEffect` instances. Lifted into
    # :class:`EffectCandidate` shape on access.
    self._primary_effects = (
        tuple(primary_effects) if primary_effects is not None
        else (self,))
    self.mutant_transcript = mutant_transcript
    # Cryptic-exon candidates nominated by
    # :func:`varcode.cryptic_exons.enumerate_from_structural_variant`
    # (#337). Kept separate from primary effects because their
    # entries carry different source / evidence.
    self._cryptic_candidates = ()
    # Splice-outcome candidates attached by the SV annotator when
    # an SV breakpoint lands in a canonical splice window (#341).
    # Pre-constructed :class:`~varcode.EffectCandidate` tuples; the
    # annotator re-sources them as ``"varcode_splice"`` and
    # enriches evidence with ``sv_type`` before attaching.
    self._splice_candidates = ()

candidates property

Unified :class:~varcode.EffectCandidate view over primary SV classifications, attached cryptic candidates, and any splice-outcome candidates (#339, #337, #341, #382).

Primary candidates carry source="varcode"; cryptic-exon candidates carry source="varcode_motif"; splice-outcome candidates carry source="varcode_splice". Each source marks provenance so external scorers (SpliceAI, Pangolin, RNA evidence) can filter before rescoring.

LargeDeletion(variant, transcript, affected_exons, primary_effects=None, mutant_transcript=None)

Bases: StructuralVariantEffect

A deletion (<DEL> / <CN0>) that removes one or more exons — or an entire gene. Carries the list of affected exons for downstream analysis; the single outcome is this effect itself (callers that want to add RNA evidence or SpliceAI scoring construct additional outcomes and wrap the result).

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript, affected_exons,
        primary_effects=None, mutant_transcript=None):
    StructuralVariantEffect.__init__(
        self, variant, transcript,
        primary_effects=primary_effects,
        mutant_transcript=mutant_transcript)
    self.affected_exons = tuple(affected_exons)

LargeDuplication(variant, transcript, affected_exons, primary_effects=None, mutant_transcript=None)

Bases: StructuralVariantEffect

A tandem duplication (<DUP>) overlapping exons. Biologically may produce a copy-number increase, a fused reading frame if junctions land in-frame, or a regulatory effect. Varcode reports the affected exons and leaves scoring to downstream tools.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript, affected_exons,
        primary_effects=None, mutant_transcript=None):
    StructuralVariantEffect.__init__(
        self, variant, transcript,
        primary_effects=primary_effects,
        mutant_transcript=mutant_transcript)
    self.affected_exons = tuple(affected_exons)

Inversion(variant, transcript, primary_effects=None, mutant_transcript=None)

Bases: StructuralVariantEffect

An inversion (<INV>) that flips a stretch of a transcript. Depending on whether breakpoints fall in exons or introns, the consequence is very different (exonic: likely disruptive; purely intronic: may or may not affect splicing). Reported as a single outcome here; subclasses / callers can enrich with cryptic- splice candidates per PR 11.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript,
        primary_effects=None, mutant_transcript=None):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    # Primary classifications: either the subclass's own effect
    # (``self``) or a caller-supplied tuple of
    # :class:`MutationEffect` instances. Lifted into
    # :class:`EffectCandidate` shape on access.
    self._primary_effects = (
        tuple(primary_effects) if primary_effects is not None
        else (self,))
    self.mutant_transcript = mutant_transcript
    # Cryptic-exon candidates nominated by
    # :func:`varcode.cryptic_exons.enumerate_from_structural_variant`
    # (#337). Kept separate from primary effects because their
    # entries carry different source / evidence.
    self._cryptic_candidates = ()
    # Splice-outcome candidates attached by the SV annotator when
    # an SV breakpoint lands in a canonical splice window (#341).
    # Pre-constructed :class:`~varcode.EffectCandidate` tuples; the
    # annotator re-sources them as ``"varcode_splice"`` and
    # enriches evidence with ``sv_type`` before attaching.
    self._splice_candidates = ()

GeneFusion(variant, transcript, partner_transcript, mutant_transcript=None, primary_effects=None)

Bases: StructuralVariantEffect

A breakend (<BND>) whose mate lies in another protein-coding gene — the canonical fusion shape.

Carries the two partner transcripts (5' and 3') and, when the annotator has enough context, a :class:MutantTranscript built from :class:ReferenceSegment entries describing the fused allele. Predicting the exact fused-protein sequence requires knowing which exons are retained, which typically needs RNA evidence — outcomes beyond "this is a plausible fusion" are left to downstream tools that attach :class:EffectCandidate objects with their own producer source tag.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript, partner_transcript,
        mutant_transcript=None, primary_effects=None):
    StructuralVariantEffect.__init__(
        self, variant, transcript,
        primary_effects=primary_effects,
        mutant_transcript=mutant_transcript)
    self.partner_transcript = partner_transcript

TranslocationToIntergenic(variant, transcript, primary_effects=None, mutant_transcript=None)

Bases: StructuralVariantEffect

A breakend whose mate lies in intergenic space. The downstream consequence depends on whether the intergenic region contains cryptic splice / ORF signals — reported as a single outcome here, with PR 11's cryptic-exon enumerator adding candidate outcomes when applicable.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, transcript,
        primary_effects=None, mutant_transcript=None):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    # Primary classifications: either the subclass's own effect
    # (``self``) or a caller-supplied tuple of
    # :class:`MutationEffect` instances. Lifted into
    # :class:`EffectCandidate` shape on access.
    self._primary_effects = (
        tuple(primary_effects) if primary_effects is not None
        else (self,))
    self.mutant_transcript = mutant_transcript
    # Cryptic-exon candidates nominated by
    # :func:`varcode.cryptic_exons.enumerate_from_structural_variant`
    # (#337). Kept separate from primary effects because their
    # entries carry different source / evidence.
    self._cryptic_candidates = ()
    # Splice-outcome candidates attached by the SV annotator when
    # an SV breakpoint lands in a canonical splice window (#341).
    # Pre-constructed :class:`~varcode.EffectCandidate` tuples; the
    # annotator re-sources them as ``"varcode_splice"`` and
    # enriches evidence with ``sv_type`` before attaching.
    self._splice_candidates = ()

CrypticExonCandidate(variant, contig, interval_start, interval_end, donor_score=None, acceptor_score=None)

Bases: MutationEffect

A region where an SV has brought novel sequence into range of the transcript, and motif scoring flags a plausible new splice acceptor / donor pair. Produced by PR 11's cryptic-exon enumerator; attached as additional :class:EffectCandidate entries on SV effects rather than standalone.

Not a :class:TranscriptMutationEffect because the candidate region may not overlap any existing transcript — it's a new exon hypothesis. Carries the contig / interval and the motif scores as plain fields; external predictors (SpliceAI, Pangolin) attach their own scores via the enclosing :class:EffectCandidate.evidence dict.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variant, contig, interval_start, interval_end,
        donor_score=None, acceptor_score=None):
    MutationEffect.__init__(self, variant)
    self.contig = contig
    self.interval_start = interval_start
    self.interval_end = interval_end
    self.donor_score = donor_score
    self.acceptor_score = acceptor_score

HaplotypeEffect(variants, transcript, mutant_transcript, phase_source=None)

Bases: TranscriptMutationEffect, MultiOutcomeEffect

Joint effect of two or more cis variants on the same transcript (#269).

Emitted by :meth:VariantCollection.effects when a phase_resolver (VCF PS tag, Isovar assembly, or any other :class:PhaseResolver) groups cis variants together and :func:varcode.mutant_transcript.apply_variants_to_transcript can build a combined mutant cDNA. The per-variant effects stay on the collection — this is additive, not a replacement.

Downstream consumers that want haplotype-level context (peptide prediction, ASE, compound het interpretation) read the :attr:mutant_transcript here. Consumers that want per-variant HGVS or annotation granularity iterate the per-variant effects as before.

Source code in varcode/effects/effect_classes.py
def __init__(
        self, variants, transcript, mutant_transcript,
        phase_source=None):
    # The HaplotypeEffect is scoped to a set of variants — the
    # :class:`MutationEffect.variant` slot takes the first one
    # as a back-compat anchor so existing single-variant
    # consumers don't blow up, but real consumers read
    # :attr:`variants`.
    assert len(variants) >= 2, (
        "HaplotypeEffect requires ≥ 2 cis variants; got %d" % len(variants))
    TranscriptMutationEffect.__init__(self, variants[0], transcript)
    self.variants = tuple(variants)
    self.mutant_transcript = mutant_transcript
    # Which resolver produced the phase grouping (e.g. "vcf_ps"
    # for DNA-PS-tag phasing, "read_phasing" for an RNA / long-
    # read source). Useful for downstream filtering and for
    # auditing whether the cis call came from DNA-only phasing
    # or RNA-assembly evidence.
    self.phase_source = phase_source

candidates property

Single-outcome wrapping: the haplotype IS the outcome, lifted into the uniform :class:EffectCandidate shape so consumers iterate :attr:candidates the same way across all :class:MultiOutcomeEffect subclasses (#382). Extra candidates attached via :meth:_combine_with_extra_candidates (e.g. from RNA evidence) come after.

mutant_protein_sequence property

Joint protein translated from the haplotype's cDNA, or None if the edits don't land after the CDS start.

short_description property

HGVS haplotype notation: [c.1A>T;c.5G>C] for cis.

Per-variant short descriptions are synthesized from each variant's genomic coords when the per-variant effect isn't available here — the transcript-level HGVS would require re-running the per-variant classifier, which HaplotypeEffect deliberately doesn't do (that work lives on the per-variant effects that coexist in the collection).

PhaseCandidateSet(variant, transcript, candidates, hypotheses, germline_variants)

Bases: TranscriptMutationEffect, MultiOutcomeEffect

Possibility set across phase hypotheses when a somatic variant and one or more germline variants share a window on a transcript and phase between them is unknown.

The somatic effect at this locus depends on which haplotype the somatic landed on, and without phase data we can't say which. The honest output is the set of plausible effects, one per hypothesis. most_likely returns the highest-priority candidate; :attr:candidates exposes the full set with per-hypothesis evidence keys (phase_state, haplotype, germline_variants) so consumers — including RNA-evidence integrations from #259 — can align across axes.

Emitted by :func:varcode.germline.predict_germline_aware_effect. Sibling of :class:HaplotypeEffect: that one captures the known-cis multi-variant case; this one captures the unknown-phase possibility set.

Source code in varcode/effects/effect_classes.py
def __init__(
        self,
        variant,
        transcript,
        candidates,
        hypotheses,
        germline_variants):
    TranscriptMutationEffect.__init__(self, variant, transcript)
    if len(candidates) != len(hypotheses):
        raise ValueError(
            "PhaseCandidateSet needs one candidate per "
            "hypothesis; got %d candidates and %d hypotheses." % (
                len(candidates), len(hypotheses)))
    if not candidates:
        raise ValueError(
            "PhaseCandidateSet requires at least one candidate.")
    self._candidates_raw = tuple(candidates)
    self._hypotheses = tuple(hypotheses)
    self.germline_variants = tuple(germline_variants)

candidates property

One :class:EffectCandidate per hypothesis, sorted by underlying effect priority (most-severe first), carrying the phase metadata needed to align with RNA-evidence outcomes (#259, #382).

evidence keys: phase_state ("phased" / "implicit" / "unknown" / "too_many_hypotheses"), haplotype (opaque tag), germline_variants (tuple of the cis germline variants on that hypothesis's haplotype).

short_description property

"?<most_likely_effect>" — the leading ? flags the ambiguity. Consumers wanting the full possibility set read :attr:candidates.

mutant_protein_sequence property

Most-likely candidate's mutant protein. Consumers iterating over hypotheses pull per-candidate sequences from :attr:candidates.