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
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
test:
strategy:
matrix:
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
python: ["3.10", "3.11", "3.12", "3.13", "3.14"]
platform:
- ubuntu-latest
- macos-latest
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changelog

## Version 0.0.1
## Version 0.0.1 - 0.0.2

- Initial implementation, added the DNAString and DNAStringSet classes.
- Classes extend Biocobject from biocutils.
4 changes: 2 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
furo
myst-nb
# Requirements file for ReadTheDocs, check .readthedocs.yml.
# To build the module reference correctly, make sure every external package
# under `install_requires` in `setup.cfg` is also listed here!
# sphinx_rtd_theme
myst-parser[linkify]
sphinx>=3.2.1
myst-nb
furo
sphinx-autodoc-typehints
8 changes: 4 additions & 4 deletions lib/src/stringsetpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ py::tuple create_dnastringset_pool(py::list py_seqs) {

int32_t* starts_ptr = np_starts.mutable_data();
int32_t* widths_ptr = np_widths.mutable_data();

std::stringstream pool_stream;
int32_t current_start = 0;
const std::string valid_chars = "ACGTRYSWKMBDHVN-";
Expand All @@ -38,11 +38,11 @@ py::tuple create_dnastringset_pool(py::list py_seqs) {
);
}
}

pool_stream.write(s.c_str(), current_width);
current_start += current_width;
}

py::bytes pool = py::bytes(pool_stream.str());
return py::make_tuple(pool, np_starts, np_widths);
}
Expand All @@ -54,4 +54,4 @@ void init_stringsetpool(pybind11::module &m) {
&create_dnastringset_pool,
"Efficiently create the pool and ranges for a DnaStringset from a list of strings."
);
}
}
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ install_requires =
importlib-metadata; python_version<"3.8"
iranges
numpy
biocutils>=0.3.3


[options.packages.find]
Expand Down
2 changes: 1 addition & 1 deletion src/biostrings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@


from .dnastring import DNAString
from .dnastringset import DNAStringSet
from .dnastringset import DNAStringSet
90 changes: 22 additions & 68 deletions src/biostrings/dnastring.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import re
from copy import deepcopy
from typing import Optional, Union
from warnings import warn
from typing import Any, Dict, Optional, Union

import biocutils as ut

from .utils import _sanitize_metadata

Expand All @@ -22,14 +25,19 @@
__license__ = "MIT"


class DNAString:
class DNAString(ut.BiocObject):
"""A string container for a DNA sequence, similar to Bioconductor's DNAString.

This class stores the sequence internally as bytes, enforcing the
DNA alphabet.
"""

def __init__(self, sequence: Union[str, bytes], metadata: Optional[dict] = None, validate: bool = True):
def __init__(
self,
sequence: Union[str, bytes],
metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None,
_validate: bool = True,
):
"""Create a DNAString.

Args:
Expand All @@ -39,9 +47,11 @@ def __init__(self, sequence: Union[str, bytes], metadata: Optional[dict] = None,
metadata:
Additional metadata. If None, defaults to an empty dictionary.

validate:
_validate:
Whether to validate the arguments, internal use only.
"""
super().__init__(metadata=metadata, _validate=_validate)

if isinstance(sequence, str):
self._data = sequence.upper().encode("ascii")
elif isinstance(sequence, bytes):
Expand All @@ -51,21 +61,15 @@ def __init__(self, sequence: Union[str, bytes], metadata: Optional[dict] = None,

self._metadata = _sanitize_metadata(metadata)

if validate:
if _validate:
if not _DNA_VALIDATOR.match(self._data.decode("ascii")):
raise ValueError("Input string contains non-DNA characters.")

#################
#### Copying ####
#################

def _define_output(self, in_place):
if in_place:
return self
else:
return self.__copy__()

def __copy__(self) -> "DNAString":
def __copy__(self) -> DNAString:
"""Shallow copy of the object.

Returns:
Expand All @@ -74,10 +78,10 @@ def __copy__(self) -> "DNAString":
return type(self)(
sequence=str(self),
metadata=self._metadata,
validate=False,
_validate=False,
)

def __deepcopy__(self, memo) -> "DNAString":
def __deepcopy__(self, memo) -> DNAString:
"""Deep copy of the object.

Args:
Expand All @@ -89,63 +93,13 @@ def __deepcopy__(self, memo) -> "DNAString":
return type(self)(
sequence=deepcopy(str(self), memo),
metadata=deepcopy(self._metadata, memo),
validate=False,
_validate=False,
)

########################
#### Getter/setters ####
########################

def get_metadata(self) -> dict:
"""Get additional metadata.

Returns:
Dictionary containing additional metadata.
"""
return self._metadata

def set_metadata(self, metadata: Optional[dict], in_place: bool = False) -> "DNAString":
"""Set or replace metadata.

Args:
metadata:
Additional metadata.

in_place:
Whether to modify the object in place.

Returns:
If ``in_place = False``, a new ``DNAString`` is returned with the
modified metadata. Otherwise, the current object is directly
modified and a reference to it is returned.
"""
output = self._define_output(in_place)
output._metadata = _sanitize_metadata(metadata)
return output

@property
def metadata(self) -> dict:
"""Get additional metadata.

Returns:
Dictionary containing additional metadata.
"""
return self.get_metadata()

@metadata.setter
def metadata(self, metadata: Optional[dict]):
"""Set or replace metadata (in-place operation).

Args:
metadata:
Additional metadata.
"""
warn(
"Setting property 'metadata'is an in-place operation, use 'set_metadata' instead",
UserWarning,
)
self.set_metadata(metadata, in_place=True)

def get_sequence(self) -> str:
"""Get the sequence.

Expand Down Expand Up @@ -191,7 +145,7 @@ def __eq__(self, other) -> bool:
#### Getitem/setitem ####
#########################

def __getitem__(self, key: Union[int, slice]) -> "DNAString":
def __getitem__(self, key: Union[int, slice]) -> DNAString:
"""Extract a subsequence (slicing).

Args:
Expand All @@ -214,7 +168,7 @@ def __getitem__(self, key: Union[int, slice]) -> "DNAString":
#### methods ####
#################

def reverse_complement(self) -> "DNAString":
def reverse_complement(self) -> DNAString:
"""Compute the reverse complement of the sequence.

Returns:
Expand Down
Loading
Loading