Source code for ASOkai.Antisense._gapmer

#!/usr/bin/env python
"""
Filename: src/ASOkai/Antisense/_gapmer.py
Author: Arash Ayat
Copyright: 2026, Alexander Schliep
Description: This file defines the Gapmer class.
License: LGPL-3.0-or-later
"""

from Bio.Seq import Seq

from ..Biochemistry import Chemistry, MechanismOfAction
from ._aso import ASO


[docs] class Gapmer(ASO): """An ASO with a wing-gap-wing design.""" left_wing_length: int gap_length: int right_wing_length: int def __init__( self, sequence: Seq, mechanism: MechanismOfAction, left_wing_length: int, gap_length: int, right_wing_length: int, *, chemistry: Chemistry | None = None, id: str | None = None, **kwargs, ): """Initialize a gapmer. Args: id: Optional stored ID, checked against the design-derived ID. sequence: Antisense-strand sequence in the 5' to 3' direction. mechanism: Mechanism through which the gapmer acts. left_wing_length: Number of nucleotides in the left wing. gap_length: Number of nucleotides in the central gap. right_wing_length: Number of nucleotides in the right wing. chemistry: Optional chemistry of this strand. kwargs: Additional serializable metadata attributes. Raises: ValueError: If a segment length is not a positive integer or the segment lengths do not sum to the sequence length. """ self.left_wing_length = _positive_length( "left_wing_length", left_wing_length, ) self.gap_length = _positive_length("gap_length", gap_length) self.right_wing_length = _positive_length( "right_wing_length", right_wing_length, ) design_length = sum(self.lengths) if design_length != len(sequence): raise ValueError( "Gapmer segment lengths must equal the sequence length " f"({len(sequence)}); received {design_length}." ) super().__init__( id=id, sequence=sequence, mechanism=mechanism, chemistry=chemistry, **kwargs, ) @property def lengths(self) -> tuple[int, int, int]: """Return the left-wing, gap, and right-wing lengths.""" return ( self.left_wing_length, self.gap_length, self.right_wing_length, ) @property def layout(self) -> str: """Return the compact wing-gap-wing representation.""" return "-".join(str(length) for length in self.lengths) def _design_id(self) -> str: """Include the wing-gap-wing layout in the ASO design ID.""" chemistry = ( self._identity_suffix("c", self.chemistry.id) if self.chemistry else "" ) return ( f"gapmer:l{self.left_wing_length}g{self.gap_length}" f"r{self.right_wing_length}-m{self.mechanism.id}-" f"{self._sequence_token(self.sequence)}{chemistry}" )
def _positive_length(name: str, value: int) -> int: """Return one validated gapmer segment length.""" if type(value) is not int or value < 1: raise ValueError(f"Gapmer {name} must be a positive integer.") return value