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