diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index ad5eb9a..65e0ddf 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.13' - name: Build wheels uses: PyO3/maturin-action@v1 with: @@ -37,7 +37,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.13' architecture: ${{ matrix.target }} - name: Build wheels uses: PyO3/maturin-action@v1 @@ -59,7 +59,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.13' - name: Build wheels uses: PyO3/maturin-action@v1 with: diff --git a/.github/workflows/tests_and_lint.yml b/.github/workflows/tests_and_lint.yml index 42b9213..a87349a 100644 --- a/.github/workflows/tests_and_lint.yml +++ b/.github/workflows/tests_and_lint.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.9] + python-version: [3.13] lint-flags: - "--run-only-fast-linters" - "--run-only-pylint" @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -61,7 +61,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 11da52d..805700a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ and other sequence alignment algorithms written in Rust with Python bindings via

## Installation -`sequence_align` is distributed via [PyPi](https://pypi.org/project/sequence_align) for Python 3.7+, making installation as simple as the following -- +`sequence_align` is distributed via [PyPi](https://pypi.org/project/sequence_align) for Python 3.9 - 3.13, making installation as simple as the following -- no special setup required for cross-platform compatibility, Rust installation, etc.! ``` bash diff --git a/pyproject.toml b/pyproject.toml index 4bb5fd6..53a9fd0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,26 +4,24 @@ build-backend = "maturin" [project] name = "sequence_align" -version = "0.2.0" +version = "0.3.0" description = "Efficient implementations of Needleman-Wunsch and other sequence alignment algorithms in Rust with Python bindings." readme = "README.md" -requires-python = ">=3.7" +requires-python = ">=3.9,<3.14" authors = [ {name = "Kensho Technologies LLC.", email = "sequence-align-maintainer@kensho.com"}, ] maintainers = [ {name = "Kensho Technologies LLC.", email = "sequence-align-maintainer@kensho.com"}, ] -license-files.paths = [ - "LICENSE", # Apache 2.0 -] +license = {file = "LICENSE"} # Apache 2.0 classifiers = [ "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Programming Language :: Rust", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Topic :: Scientific/Engineering :: Bio-Informatics", diff --git a/src/sequence_align/pairwise.py b/src/sequence_align/pairwise.py index 8127e12..d16c9e4 100644 --- a/src/sequence_align/pairwise.py +++ b/src/sequence_align/pairwise.py @@ -1,5 +1,5 @@ # Copyright 2023-present Kensho Technologies, LLC. -from typing import Dict, List, Sequence, Tuple +from typing import Sequence from sequence_align import _sequence_align # type: ignore @@ -12,13 +12,13 @@ def _entry2idx( seq_b: Sequence[str], gap: str, allow_gap: bool = False, -) -> Tuple[Dict[int, str], List[int], List[int]]: +) -> tuple[dict[int, str], list[int], list[int]]: symbols = set(seq_a).union(set(seq_b)) if not allow_gap and gap in symbols: raise ValueError(f'Gap entry "{gap}" found in seq_a and/or seq_b; must not exist in either') symbols_without_gap = symbols - {gap} - idx2symbol: Dict[int, str] = { + idx2symbol: dict[int, str] = { _GAP_VAL: gap, **{idx: symbol for idx, symbol in enumerate(sorted(symbols_without_gap))}, } @@ -31,11 +31,11 @@ def _entry2idx( def _idx2entry( - idx2symbol: Dict[int, str], - seq_a_indices_aligned: List[int], - seq_b_indices_aligned: List[int], + idx2symbol: dict[int, str], + seq_a_indices_aligned: list[int], + seq_b_indices_aligned: list[int], gap: str, -) -> Tuple[List[str], List[str]]: +) -> tuple[list[str], list[str]]: seq_a_aligned = [gap if idx == _GAP_VAL else idx2symbol[idx] for idx in seq_a_indices_aligned] seq_b_aligned = [gap if idx == _GAP_VAL else idx2symbol[idx] for idx in seq_b_indices_aligned] return (seq_a_aligned, seq_b_aligned) @@ -48,7 +48,7 @@ def needleman_wunsch( mismatch_score: float = -1.0, indel_score: float = -1.0, gap: str = "-", -) -> Tuple[List[str], List[str]]: +) -> tuple[list[str], list[str]]: """Compute an optimal global pairwise alignment using the Needleman-Wunsch algorithm. Args: @@ -106,7 +106,7 @@ def hirschberg( mismatch_score: float = -1.0, indel_score: float = -1.0, gap: str = "-", -) -> Tuple[List[str], List[str]]: +) -> tuple[list[str], list[str]]: """Compute an optimal global pairwise alignment using the Hirschberg algorithm. Args: diff --git a/tests/perf/test_hirschberg.py b/tests/perf/test_hirschberg.py index 8723415..5cad518 100644 --- a/tests/perf/test_hirschberg.py +++ b/tests/perf/test_hirschberg.py @@ -1,6 +1,6 @@ # Copyright 2023-present Kensho Technologies, LLC. import time -from typing import Any, Dict +from typing import Any import unittest from sequence_align.pairwise import hirschberg @@ -23,7 +23,7 @@ class TestHirschberg(unittest.TestCase): # Needed for mypy to not complain - expected_perf: Dict[str, Any] = dict() + expected_perf: dict[str, Any] = dict() @classmethod def setUpClass(cls) -> None: diff --git a/tests/perf/test_needleman_wunsch.py b/tests/perf/test_needleman_wunsch.py index d53e5e6..5919bcf 100644 --- a/tests/perf/test_needleman_wunsch.py +++ b/tests/perf/test_needleman_wunsch.py @@ -1,6 +1,6 @@ # Copyright 2023-present Kensho Technologies, LLC. import time -from typing import Any, Dict +from typing import Any import unittest from sequence_align.pairwise import needleman_wunsch @@ -23,7 +23,7 @@ class TestNeedlemanWunsch(unittest.TestCase): # Needed for mypy to not complain - expected_perf: Dict[str, Any] = dict() + expected_perf: dict[str, Any] = dict() @classmethod def setUpClass(cls) -> None: diff --git a/tests/perf/utils.py b/tests/perf/utils.py index 817d58e..86d02b6 100644 --- a/tests/perf/utils.py +++ b/tests/perf/utils.py @@ -2,7 +2,7 @@ import multiprocessing as mp import os import random -from typing import Any, Callable, Dict, List, Tuple +from typing import Any, Callable import psutil import yaml @@ -20,7 +20,7 @@ MEM_CHECK_INTERVAL = 0.01 # seconds -def _create_perturbed_seq(seq_a: List[str]) -> List[str]: +def _create_perturbed_seq(seq_a: list[str]) -> list[str]: random.seed(1234) seq_b = list() @@ -48,7 +48,7 @@ def _create_perturbed_seq(seq_a: List[str]) -> List[str]: return seq_b -def create_seq_pair(seq_a_len: int) -> Tuple[List[str], List[str]]: +def create_seq_pair(seq_a_len: int) -> tuple[list[str], list[str]]: """Create a pair of sequences, where the second is a perturbed version of the first.""" seq_a = random.choices(CHARS, k=seq_a_len) seq_b = _create_perturbed_seq(seq_a) @@ -63,7 +63,7 @@ def get_expected_perf(key: str) -> Any: def max_memory_usage( - func: Callable[..., Any], args: Tuple[Any, ...], kwargs: Dict[str, Any] + func: Callable[..., Any], args: tuple[Any, ...], kwargs: dict[str, Any] ) -> int: """Run the given function in a separate process and return the maximum memory usage in MiB.""" max_mem = 0