Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ jobs:
run: tox -e type

- name: Confirm Documentation Builds
if: always()
run: tox -e docs

tests:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [ "3.11", "3.12" ]
os: [ ubuntu-latest, macos-latest, windows-latest ]
python-version: [ "3.11", "3.12", "3.13" ]

steps:
- name: Checkout DesignerDNA Project
Expand All @@ -58,7 +60,16 @@ jobs:
python -m pip install -U pip
pip install wheel setuptools
pip install tox
echo "TOX_VERSION=py$(echo ${{ matrix.python-version }} | tr -d .)-tests" >> $GITHUB_ENV

- name: Setup tox python version
if: matrix.os != 'windows-latest'
run: |
echo "TOX_VERSION=py$(echo ${{ matrix.python-version }} | tr -d .)-tests" >> $GITHUB_ENV

- name: Setup tox python version
if: matrix.os == 'windows-latest'
run: |
"TOX_VERSION=py$(echo ${{ matrix.python-version }} | tr -d .)-tests" >> $env:GITHUB_ENV

- name: Unit Testing
run: |
Expand All @@ -67,7 +78,7 @@ jobs:
- name: Save Coverage Data Temporarily
uses: actions/upload-artifact@v4
with:
name: coverage-data-${{ matrix.python-version }}
name: coverage-data-${{ matrix.os }}-${{ matrix.python-version }}
path: .coverage.*
retention-days: 1
if-no-files-found: ignore
Expand Down
75 changes: 63 additions & 12 deletions .github/workflows/python-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,84 @@ name: Upload Python Package

on:
release:
types: [published]
types: [ published ]

permissions:
contents: read

jobs:
deploy:
sdist:
runs-on: ubuntu-latest
if: github.event_name == 'release'

steps:
- uses: actions/checkout@v4
- name: Checkout Project
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version-file: .python-version-default

- name: Install dependencies
run: |
python -m pip install -U pip --disable-pip-version-check
python -m pip install setuptools wheel build

- name: Build package
run: python -m build
- name: Build Package
run: python -m build --sdist

- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
- name: Upload distributions
uses: actions/upload-artifact@v4
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
name: ci-sdist
path: dist/*.tar.gz

wheels:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [
ubuntu-latest, ubuntu-24.04-arm,
windows-latest, windows-11-arm,
macos-15-intel, macos-14
]

steps:
- name: Checkout Project
uses: actions/checkout@v5

- name: Set up QEMU
if: runner.os == 'Linux' && runner.arch == 'X64'
uses: docker/setup-qemu-action@v3
with:
platforms: all

- name: Build wheels
uses: pypa/cibuildwheel@v3.2.0
env:
CIBW_BUILD_VERBOSITY: 3
CIBW_ARCHS_LINUX: ${{ runner.arch == 'X64' && 'auto ppc64le s390x' || 'auto' }}

- uses: actions/upload-artifact@v4
with:
name: ci-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl

publish:
runs-on: ubuntu-latest
needs: [ sdist, wheels ]
permissions:
id-token: write

steps:
- name: Retrieve release distributions
uses: actions/download-artifact@v4
with:
name: ci-*
path: dist/

- name: Publish package
uses: pypa/gh-action-pypi-publish@v.1.13.0
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
13 changes: 13 additions & 0 deletions src/designer_dna/_oligos.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ def reverse_complement(sequence: str, dna: bool = ...) -> str:

"""

def m_palindrome(sequence: array[int], dna: bool = ...) -> tuple[int, int]:
"""Find the longest palindromic substring within a nucleotide sequence.

Args:
sequence (uchar[]): Nucleotide sequence writeable memory view.
dna (bool): Sequence is DNA, else RNA.

Returns:
(int, int) start and end indices denoting the longest found
palindromic subsequence within sequence.

"""

def palindrome(sequence: str, dna: bool = ...) -> str:
"""Find the longest palindromic substring within a nucleotide sequence.

Expand Down
62 changes: 46 additions & 16 deletions src/designer_dna/_oligos.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,8 @@ cpdef void m_complement(unsigned char[:] sequence, bint dna = True):
"""
cdef:
Py_ssize_t length = sequence.shape[0]
unsigned char* c_string = &sequence[0]

c_complement(c_string, length, dna)
c_complement(&sequence[0], length, dna)


cpdef str complement(str sequence, bint dna = True):
Expand Down Expand Up @@ -253,11 +252,11 @@ cdef void c_reverse_complement(

Args:
sequence (uchar*): Nucleotide sequence pointer.
length (Py_ssize_t): Length of seq.
length (Py_ssize_t): Length of sequence.
dna (bint): Sequence is DNA, else RNA.

Returns:
(void) Complement seq in place.
(void) Complement sequence in place.

"""
if dna:
Expand Down Expand Up @@ -354,6 +353,18 @@ cdef inline (Py_ssize_t, Py_ssize_t) c_palindrome(
Py_ssize_t size,
bint dna
) noexcept:
"""Find the longest palindromic substring within a nucleotide sequence.

Args:
seq (uchar*): Nucleotide sequence pointer.
size (Py_ssize_t): Length of seq.
dna (bint): Sequence is DNA, else RNA.

Returns:
(tuple[Py_ssize_t, Py_ssize_t]) longest palindromic subsequence within sequence,
described by start (inclusive) and end (exclusive) index.

"""
cdef:
unsigned char* com = <unsigned char*> malloc((size + 1) * sizeof(unsigned char))
Py_ssize_t i, left, right, current, length = 0, start = 0, end = 0
Expand Down Expand Up @@ -383,6 +394,30 @@ cdef inline (Py_ssize_t, Py_ssize_t) c_palindrome(
return start, end


cpdef (int, int) m_palindrome(
unsigned char[:] sequence,
bint dna = True
):
"""Find the longest palindromic substring within a nucleotide sequence.

Args:
sequence (uchar[]): Nucleotide sequence writeable memory view.
dna (bool): Sequence is DNA, else RNA.

Returns:
(int, int) start and end indices denoting the longest found
palindromic subsequence within sequence.

"""
cdef:
int start, end
Py_ssize_t length = sequence.shape[0]

start, end = c_palindrome(&sequence[0], length, dna)

return start, end


cpdef str palindrome(str sequence, bint dna = True):
"""Find the longest palindromic substring within a nucleotide sequence.

Expand Down Expand Up @@ -479,19 +514,10 @@ cpdef int stretch(str sequence):

"""
cdef:
int longest
StringView view = str_to_view(sequence)
Py_ssize_t j
int longest = 0, current = 0
unsigned char prev = view.ptr[0]

for j in range(1, view.size):
if view.ptr[j] == prev:
current += 1
if current > longest:
longest = current
else:
current = 0
prev = view.ptr[j]
longest = c_stretch(view.ptr, view.size)
free(view.ptr)

return longest
Expand Down Expand Up @@ -530,7 +556,11 @@ cdef inline void _assign(
count += 1


cdef int c_nrepeats(unsigned char* sequence, int length, int n) noexcept:
cdef inline int c_nrepeats(
unsigned char* sequence,
int length,
int n
) noexcept:
"""Calculate the maximum observed repeats of composite pattern size n characters.

Args:
Expand Down
Loading