Skip to content

Commit 4d3df23

Browse files
authored
Support wheels for Python 3.11-3.12. (#60)
(also includes updates for dependencies as necessary)
1 parent 84a8475 commit 4d3df23

7 files changed

Lines changed: 75 additions & 83 deletions

File tree

.github/workflows/build-wheels.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
set -euxo pipefail nullglob
55

6-
for PY in cp38-cp38 cp39-cp39 cp310-cp310; do
6+
for PY in cp310-cp310 cp311-cp311 cp312-cp312; do
77
PYBIN="/opt/python/${PY}/bin"
88
"${PYBIN}/pip" install maturin
99
"${PYBIN}/maturin" build -i "${PYBIN}/python" --release

.github/workflows/python.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
- uses: actions/checkout@v3
1212
- uses: actions/setup-python@v2
1313
with:
14-
python-version: '3.10'
15-
- uses: abatilo/actions-poetry@v2.1.4
14+
python-version: '3.14'
15+
- uses: abatilo/actions-poetry@v3.0.2
1616
- run: poetry install
1717
- run: poetry run black --check --verbose .

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
runs-on: macos-latest
2727
strategy:
2828
matrix:
29-
python-version: ["3.8", "3.9", "3.10"]
29+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
3030
steps:
3131
- uses: actions/checkout@v1
3232
- uses: actions-rs/toolchain@v1
@@ -75,7 +75,7 @@ jobs:
7575
- uses: actions/download-artifact@v2 # downloads all artifacts into subdir of current directory named artifact name
7676
- uses: actions/setup-python@v2
7777
with:
78-
python-version: 3.9
78+
python-version: 3.14
7979
- name: Publish
8080
env:
8181
MATURIN_PASSWORD: ${{ secrets.MATURIN_PASSWORD }}

.github/workflows/rust.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ jobs:
9090
- uses: actions/checkout@v3
9191
- uses: actions/setup-python@v2
9292
with:
93-
python-version: '3.10'
94-
- uses: abatilo/actions-poetry@v2.1.4
93+
python-version: '3.14'
94+
- uses: abatilo/actions-poetry@v3.0.2
9595
- run: poetry install
9696
- run: poetry run maturin develop --release
9797
- run: poetry run pytest

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ publish = false
1010
lazy_static = "1.4.0"
1111
thiserror = "1.0.30"
1212
smallvec = "1.8.0"
13-
pyo3 = {version = "0.20.0", features = ["extension-module"], optional = true}
13+
pyo3 = {version = "0.27.1", features = ["extension-module"], optional = true}
1414
quickcheck = {version = "1.0.3", optional = true}
1515
serde = {version = "1.0", features = ["derive"], optional = true}
1616

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ repository = "https://github.com/SecureDNA/quickdna"
99

1010

1111
[tool.poetry.dependencies]
12-
python = "^3.8"
12+
python = "^3.10"
1313

1414
[tool.poetry.dev-dependencies]
15-
pytest = "^6.1"
15+
pytest = "^9.0"
1616
pylint = "^2.6"
1717
flake8 = "^3.9"
1818
wheel = "*"

src/python_api.rs

Lines changed: 65 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,75 @@
1-
#![allow(clippy::borrow_deref_ref)] // TODO: broken clippy lint?
2-
// Copyright 2021-2024 SecureDNA Stiftung (SecureDNA Foundation) <licensing@securedna.org>
3-
// SPDX-License-Identifier: MIT OR Apache-2.0
1+
// Copyright 2021-2024 SecureDNA Stiftung (SecureDNA Foundation) <licensing@securedna.org>
2+
// SPDX-License-Identifier: MIT OR Apache-2.0
43

5-
use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes};
4+
#[pyo3::pymodule]
5+
mod quickdna {
66

7-
use crate::{
8-
errors::TranslationError,
9-
trans_table::{reverse_complement_bytes, TranslationTable},
10-
Nucleotide, NucleotideAmbiguous,
11-
};
7+
use pyo3::{exceptions::PyValueError, prelude::*, types::PyBytes};
128

13-
impl From<TranslationError> for PyErr {
14-
fn from(err: TranslationError) -> PyErr {
15-
PyValueError::new_err(err.to_string())
16-
}
17-
}
18-
19-
#[pyfunction]
20-
fn _check_table(table: u8) -> PyResult<()> {
21-
let _ = TranslationTable::try_from(table)?;
22-
Ok(())
23-
}
9+
use crate::{
10+
errors::TranslationError,
11+
trans_table::{reverse_complement_bytes, TranslationTable},
12+
Nucleotide, NucleotideAmbiguous,
13+
};
2414

25-
/// Translate a bytestring of DNA nucleotides into a bytestring of amino acids.
26-
///
27-
/// The input string is allowed to contain IUPAC ambiguity codes; ambiguous
28-
/// codons are represented by `X` in the output.
29-
///
30-
/// * `translate(b"CCNTACACK CATNCNAAT")` returns `b"PYTHXN"`
31-
#[pyfunction]
32-
fn _translate(py: Python, table: u8, dna: &PyBytes) -> PyResult<PyObject> {
33-
let table = TranslationTable::try_from(table)?;
34-
let bytes = table.translate_dna_bytes::<NucleotideAmbiguous>(dna.as_bytes())?;
35-
Ok(PyBytes::new(py, &bytes).into())
36-
}
15+
impl From<TranslationError> for PyErr {
16+
fn from(err: TranslationError) -> PyErr {
17+
PyValueError::new_err(err.to_string())
18+
}
19+
}
3720

38-
/// Translate a bytestring of DNA nucleotides into a bytestring of amino acids.
39-
///
40-
/// The input string is validated to consist of unambiguous nucleotides (no IUPAC ambiguity codes).
41-
///
42-
/// * `translate_strict(b"AAACCCTTTGGG")` returns `b"KPFG"`
43-
/// * `translate_strict(b"AAACCCTTTGGN")` is an error.
44-
#[pyfunction]
45-
fn _translate_strict(py: Python, table: u8, dna: &PyBytes) -> PyResult<PyObject> {
46-
let table = TranslationTable::try_from(table)?;
47-
let bytes = table.translate_dna_bytes::<Nucleotide>(dna.as_bytes())?;
48-
Ok(PyBytes::new(py, &bytes).into())
49-
}
21+
#[pyfunction]
22+
fn _check_table(table: u8) -> PyResult<()> {
23+
let _ = TranslationTable::try_from(table)?;
24+
Ok(())
25+
}
5026

51-
/// Get the reverse complement of a bytestring of DNA nucleotides.
52-
///
53-
/// The input string is allowed to contain IUPAC ambiguity codes.
54-
///
55-
/// * `reverse_complement(b"AAAAABCCC")` returns `b"GGGVTTTTT"`
56-
#[pyfunction]
57-
fn _reverse_complement(py: Python, dna: &PyBytes) -> PyResult<PyObject> {
58-
let bytes = reverse_complement_bytes::<NucleotideAmbiguous>(dna.as_bytes())?;
59-
Ok(PyBytes::new(py, &bytes).into())
60-
}
27+
/// Translate a bytestring of DNA nucleotides into a bytestring of amino acids.
28+
///
29+
/// The input string is allowed to contain IUPAC ambiguity codes; ambiguous
30+
/// codons are represented by `X` in the output.
31+
///
32+
/// * `translate(b"CCNTACACK CATNCNAAT")` returns `b"PYTHXN"`
33+
#[pyfunction]
34+
fn _translate(py: Python, table: u8, dna: Bound<'_, PyBytes>) -> PyResult<Py<PyAny>> {
35+
let table = TranslationTable::try_from(table)?;
36+
let bytes = table.translate_dna_bytes::<NucleotideAmbiguous>(dna.as_bytes())?;
37+
Ok(PyBytes::new(py, &bytes).into())
38+
}
6139

62-
/// Get the reverse complement of a bytestring of DNA nucleotides.
63-
///
64-
/// The input string is validated to consist of unambiguous nucleotides (no IUPAC ambiguity codes).
65-
///
66-
/// * `reverse_complement_strict(b"AAAAAACCC")` returns `b"GGGTTTTTT"`
67-
/// * `reverse_complement_strict(b"AAAAAACCN")` is an error.
68-
#[pyfunction]
69-
fn _reverse_complement_strict(py: Python, dna: &PyBytes) -> PyResult<PyObject> {
70-
let bytes = reverse_complement_bytes::<Nucleotide>(dna.as_bytes())?;
71-
Ok(PyBytes::new(py, &bytes).into())
72-
}
40+
/// Translate a bytestring of DNA nucleotides into a bytestring of amino acids.
41+
///
42+
/// The input string is validated to consist of unambiguous nucleotides (no IUPAC ambiguity codes).
43+
///
44+
/// * `translate_strict(b"AAACCCTTTGGG")` returns `b"KPFG"`
45+
/// * `translate_strict(b"AAACCCTTTGGN")` is an error.
46+
#[pyfunction]
47+
fn _translate_strict(py: Python, table: u8, dna: Bound<'_, PyBytes>) -> PyResult<Py<PyAny>> {
48+
let table = TranslationTable::try_from(table)?;
49+
let bytes = table.translate_dna_bytes::<Nucleotide>(dna.as_bytes())?;
50+
Ok(PyBytes::new(py, &bytes).into())
51+
}
7352

74-
#[pymodule]
75-
fn quickdna(_py: Python, m: &PyModule) -> PyResult<()> {
76-
m.add_function(wrap_pyfunction!(_check_table, m)?)?;
77-
m.add_function(wrap_pyfunction!(_translate, m)?)?;
78-
m.add_function(wrap_pyfunction!(_translate_strict, m)?)?;
79-
m.add_function(wrap_pyfunction!(_reverse_complement, m)?)?;
80-
m.add_function(wrap_pyfunction!(_reverse_complement_strict, m)?)?;
53+
/// Get the reverse complement of a bytestring of DNA nucleotides.
54+
///
55+
/// The input string is allowed to contain IUPAC ambiguity codes.
56+
///
57+
/// * `reverse_complement(b"AAAAABCCC")` returns `b"GGGVTTTTT"`
58+
#[pyfunction]
59+
fn _reverse_complement(py: Python, dna: Bound<'_, PyBytes>) -> PyResult<Py<PyAny>> {
60+
let bytes = reverse_complement_bytes::<NucleotideAmbiguous>(dna.as_bytes())?;
61+
Ok(PyBytes::new(py, &bytes).into())
62+
}
8163

82-
Ok(())
64+
/// Get the reverse complement of a bytestring of DNA nucleotides.
65+
///
66+
/// The input string is validated to consist of unambiguous nucleotides (no IUPAC ambiguity codes).
67+
///
68+
/// * `reverse_complement_strict(b"AAAAAACCC")` returns `b"GGGTTTTTT"`
69+
/// * `reverse_complement_strict(b"AAAAAACCN")` is an error.
70+
#[pyfunction]
71+
fn _reverse_complement_strict(py: Python, dna: Bound<'_, PyBytes>) -> PyResult<Py<PyAny>> {
72+
let bytes = reverse_complement_bytes::<Nucleotide>(dna.as_bytes())?;
73+
Ok(PyBytes::new(py, &bytes).into())
74+
}
8375
}

0 commit comments

Comments
 (0)