Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support biopython 1.85 and drop python 3.8 #340

Merged
merged 6 commits into from
Jan 25, 2025
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
4 changes: 2 additions & 2 deletions .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
ignore = E203, E266, E501, W503, F403
max-line-length = 79
ignore = E203, E266, E501, W503, F403, E741, B905
max-line-length = 119
max-complexity = 18
select = B,C,E,F,W,T4,B9
7 changes: 2 additions & 5 deletions .github/workflows/pydna_test_and_coverage_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ jobs:
fail-fast: false
matrix:
os: ["macos-latest", "windows-latest"]
python-version: ["3.12", "3.11", "3.10", "3.9", "3.8"]
python-version: ["3.12", "3.11", "3.10", "3.9"]
include:
- os: ubuntu-latest
python-version: "3.8"
codecov: true
- os: ubuntu-latest
python-version: "3.9"
codecov: true
- os: ubuntu-latest
python-version: "3.10"
- os: ubuntu-latest
Expand Down Expand Up @@ -51,7 +49,6 @@ jobs:
uses: snok/install-poetry@v1
with:
virtualenvs.prefer-active-python: true
version: ${{ matrix.python-version == '3.8' && '1.8.2' || 'latest' }}
- name: 🔩 list Poetry settings
run: poetry config --list

Expand Down
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ repos:
args: [--branch, master]
# Format yaml files
- id: check-yaml
# Ignore conda build meta.yaml files
exclude: ^scripts/conda-build/meta.*\.yaml$
# Fix end-of-file issues
- id: end-of-file-fixer
# Fix trailing whitespace
Expand Down
3,141 changes: 1,586 additions & 1,555 deletions poetry.lock

Large diffs are not rendered by default.

