From a6f239367101ec5cd276c3018c3d27952d995359 Mon Sep 17 00:00:00 2001 From: Martin Larralde <martin.larralde@embl.de> Date: Mon, 2 Sep 2024 00:37:45 +0200 Subject: [PATCH] Add type annotations for `lightmotif.lib` Python module --- lightmotif-py/lightmotif/io.rs | 3 + lightmotif-py/lightmotif/lib.pyi | 152 ++++++++++++++++++++++++++++++ lightmotif-py/lightmotif/lib.rs | 5 +- lightmotif-py/lightmotif/py.typed | 0 setup.cfg | 2 +- 5 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 lightmotif-py/lightmotif/lib.pyi create mode 100644 lightmotif-py/lightmotif/py.typed diff --git a/lightmotif-py/lightmotif/io.rs b/lightmotif-py/lightmotif/io.rs index ea1432c..6d18d48 100644 --- a/lightmotif-py/lightmotif/io.rs +++ b/lightmotif-py/lightmotif/io.rs @@ -100,8 +100,11 @@ impl UniprobeMotif { #[pyclass(module = "lightmotif.lib", extends = Motif)] pub struct TransfacMotif { + #[pyo3(get)] id: Option<String>, + #[pyo3(get)] accession: Option<String>, + #[pyo3(get)] description: Option<String>, } diff --git a/lightmotif-py/lightmotif/lib.pyi b/lightmotif-py/lightmotif/lib.pyi new file mode 100644 index 0000000..41cc93c --- /dev/null +++ b/lightmotif-py/lightmotif/lib.pyi @@ -0,0 +1,152 @@ +import typing +from typing import Dict, List, Iterable, Union, Optional, BinaryIO, Iterator, Generic +from os import PathLike + +try: + from typing import Literal +except ImportError: + from typing_extensions import Literal # type: ignore + +__version__: str +__author__: str + +AVX2_SUPPORTED: bool + +FORMAT = Literal["jaspar", "jaspar16", "uniprobe", "transfac"] + +class EncodedSequence: + def __init__(self, sequence: str, protein: bool = False) -> None: ... + def __str__(self) -> str: ... + def __len__(self) -> int: ... + def __copy__(self) -> EncodedSequence: ... + def __buffer__(self, flags: int) -> memoryview: ... + def copy(self) -> EncodedSequence: ... + def stripe(self) -> StripedSequence: ... + +class StripedSequence: + def __copy__(self) -> StripedSequence: ... + def __buffer__(self, flags: int) -> memoryview: ... + def copy(self) -> StripedSequence: ... + +class CountMatrix: + def __init__( + self, values: Dict[str, Iterable[int]], protein: bool = False + ) -> None: ... + def __eq__(self, other: object) -> bool: ... + def __len__(self) -> int: ... + def __getitem__(self, index: int) -> List[int]: ... + def normalize( + self, pseudocount: Union[None, float, Dict[str, float]] = None + ) -> WeightMatrix: ... + +class WeightMatrix: + def __eq__(self, other: object) -> bool: ... + def __len__(self) -> int: ... + def __getitem__(self, index: int) -> List[int]: ... + def log_odds( + self, background: Union[None, Dict[str, float]] = None, base: float = 2.0 + ) -> ScoringMatrix: ... + +class ScoringMatrix: + def __init__( + self, values: Dict[str, Iterable[float]], protein: bool = False + ) -> None: ... + def __len__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + def __buffer__(self, flags: int) -> memoryview: ... + def calculate(self, sequence: StripedSequence) -> StripedScores: ... + def pvalue(self, score: float) -> float: ... + def score(self, pvalue: float) -> float: ... + def reverse_complement(self) -> ScoringMatrix: ... + +class StripedScores: + def __len__(self) -> int: ... + def __getitem__(self, index: int) -> float: ... + def __buffer__(self, flags: int) -> memoryview: ... + def threshold(self, threshold: float) -> List[int]: ... + def max(self) -> Optional[float]: ... + def argmax(self) -> Optional[int]: ... + +class Motif: + @property + def counts(self) -> Optional[CountMatrix]: ... + @property + def pwm(self) -> WeightMatrix: ... + @property + def pssm(self) -> ScoringMatrix: ... + @property + def name(self) -> Optional[str]: ... + +class TransfacMotif(Motif): + @property + def counts(self) -> CountMatrix: ... + +class JasparMotif(Motif): + @property + def counts(self) -> CountMatrix: ... + +class UniprobeMotif(Motif): + @property + def counts(self) -> None: ... + +class Scanner(Iterator[Hit]): + def __iter__(self) -> Scanner: ... + def __next__(self) -> Hit: ... + +class Hit: + @property + def position(self) -> int: ... + @property + def score(self) -> float: ... + +M = typing.TypeVar("M", bound=Motif) + +class Loader(Generic[M], Iterator[M]): + def __iter__(self) -> Loader[M]: ... + def __next__(self) -> M: ... + +def create( + sequences: Iterable[str], protein: bool = False, name: Optional[str] = None +) -> Motif: ... +def stripe(sequence: str, protein: bool = False) -> StripedSequence: ... +def scan( + pssm: ScoringMatrix, + sequence: StripedSequence, + threshold: float = 0.0, + block_size: int = 256, +) -> Scanner: ... +@typing.overload +def load( + file: Union[BinaryIO, PathLike[str]], + format: Literal["jaspar"], + *, + protein: bool = False +) -> Loader[JasparMotif]: ... +@typing.overload +def load( + file: Union[BinaryIO, PathLike[str]], + format: Literal["jaspar16"], + *, + protein: bool = False +) -> Loader[JasparMotif]: ... +@typing.overload +def load( + file: Union[BinaryIO, PathLike[str]], + format: Literal["uniprobe"], + *, + protein: bool = False +) -> Loader[UniprobeMotif]: ... +@typing.overload +def load( + file: Union[BinaryIO, PathLike[str]], + format: Literal["transfac"], + *, + protein: bool = False +) -> Loader[TransfacMotif]: ... +@typing.overload +def load( + file: Union[BinaryIO, PathLike[str]], + format: FORMAT = "jaspar", + *, + protein: bool = False +) -> Loader[Motif]: ... diff --git a/lightmotif-py/lightmotif/lib.rs b/lightmotif-py/lightmotif/lib.rs index 310aae7..36918f8 100644 --- a/lightmotif-py/lightmotif/lib.rs +++ b/lightmotif-py/lightmotif/lib.rs @@ -1202,10 +1202,9 @@ pub fn create(sequences: Bound<PyAny>, protein: bool, name: Option<String>) -> P /// Encode and stripe a text sequence. #[pyfunction] #[pyo3(signature = (sequence, protein=false))] -pub fn stripe(sequence: Bound<PyAny>, protein: bool) -> PyResult<StripedSequence> { +pub fn stripe(sequence: Bound<PyString>, protein: bool) -> PyResult<StripedSequence> { let py = sequence.py(); - let s = sequence.extract::<Bound<PyString>>()?; - let encoded = EncodedSequence::__init__(s, protein).and_then(|e| Py::new(py, e))?; + let encoded = EncodedSequence::__init__(sequence, protein).and_then(|e| Py::new(py, e))?; let striped = encoded.borrow(py).stripe(); striped } diff --git a/lightmotif-py/lightmotif/py.typed b/lightmotif-py/lightmotif/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/setup.cfg b/setup.cfg index 946c470..34bcb00 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ classifiers = Topic :: Scientific/Engineering :: Bio-Informatics Topic :: Scientific/Engineering :: Medical Science Apps. Topic :: Software Development :: Libraries :: Python Modules - # Typing :: Typed + Typing :: Typed project_urls = Bug Tracker = https://github.com/althonos/lightmotif/issues Changelog = https://github.com/althonos/lightmotif/blob/master/CHANGELOG.md -- GitLab