"""Pairwise and multiple-sequence aligner wrappers for mapping kinase sequences onto UniProt.
Wraps Clustal Omega (:class:`ClustalOmegaAligner`) and Biopython (:class:`BioAligner`)
behind a common :class:`CustomAligner` interface, with specializations for aligning
BLOSUM-based and KinCore sequences to UniProt.
"""
from abc import ABC, abstractmethod
from dataclasses import dataclass
[docs]
class CustomAligner(ABC):
"""Custom aligner class for aligning sequences."""
[docs]
@abstractmethod
def align(self, *args, **kwargs):
"""Abstract method for aligning sequences."""
...
[docs]
@dataclass
class ClustalOmegaAligner(CustomAligner):
"""ClustalOmega aligner class for multiple sequence alignments (need to initialize with list of sequences)."""
list_sequences: list[str]
"""list[str]: List of sequences to align."""
substitution_matrix: str = "BLOSUM62"
"""str: Substitution matrix used. Default is BLOSUM62."""
path_bin: str = "/usr/local/bin/clustalo"
"""str: Path to clustalo binary. Default is "/usr/local/bin/clustalo"."""
def __post_init__(self):
from biotite.sequence import ProteinSequence, align
self.alphabet = ProteinSequence.alphabet
self.matrix_substitution = align.SubstitutionMatrix(
self.alphabet, self.alphabet, self.substitution_matrix
)
self.list_sequences = [ProteinSequence(seq) for seq in self.list_sequences]
self.align()
[docs]
def align(self) -> str:
from biotite.application import clustalo
app = clustalo.ClustalOmegaApp(
self.list_sequences, self.path_bin, self.matrix_substitution
)
app.start()
app.join()
self.alignments = app.get_alignment()
self.list_alignments = self.alignments.get_gapped_sequences()
[docs]
@dataclass
class BioAligner(CustomAligner):
"""BioPython aligner class for aligning sequences. Initialized without sequences"""
from Bio import Align
substitution_matrix: str = "BLOSUM90"
"""str: Substitution matrix used. Default is BLOSUM90 to maximize mismatch penalty."""
mode: str = "local"
"""str: Alignment mode. Default is "local"."""
gap_score: int = -5
"""int: Gap score. Default is -5."""
extend_gap_score: int = -1
"""int: Gap extension score. Default is -1."""
def __post_init__(self):
from Bio import Align
self.aligner = Align.PairwiseAligner()
self.aligner.mode = self.mode
self.aligner.substitution_matrix = Align.substitution_matrices.load(
self.substitution_matrix
)
self.aligner.open_gap_score = self.gap_score
self.aligner.extend_gap_score = self.extend_gap_score
[docs]
def align(self, seq1: str, seq2: str) -> Align.MultipleSeqAlignment:
return self.aligner.align(seq1, seq2)
[docs]
@dataclass
class BL2UniProtAligner(BioAligner):
mode: str = "global"
"""str: Alignment mode. Default is "global."""
def __post_init__(self):
super().__post_init__()
[docs]
@dataclass
class Kincore2UniProtAligner(BioAligner):
mode: str = "local"
"""str: Alignment mode. Default is "local."""
def __post_init__(self):
super().__post_init__()