30 changes: 13 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[project]
authors = [
{name = "Björn F. Johansson", email = "[email protected]"},
{name = "Manuel Lera-Ramirez", email = "[email protected]"}
{ name = "Björn F. Johansson", email = "[email protected]" },
{ name = "Manuel Lera-Ramirez", email = "[email protected]" },
]
classifiers = [
"Development Status :: 4 - Beta",
Expand All @@ -11,7 +11,6 @@ classifiers = [
"Intended Audience :: Science/Research",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -27,7 +26,10 @@ readme = "README.md"
Changelog = "https://github.com/pydna-group/pydna/blob/master/docs/CHANGELOG.md#changelog"
[tool.poetry]
description = "Representing double stranded DNA and functions for simulating cloning and homologous recombination between DNA molecules."
authors = ["Björn F. Johansson <[email protected]>", "Manuel Lera-Ramirez <[email protected]>"]
authors = [
"Björn F. Johansson <[email protected]>",
"Manuel Lera-Ramirez <[email protected]>",
]
documentation = "https://pydna-group.github.io/pydna"
homepage = "https://github.com/pydna-group/pydna#-pydna"
license = "BSD"
Expand All @@ -37,21 +39,18 @@ repository = "https://github.com/pydna-group/pydna/tree/master"
version = "6.0.0-a.24.post.17+b7b559bd66"
[tool.poetry.dependencies]
appdirs = ">=1.4.4"
biopython = ">=1.80,<1.85"
biopython = "1.85"
cai2 = { version = ">=1.0.5", optional = true }
matplotlib = { version = ">=3.4.3", optional = true }
networkx = ">=2.8.8"
numpy = [
{ version = ">1.26", python = ">=3.9" },
{ version = "<1.24", python = "<3.9" },
]
numpy = ">1.26"
pillow = { version = ">=8.4.0", optional = true }
prettytable = ">=3.5.0"
pydivsufsort = ">=0.0.14"
pyfiglet = "0.8.post1"
pyparsing = { version = ">=2.4.7", optional = true }
pyperclip = { version = ">=1.8.2", optional = true }
python = ">=3.8,<4.0"
python = ">=3.9,<4.0"
requests = { version = ">=2.26.0", optional = true }
scipy = [
{ version = ">=1.11.3", python = ">=3.12", optional = true },
Expand Down Expand Up @@ -88,19 +87,16 @@ sphinx-autobuild = "^2021.3.14"
sphinx-rtd-theme = ">=1.3,<4.0"
nbconvert = "^7.16.4"
myst-parser = [
{ version = ">3.0.0", python = "<3.10"},
{ version = ">=4.0.0", python = ">=3.10"},
{ version = ">3.0.0", python = "<3.10" },
{ version = ">=4.0.0", python = ">=3.10" },
]

[tool.poetry.group.dev.dependencies]
autopep8 = "^2.1.0"
black = "^24.4.2"
flake8-bugbear = [{ version = ">=24.4.21", python = ">=3.8.1" }]
pre-commit = [
{ version = ">3.6", python = ">=3.9" },
{ version = "<3.6", python = "<3.9" },
]
pre-commit = ">3.6"
nbstripout = "^0.7.1"
flake8-bugbear = "^24.12.12"

[tool.pytest.ini_options]
minversion = "6.0.2"
Expand Down
2 changes: 1 addition & 1 deletion scripts/conda-build/meta (copy).yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ source:
path: .
requirements:
host:
- python>=3.8,<4.0
- python>=3.9,<4.0
- pip
- poetry-core
- poetry-dynamic-versioning
Expand Down
2 changes: 1 addition & 1 deletion scripts/conda-build/meta (copy2).yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ source:
path: .
requirements:
host:
- python >=3.8
- python >=3.9
- pip
- poetry
- poetry-dynamic-versioning
Expand Down
2 changes: 1 addition & 1 deletion scripts/conda-build/meta2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ source:
path: .
requirements:
host:
- python>=3.8,<4.0
- python>=3.9,<4.0
- pip
- poetry-core
- poetry-dynamic-versioning
Expand Down
2 changes: 1 addition & 1 deletion scripts/conda-build/myenvironment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
###############################################################################
name: some-name-env
dependencies:
- python>=3.8,<4.0
- python>=3.9,<4.0
- conda-forge::appdirs>=1.4.4,<2.0.0
- conda-forge::biopython>=1.80,<2.0
- conda-forge::networkx>=2.8.8,<3.0.0
Expand Down
5 changes: 0 additions & 5 deletions src/pydna/dseq.py
Original file line number Diff line number Diff line change
Expand Up @@ -1479,11 +1479,6 @@ def cutsite_is_valid(self, cutsite: CutSiteType) -> bool:
enz = cutsite[1]
watson, crick, ovhg = self.get_cut_parameters(cutsite, True)

# The cut positions fall within the sequence
# This could go into Biopython
if not self.circular and crick < 0 or crick > len(self):
return False

# The overhang is double stranded
overhang_dseq = self[watson:crick] if ovhg < 0 else self[crick:watson]
if overhang_dseq.ovhg != 0 or overhang_dseq.watson_ovhg() != 0:
Expand Down
8 changes: 7 additions & 1 deletion src/pydna/dseqrecord.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,9 +788,10 @@ def __mul__(self, number):
if self.circular:
raise TypeError("TypeError: can't multiply circular Dseqrecord.")
if number > 0:
new = _copy.copy(self)
new = _copy.deepcopy(self)
for i in range(1, number):
new += self
new._per_letter_annotations = self._per_letter_annotations
return new
else:
return self.__class__("")
Expand Down Expand Up @@ -1061,7 +1062,10 @@ def upper(self):
pydna.dseqrecord.Dseqrecord.lower"""

upper = _copy.deepcopy(self)
# This is because the @seq.setter methods otherwise sets the _per_letter_annotations to an empty dict
prev_per_letter_annotation = upper._per_letter_annotations
upper.seq = upper.seq.upper()
upper._per_letter_annotations = prev_per_letter_annotation
return upper

def lower(self):
Expand Down Expand Up @@ -1092,7 +1096,9 @@ def lower(self):

"""
lower = _copy.deepcopy(self)
prev_per_letter_annotation = lower._per_letter_annotations
lower.seq = lower.seq.lower()
lower._per_letter_annotations = prev_per_letter_annotation
return lower

def orfs(self, minsize=300):
Expand Down
25 changes: 22 additions & 3 deletions src/pydna/seqrecord.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,29 @@ class SeqRecord(_SeqRecord):
nicer output in the IPython shell.
"""

def __init__(self, seq, *args, id="id", name="name", description="description", **kwargs):
def __init__(
self,
seq,
id="id",
name="name",
description="description",
dbxrefs=None,
features=None,
annotations=None,
letter_annotations=None,
):
if isinstance(seq, str):
seq = _Seq(seq)
super().__init__(seq, *args, id=id, name=name, description=description, **kwargs)
super().__init__(
seq,
id=id,
name=name,
description=description,
dbxrefs=dbxrefs,
features=features,
annotations=annotations,
letter_annotations=letter_annotations,
)
self._fix_attributes()

def _fix_attributes(self):
Expand Down Expand Up @@ -612,7 +631,7 @@ def removeprefix(text, prefix):

if format == "pydnafasta":
return _pretty_str(
f">{self.id} {len(self)} bp {dict(((True,'circular'),(False,'linear')))[self.seq.circular]}\n{str(self.seq)}\n"
f">{self.id} {len(self)} bp {dict(((True, 'circular'), (False, 'linear')))[self.seq.circular]}\n{str(self.seq)}\n"
)
if format == "primer":
return _pretty_str(
Expand Down
41 changes: 18 additions & 23 deletions tests/test_module_dseq.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,16 @@ def test_initialization():

obj2 = Dseq("gata")

assert obj2.circular == False
assert not obj2.circular

l = Dseq("gt")
c = l.looped()
lin_seq = Dseq("gt")
cir_seq = lin_seq.looped()

assert not l.circular
assert c.circular
assert not lin_seq.circular
assert cir_seq.circular

assert Dseq("gt", circular=False) == l
assert Dseq("gt", circular=True) == c
assert Dseq("gt", circular=False) == lin_seq
assert Dseq("gt", circular=True) == cir_seq

assert Dseq.from_string("A") == Dseq("A")
assert Dseq.from_string("A", circular=True) == Dseq("A", circular=True)
Expand Down Expand Up @@ -218,7 +218,7 @@ def test_Dseq_cutting_adding():
ovhg=0,
)

f, d, l = c.cut((EcoRI, PstI))
f, d, _ = c.cut((EcoRI, PstI))

assert d.watson == "GtcatctactatcatcgtagcgtactgatctattctgctgctcatcatcggtactctctataattatatatatatgcgcgtG"
assert d.crick == "AATTCacgcgcatatatatataattatagagagtaccgatgatgagcagcagaatagatcagtacgctacgatgatagtagatgaCTGCA"
Expand Down Expand Up @@ -554,11 +554,6 @@ def test_dseq():

def test_Dseq_slicing():
from pydna.dseq import Dseq
from pydna.readers import read
from pydna.utils import eq

from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord as Srec
from Bio.Restriction import BamHI

a = Dseq("ggatcc", "ggatcc", 0)
Expand Down Expand Up @@ -947,7 +942,7 @@ def test_apply_cut():
def test_cutsite_is_valid():

from pydna.dseq import Dseq
from Bio.Restriction import EcoRI, BsaI, PacI, NmeDI, Acc65I, NotI, BamHI, EcoRV
from Bio.Restriction import EcoRI, PacI, NmeDI, EcoRV

# Works for circular case
seqs = ["GAATTC", "TTAATTAAC", "GATATC"]
Expand Down Expand Up @@ -977,7 +972,7 @@ def test_cutsite_is_valid():
assert len(dseq.get_cutsites([enz])) == 1

# Special cases:
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 0, 0)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 0, 0)
assert len(dseq.get_cutsites([NmeDI])) == 2
# Remove left cutting place
assert len(dseq[2:].get_cutsites([NmeDI])) == 1
Expand All @@ -987,27 +982,27 @@ def test_cutsite_is_valid():
assert len(dseq[2:-2].get_cutsites([NmeDI])) == 0

# overhang left side
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", -2, 0)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", -2, 0)
assert len(dseq.get_cutsites([NmeDI])) == 1
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 2, 0)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 2, 0)
assert len(dseq.get_cutsites([NmeDI])) == 1

# overhang right side
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 0, 2)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 0, 2)
assert len(dseq.get_cutsites([NmeDI])) == 1
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 0, -2)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 0, -2)
assert len(dseq.get_cutsites([NmeDI])) == 1

# overhang both sides
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 2, 2)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 2, 2)
assert len(dseq.get_cutsites([NmeDI])) == 0
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", -2, -2)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", -2, -2)
assert len(dseq.get_cutsites([NmeDI])) == 0

# overhang on recognition site removes both cutting places
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 16, 0)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 16, 0)
assert len(dseq.get_cutsites([NmeDI])) == 0
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAAAAAAAAAGCCGGCAAAAAAAAAAAA", 0, 16)
dseq = Dseq.from_full_sequence_and_overhangs("AAAAAATTTTTTTGCCGGCAAAAAAAATTTTT", 0, 16)
assert len(dseq.get_cutsites([NmeDI])) == 0


Expand Down
Loading
Loading