pydna 6.0.0a15__py3-none-any.whl → 6.0.0a25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydna/__init__.py +1 -1
- pydna/crispr.py +128 -0
- pydna/ligate.py +68 -0
- pydna/myprimers.py +6 -5
- pydna/seqrecord.py +15 -0
- {pydna-6.0.0a15.dist-info → pydna-6.0.0a25.dist-info}/METADATA +1 -1
- {pydna-6.0.0a15.dist-info → pydna-6.0.0a25.dist-info}/RECORD +9 -7
- {pydna-6.0.0a15.dist-info → pydna-6.0.0a25.dist-info}/LICENSE.txt +0 -0
- {pydna-6.0.0a15.dist-info → pydna-6.0.0a25.dist-info}/WHEEL +0 -0
pydna/__init__.py
CHANGED
|
@@ -149,7 +149,7 @@ __license__ = "BSD"
|
|
|
149
149
|
__maintainer__ = "Björn Johansson"
|
|
150
150
|
__email__ = "bjorn_johansson@bio.uminho.pt"
|
|
151
151
|
__status__ = "Development" # "Production" #"Prototype"
|
|
152
|
-
__version__ = "6.0.0-a.
|
|
152
|
+
__version__ = "6.0.0-a.25"
|
|
153
153
|
|
|
154
154
|
# create config directory
|
|
155
155
|
_os.environ["pydna_config_dir"] = _os.getenv("pydna_config_dir", _appdirs.user_config_dir("pydna"))
|
pydna/crispr.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# Copyright 2013-2023 by Björn Johansson. All rights reserved.
|
|
5
|
+
# This code is part of the Python-dna distribution and governed by its
|
|
6
|
+
# license. Please see the LICENSE.txt file that should have been included
|
|
7
|
+
# as part of this package.
|
|
8
|
+
"""Provides the Dseq class for handling double stranded DNA sequences.
|
|
9
|
+
|
|
10
|
+
Dseq is a subclass of :class:`Bio.Seq.Seq`. The Dseq class
|
|
11
|
+
is mostly useful as a part of the :class:`pydna.dseqrecord.Dseqrecord` class
|
|
12
|
+
which can hold more meta data.
|
|
13
|
+
|
|
14
|
+
The Dseq class support the notion of circular and linear DNA topology.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
import re
|
|
19
|
+
from pydna.utils import rc
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _cas(ABC):
|
|
23
|
+
scaffold = "ND"
|
|
24
|
+
pam = "ND"
|
|
25
|
+
size = 0
|
|
26
|
+
fst5 = 0
|
|
27
|
+
fst3 = 0
|
|
28
|
+
|
|
29
|
+
def __init__(self, protospacer):
|
|
30
|
+
self.protospacer = protospacer
|
|
31
|
+
self.compsite = re.compile(
|
|
32
|
+
f"(?=(?P<watson>{protospacer}{self.pam}))|(?=(?P<crick>{rc(self.pam)}{rc(protospacer)}))", re.UNICODE
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def search(self, dna, linear=True):
|
|
37
|
+
"""To override in subclass."""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def __repr__(self):
|
|
41
|
+
return f"{type(self).__name__}({self.protospacer[:3]}..{self.protospacer[-3:]})"
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def __str__(self):
|
|
45
|
+
"""To override in subclass."""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class cas9(_cas):
|
|
50
|
+
"""docstring.
|
|
51
|
+
|
|
52
|
+
|----size----------|
|
|
53
|
+
|
|
54
|
+
---protospacer------
|
|
55
|
+
-fst3
|
|
56
|
+
fst5 |-|
|
|
57
|
+
|--------------|
|
|
58
|
+
PAM
|
|
59
|
+
5-NNGGAAGAGTAATACACTA-AAANGGNN-3
|
|
60
|
+
||||||||||||||||||| ||||||||
|
|
61
|
+
3-NNCCTTCTCATTATGTGAT-TTTNCCNN-5
|
|
62
|
+
||||||||||||||||| |||
|
|
63
|
+
5-GGAAGAGTAATACACTA-AAAg-u-a-a-g-g Scaffold
|
|
64
|
+
---gRNA spacer--- u-a
|
|
65
|
+
u-a
|
|
66
|
+
u-a
|
|
67
|
+
u-a
|
|
68
|
+
a-u
|
|
69
|
+
g-u-g
|
|
70
|
+
a a
|
|
71
|
+
g-c-a
|
|
72
|
+
c-g
|
|
73
|
+
u-a
|
|
74
|
+
a-u
|
|
75
|
+
g a tetraloop
|
|
76
|
+
a-a
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
scaffold = "GTTTTAGAGCTAGAAATAGCAAGTTAAAATAAGG"
|
|
80
|
+
pam = ".GG"
|
|
81
|
+
size = 20
|
|
82
|
+
fst5 = 17
|
|
83
|
+
fst3 = -3
|
|
84
|
+
ovhg = fst5 - (size + fst3)
|
|
85
|
+
|
|
86
|
+
def search(self, dna, linear=True):
|
|
87
|
+
"""docstring."""
|
|
88
|
+
dna = str(dna).upper()
|
|
89
|
+
if linear:
|
|
90
|
+
dna = dna
|
|
91
|
+
else:
|
|
92
|
+
dna = dna + dna[1 : self.size]
|
|
93
|
+
results = []
|
|
94
|
+
for mobj in self.compsite.finditer(dna):
|
|
95
|
+
w, c = mobj.groups()
|
|
96
|
+
if w:
|
|
97
|
+
results.append(mobj.start("watson") + 1 + self.fst5)
|
|
98
|
+
if c:
|
|
99
|
+
results.append(mobj.start("crick") + len(self.pam) + 1 - self.fst3)
|
|
100
|
+
return results
|
|
101
|
+
|
|
102
|
+
def __str__(self):
|
|
103
|
+
"""docstring."""
|
|
104
|
+
return f">{type(self).__name__} protospacer scaffold\n{self.protospacer} {self.scaffold}"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def protospacer(guide_construct, cas=cas9):
|
|
108
|
+
"""docstring."""
|
|
109
|
+
in_watson = [
|
|
110
|
+
mobj.group("ps")
|
|
111
|
+
for mobj in re.finditer(f"(?P<ps>.{{{cas.size}}})(?:{cas.scaffold})", str(guide_construct.seq).upper())
|
|
112
|
+
]
|
|
113
|
+
in_crick = [
|
|
114
|
+
rc(mobj.group("ps"))
|
|
115
|
+
for mobj in re.finditer(f"(?:{rc(cas.scaffold)})(?P<ps>.{{{cas.size}}})", str(guide_construct.seq).upper())
|
|
116
|
+
]
|
|
117
|
+
return in_watson + in_crick
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
import os as _os
|
|
122
|
+
|
|
123
|
+
cached = _os.getenv("pydna_cached_funcs", "")
|
|
124
|
+
_os.environ["pydna_cached_funcs"] = ""
|
|
125
|
+
import doctest
|
|
126
|
+
|
|
127
|
+
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
|
|
128
|
+
_os.environ["pydna_cached_funcs"] = cached
|
pydna/ligate.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright 2013-2023 by Björn Johansson. All rights reserved.
|
|
4
|
+
# This code is part of the Python-dna distribution and governed by its
|
|
5
|
+
# license. Please see the LICENSE.txt file that should have been included
|
|
6
|
+
# as part of this package.
|
|
7
|
+
"""docstring."""
|
|
8
|
+
from operator import add
|
|
9
|
+
from functools import reduce
|
|
10
|
+
import networkx as _nx
|
|
11
|
+
from itertools import permutations
|
|
12
|
+
import logging as _logging
|
|
13
|
+
|
|
14
|
+
_module_logger = _logging.getLogger("pydna." + __name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def ligate(fragments: list):
|
|
18
|
+
"""docstring."""
|
|
19
|
+
G = _nx.DiGraph()
|
|
20
|
+
G.add_nodes_from(["begin", "end"])
|
|
21
|
+
fragments = fragments[:]
|
|
22
|
+
|
|
23
|
+
fragments.extend(f.rc() for f in fragments[1:])
|
|
24
|
+
|
|
25
|
+
for node in fragments:
|
|
26
|
+
G.add_edge("begin", node)
|
|
27
|
+
G.add_edge(node, "end")
|
|
28
|
+
|
|
29
|
+
for seq1, seq2 in permutations(fragments, 2):
|
|
30
|
+
try:
|
|
31
|
+
seq1 + seq2
|
|
32
|
+
except TypeError as err:
|
|
33
|
+
if str(err) != "sticky ends not compatible!":
|
|
34
|
+
raise
|
|
35
|
+
else:
|
|
36
|
+
if seq1.seq.three_prime_end() != (
|
|
37
|
+
"blunt",
|
|
38
|
+
"",
|
|
39
|
+
) and seq2.seq.five_prime_end() != ("blunt", ""):
|
|
40
|
+
G.add_edge(seq1, seq2)
|
|
41
|
+
try:
|
|
42
|
+
G.remove_edge("begin", seq2)
|
|
43
|
+
except _nx.NetworkXError as err:
|
|
44
|
+
if "not in graph" not in str(err):
|
|
45
|
+
raise
|
|
46
|
+
try:
|
|
47
|
+
G.remove_edge(seq1, "end")
|
|
48
|
+
except _nx.NetworkXError as err:
|
|
49
|
+
if "not in graph" not in str(err):
|
|
50
|
+
raise
|
|
51
|
+
|
|
52
|
+
cpaths = [p for p in sorted(_nx.simple_cycles(G), key=len) if len(p) > 1]
|
|
53
|
+
csequences = [reduce(add, x).looped() for x in cpaths]
|
|
54
|
+
lpaths = [p for p in sorted(_nx.all_simple_paths(G, "begin", "end"), key=len) if len(p) > 3]
|
|
55
|
+
lsequences = [reduce(add, lp[1:-1]) for lp in lpaths]
|
|
56
|
+
|
|
57
|
+
return csequences, lsequences
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
import os as _os
|
|
62
|
+
|
|
63
|
+
cached = _os.getenv("pydna_cached_funcs", "")
|
|
64
|
+
_os.environ["pydna_cached_funcs"] = ""
|
|
65
|
+
import doctest
|
|
66
|
+
|
|
67
|
+
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
|
|
68
|
+
_os.environ["pydna_cached_funcs"] = cached
|
pydna/myprimers.py
CHANGED
|
@@ -135,8 +135,9 @@ class PrimerList(_UserList):
|
|
|
135
135
|
i = oldstrs.index(str(p.seq).upper())
|
|
136
136
|
except ValueError:
|
|
137
137
|
i = no + len(new)
|
|
138
|
-
suffix = p.id.
|
|
139
|
-
suffix.
|
|
138
|
+
# suffix = p.id.removeprefix(f"{str(i)}_") # use this after removing python 3.8
|
|
139
|
+
suffix = p.id[len(f"{str(i)}_") :] if p.id.startswith(f"{str(i)}_") else p.id
|
|
140
|
+
# suffix.lstrip("_")
|
|
140
141
|
newprimer = _copy.copy(p)
|
|
141
142
|
newprimer.id = f"{i}_{suffix}"
|
|
142
143
|
new.append(newprimer)
|
|
@@ -144,7 +145,7 @@ class PrimerList(_UserList):
|
|
|
144
145
|
found.append(self[i])
|
|
145
146
|
new = new[::-1]
|
|
146
147
|
newold = new + found
|
|
147
|
-
return _pretty_str("\n".join([p.format("
|
|
148
|
+
return _pretty_str("\n".join([p.format("primer") for p in newold]))
|
|
148
149
|
|
|
149
150
|
def pydna_code_from_list(self, lst: list):
|
|
150
151
|
"""Pydna code for a list of primer objects."""
|
|
@@ -155,7 +156,7 @@ class PrimerList(_UserList):
|
|
|
155
156
|
try:
|
|
156
157
|
prstrs.index(str(p.seq).upper())
|
|
157
158
|
except ValueError as e:
|
|
158
|
-
print(f"{p.format('
|
|
159
|
+
print(f"{p.format('primer')}")
|
|
159
160
|
err = e
|
|
160
161
|
else:
|
|
161
162
|
indices.append(self.data.index(p))
|
|
@@ -167,7 +168,7 @@ class PrimerList(_UserList):
|
|
|
167
168
|
msg += f"{self.identifier} = {curly}\n\n"
|
|
168
169
|
msg += ", ".join(f"{self.identifier}[{i}]" for i in indices)
|
|
169
170
|
msg += " = parse_primers('''\n\n"
|
|
170
|
-
msg += "\n".join(self[i].format("
|
|
171
|
+
msg += "\n".join(self[i].format("primer") for i in indices)
|
|
171
172
|
msg += "\n''')"
|
|
172
173
|
return _pretty_str(msg)
|
|
173
174
|
|
pydna/seqrecord.py
CHANGED
|
@@ -615,6 +615,21 @@ class SeqRecord(_SeqRecord):
|
|
|
615
615
|
|
|
616
616
|
def __format__(self, format):
|
|
617
617
|
"""docstring."""
|
|
618
|
+
|
|
619
|
+
def removeprefix(text, prefix):
|
|
620
|
+
"""Until Python 3.8 is dropped, then use str.removeprefix."""
|
|
621
|
+
if text.startswith(prefix):
|
|
622
|
+
return text[len(prefix) :]
|
|
623
|
+
return text
|
|
624
|
+
|
|
625
|
+
if format == "pydnafasta":
|
|
626
|
+
return _pretty_str(
|
|
627
|
+
f">{self.id} {len(self)} bp {dict(((True,'circular'),(False,'linear')))[self.seq.circular]}\n{str(self.seq)}\n"
|
|
628
|
+
)
|
|
629
|
+
if format == "primer":
|
|
630
|
+
return _pretty_str(
|
|
631
|
+
f">{self.id} {len(self)}-mer{removeprefix(self.description, self.name).strip()}\n{str(self.seq)}\n"
|
|
632
|
+
)
|
|
618
633
|
return _pretty_str(super().__format__(format))
|
|
619
634
|
|
|
620
635
|
def __add__(self, other):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pydna
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.0a25
|
|
4
4
|
Summary: Representing double stranded DNA and functions for simulating cloning and homologous recombination between DNA molecules.
|
|
5
5
|
Home-page: https://github.com/BjornFJohansson/pydna#-pydna
|
|
6
6
|
License: BSD
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
pydna/__init__.py,sha256=
|
|
1
|
+
pydna/__init__.py,sha256=s_mxJrApNHS7MAajAxc28BJ3oun19_7GU7GcUFxLXzw,13035
|
|
2
2
|
pydna/_pretty.py,sha256=8X-YttddmNO6vd536opcbNMAIJz5wK7savZhpssQ75A,1102
|
|
3
3
|
pydna/_thermodynamic_data.py,sha256=9_w-97DpkbsWbJGHVijscMvUS_SFt7GxzrxspMRPZSg,10903
|
|
4
4
|
pydna/all.py,sha256=i2dR9cAccr3AdtAMMeH4jazpVjxWjfvu8LB3LD9hwgY,2099
|
|
@@ -9,6 +9,7 @@ pydna/codon.py,sha256=7UMhxpcdKl4lwqA_ucHwD0CTz0l7ScGS7LbP6uAwr40,2408
|
|
|
9
9
|
pydna/common_sub_strings.py,sha256=sRxqEaSmDFMCnEVD5Hu5veVvbOBZUrhxKcK6zWJXRpc,11157
|
|
10
10
|
pydna/conftest.py,sha256=T5-WmHWLZH3U3ifxnbxd_oOiIFibIrkYpwNacCHlvsY,999
|
|
11
11
|
pydna/contig.py,sha256=aNKtXGaHY0c70tEEMHk_9lDMk9DQzjFRO6LK-GPtdwc,7991
|
|
12
|
+
pydna/crispr.py,sha256=n-QWU8aylK0oFEeHxsQWvketKeEfsER12L7EvDN4JHs,3697
|
|
12
13
|
pydna/design.py,sha256=bmwT1v-h0pFEtzQ01ZLVhmgwl_OTUXLaVBaI5Q0bc2c,26442
|
|
13
14
|
pydna/download.py,sha256=uFoAvWYXr1C30071p9OweMdemvKO-1F2pB5CHo2k31U,1307
|
|
14
15
|
pydna/dseq.py,sha256=dhPDGbsnLq_W09WMucM5faexyQlns4djOHs5iDiTEwU,44975
|
|
@@ -23,18 +24,19 @@ pydna/genbankfixer.py,sha256=D2-I30fIPMndyefNzOaeAYbu6LGHC_SnOyjxJhhMfYw,20695
|
|
|
23
24
|
pydna/genbankrecord.py,sha256=EWRQ9EB7xzIAVqk7ico0bL_JSjR92LXt0UTSQ0HztEI,5771
|
|
24
25
|
pydna/goldengate.py,sha256=Fmkbb4kkau9h1o6I-MiQZ0rNHwmD4-1IK0BJaL1MblI,1774
|
|
25
26
|
pydna/ladders.py,sha256=2SaTq7_Y8XLvEBK0UqgkB9aWePzG0g84cPiNtJfggx0,3574
|
|
27
|
+
pydna/ligate.py,sha256=41YYBcZxGl1PzABZoi-zkRlRcR6sYCzT4rp5HE45A9Q,2205
|
|
26
28
|
pydna/myenzymes.py,sha256=gnD9jP-G2ERTpR5vkeOD1LDvlhwW-rsolz0Zoo8IfMs,1797
|
|
27
|
-
pydna/myprimers.py,sha256=
|
|
29
|
+
pydna/myprimers.py,sha256=JtpdejRP6MkKplLv-p5NCMzxBTepYN0CVHSxxP3ms3Q,7362
|
|
28
30
|
pydna/parsers.py,sha256=TUGRo-1Av3gRaHKHS4GM3Onou-X-Ai8IRICFvO8gDeo,7998
|
|
29
31
|
pydna/primer.py,sha256=7AYWIhr1YbjG0Gak94bU6bTlsRUgWCIbKTndgsgWZ2M,2533
|
|
30
32
|
pydna/readers.py,sha256=wgk4wBiCWZKqMIwof2YESGDzWOer6uAMgxUeJVOIQrM,1710
|
|
31
33
|
pydna/seq.py,sha256=u_DX4awip98x0wBkFycEilpLY2wO8yqpcmVeCpVpDhA,4125
|
|
32
|
-
pydna/seqrecord.py,sha256=
|
|
34
|
+
pydna/seqrecord.py,sha256=shQk8oH1Aw13vo03BUR-ER32mYT0PjjlILDBXc4QXyM,22754
|
|
33
35
|
pydna/sequence_picker.py,sha256=KMyyXAidvHerRAf8oiOfnGwsMYnzMztTWulpxPiER7M,1645
|
|
34
36
|
pydna/threading_timer_decorator_exit.py,sha256=D91kqjKSavWDnXyc1Fo-CwPYtbmR2DjTXnBYSRXKmSA,2793
|
|
35
37
|
pydna/tm.py,sha256=moILUFi6W5vOfCp0uj5Cd0ADQFqgzluMJ6d-ABEUNL4,9753
|
|
36
38
|
pydna/utils.py,sha256=glw3diUAYdrcgEaSSLwq6tno9upmBc8yzTCQOrPRI5I,23443
|
|
37
|
-
pydna-6.0.
|
|
38
|
-
pydna-6.0.
|
|
39
|
-
pydna-6.0.
|
|
40
|
-
pydna-6.0.
|
|
39
|
+
pydna-6.0.0a25.dist-info/LICENSE.txt,sha256=u8QfcsnNXZM0UCexerK_MvyA2lPWgeGyUtSYXvLG6Oc,6119
|
|
40
|
+
pydna-6.0.0a25.dist-info/METADATA,sha256=oxcA3Uj0ddBcQkPMnCdDkKPL2JGFOvI8DwinFj-eGDM,17702
|
|
41
|
+
pydna-6.0.0a25.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
42
|
+
pydna-6.0.0a25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|