pydna 5.5.4__py3-none-any.whl → 5.5.6__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 +30 -195
- pydna/_pretty.py +8 -8
- pydna/_thermodynamic_data.py +3 -3
- pydna/all.py +1 -12
- pydna/alphabet.py +995 -0
- pydna/amplicon.py +19 -24
- pydna/amplify.py +75 -95
- pydna/assembly.py +64 -81
- pydna/assembly2.py +375 -310
- pydna/codon.py +4 -4
- pydna/common_sub_strings.py +6 -8
- pydna/contig.py +203 -10
- pydna/design.py +176 -60
- pydna/dseq.py +1788 -718
- pydna/dseqrecord.py +197 -179
- pydna/gateway.py +6 -6
- pydna/gel.py +5 -5
- pydna/genbank.py +43 -46
- pydna/genbankfixer.py +89 -92
- pydna/ladders.py +11 -12
- pydna/oligonucleotide_hybridization.py +124 -0
- pydna/opencloning_models.py +187 -60
- pydna/parsers.py +45 -32
- pydna/primer.py +4 -4
- pydna/primer_screen.py +833 -0
- pydna/readers.py +14 -9
- pydna/seq.py +137 -47
- pydna/seqrecord.py +54 -62
- pydna/sequence_picker.py +2 -5
- pydna/sequence_regex.py +6 -6
- pydna/tm.py +17 -17
- pydna/types.py +19 -19
- pydna/utils.py +97 -75
- {pydna-5.5.4.dist-info → pydna-5.5.6.dist-info}/METADATA +8 -8
- pydna-5.5.6.dist-info/RECORD +42 -0
- {pydna-5.5.4.dist-info → pydna-5.5.6.dist-info}/WHEEL +1 -1
- pydna/conftest.py +0 -42
- pydna/download.py +0 -32
- pydna/genbankfile.py +0 -42
- pydna/genbankrecord.py +0 -168
- pydna/goldengate.py +0 -45
- pydna/ligate.py +0 -62
- pydna/user_cloning.py +0 -29
- pydna-5.5.4.dist-info/RECORD +0 -46
- {pydna-5.5.4.dist-info → pydna-5.5.6.dist-info}/licenses/LICENSE.txt +0 -0
pydna/tm.py
CHANGED
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
"""This module provide functions for melting temperature calculations."""
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
import math
|
|
12
|
-
from Bio.SeqUtils import MeltingTemp as
|
|
13
|
-
from Bio.SeqUtils import gc_fraction
|
|
11
|
+
import math
|
|
12
|
+
from Bio.SeqUtils import MeltingTemp as mt
|
|
13
|
+
from Bio.SeqUtils import gc_fraction
|
|
14
14
|
|
|
15
|
-
import textwrap
|
|
16
|
-
from pydna._pretty import pretty_str as
|
|
15
|
+
import textwrap
|
|
16
|
+
from pydna._pretty import pretty_str as ps
|
|
17
17
|
|
|
18
18
|
# See the documentation for Bio.SeqUtils.MeltingTemp for more details
|
|
19
19
|
# The 10X Taq Buffer with (NH4)2SO4 is commercialized by companies like
|
|
@@ -30,7 +30,7 @@ def tm_default(
|
|
|
30
30
|
strict=True,
|
|
31
31
|
c_seq=None,
|
|
32
32
|
shift=0,
|
|
33
|
-
nn_table=
|
|
33
|
+
nn_table=mt.DNA_NN4, # DNA_NN4: values from SantaLucia & Hicks (2004)
|
|
34
34
|
tmm_table=None,
|
|
35
35
|
imm_table=None,
|
|
36
36
|
de_table=None,
|
|
@@ -43,7 +43,7 @@ def tm_default(
|
|
|
43
43
|
Mg=1.5, # 1.5 mM Mg2+ is often seen in modern protocols
|
|
44
44
|
dNTPs=0.8, # I assume 200 µM of each dNTP
|
|
45
45
|
saltcorr=7, # Tm = 81.5 + 0.41(%GC) - 600/N + 16.6 x log[Na+]
|
|
46
|
-
func=
|
|
46
|
+
func=mt.Tm_NN, # Used by Primer3Plus to calculate the product Tm.
|
|
47
47
|
):
|
|
48
48
|
return func(
|
|
49
49
|
seq,
|
|
@@ -73,7 +73,7 @@ def tm_dbd(
|
|
|
73
73
|
strict=True,
|
|
74
74
|
c_seq=None,
|
|
75
75
|
shift=0,
|
|
76
|
-
nn_table=
|
|
76
|
+
nn_table=mt.DNA_NN3,
|
|
77
77
|
tmm_table=None,
|
|
78
78
|
imm_table=None,
|
|
79
79
|
de_table=None,
|
|
@@ -86,7 +86,7 @@ def tm_dbd(
|
|
|
86
86
|
Mg=1.5,
|
|
87
87
|
dNTPs=0.8,
|
|
88
88
|
saltcorr=1,
|
|
89
|
-
func=
|
|
89
|
+
func=mt.Tm_NN,
|
|
90
90
|
):
|
|
91
91
|
return func(
|
|
92
92
|
seq,
|
|
@@ -119,7 +119,7 @@ def tm_product(seq: str, K=0.050):
|
|
|
119
119
|
ing temperature for DNA amplification in vitro
|
|
120
120
|
http://www.ncbi.nlm.nih.gov/pubmed/2243783
|
|
121
121
|
"""
|
|
122
|
-
tmp = 81.5 + 0.41 *
|
|
122
|
+
tmp = 81.5 + 0.41 * gc_fraction(seq) * 100 + 16.6 * math.log10(K) - 675 / len(seq)
|
|
123
123
|
return tmp
|
|
124
124
|
|
|
125
125
|
|
|
@@ -160,7 +160,7 @@ def program(amplicon, tm=tm_default, ta=ta_default):
|
|
|
160
160
|
extension_time_taq = max(30, int(taq_extension_rate * len(amplicon) / 1000))
|
|
161
161
|
# seconds
|
|
162
162
|
|
|
163
|
-
f =
|
|
163
|
+
f = textwrap.dedent(
|
|
164
164
|
r"""
|
|
165
165
|
|95°C|95°C | |tmf:{tmf:.1f}
|
|
166
166
|
|____|_____ 72°C|72°C|tmr:{tmr:.1f}
|
|
@@ -185,7 +185,7 @@ def program(amplicon, tm=tm_default, ta=ta_default):
|
|
|
185
185
|
)
|
|
186
186
|
).strip()
|
|
187
187
|
|
|
188
|
-
return
|
|
188
|
+
return ps(f)
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
taq_program = program
|
|
@@ -228,7 +228,7 @@ def dbd_program(amplicon, tm=tm_dbd, ta=ta_dbd):
|
|
|
228
228
|
tmr = tm(amplicon.reverse_primer.footprint)
|
|
229
229
|
|
|
230
230
|
if tmf >= 69.0 and tmr >= 69.0:
|
|
231
|
-
f =
|
|
231
|
+
f = textwrap.dedent(
|
|
232
232
|
r"""
|
|
233
233
|
|98°C|98°C | |tmf:{tmf:.1f}
|
|
234
234
|
|____|____ | |tmr:{tmr:.1f}
|
|
@@ -245,7 +245,7 @@ def dbd_program(amplicon, tm=tm_dbd, ta=ta_dbd):
|
|
|
245
245
|
)
|
|
246
246
|
).strip()
|
|
247
247
|
else:
|
|
248
|
-
f =
|
|
248
|
+
f = textwrap.dedent(
|
|
249
249
|
r"""
|
|
250
250
|
|98°C|98°C | |tmf:{tmf:.1f}
|
|
251
251
|
|____|_____ 72°C|72°C|tmr:{tmr:.1f}
|
|
@@ -270,7 +270,7 @@ def dbd_program(amplicon, tm=tm_dbd, ta=ta_dbd):
|
|
|
270
270
|
)
|
|
271
271
|
).strip()
|
|
272
272
|
|
|
273
|
-
return
|
|
273
|
+
return ps(f)
|
|
274
274
|
|
|
275
275
|
|
|
276
276
|
pfu_sso7d_program = dbd_program
|
|
@@ -327,8 +327,8 @@ def tmbresluc(primer: str, *args, primerc=500.0, saltc=50, **kwargs):
|
|
|
327
327
|
dS += _thermodynamic_data.dSBr[n1 - 97][n2 - 97]
|
|
328
328
|
|
|
329
329
|
tm = (
|
|
330
|
-
dH / (1.9872 *
|
|
331
|
-
+ (16.6 *
|
|
330
|
+
dH / (1.9872 * math.log(pri / 1600) + dS)
|
|
331
|
+
+ (16.6 * math.log(saltc)) / math.log(10)
|
|
332
332
|
) - 273.15
|
|
333
333
|
|
|
334
334
|
return tm
|
pydna/types.py
CHANGED
|
@@ -5,32 +5,32 @@ Types used in the pydna package.
|
|
|
5
5
|
|
|
6
6
|
from typing import (
|
|
7
7
|
TYPE_CHECKING,
|
|
8
|
-
Tuple
|
|
9
|
-
Union
|
|
10
|
-
TypeVar
|
|
11
|
-
Iterable
|
|
12
|
-
Callable
|
|
8
|
+
Tuple,
|
|
9
|
+
Union,
|
|
10
|
+
TypeVar,
|
|
11
|
+
Iterable,
|
|
12
|
+
Callable,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
# Import AbstractCut at runtime for CutSiteType
|
|
16
|
-
from Bio.Restriction.Restriction import AbstractCut
|
|
17
|
-
from pydna.crispr import _cas
|
|
16
|
+
from Bio.Restriction.Restriction import AbstractCut
|
|
17
|
+
from pydna.crispr import _cas
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
|
-
from Bio.Restriction import RestrictionBatch
|
|
20
|
+
from Bio.Restriction import RestrictionBatch
|
|
21
21
|
from pydna.dseq import Dseq
|
|
22
|
-
from Bio.SeqFeature import Location
|
|
23
|
-
from pydna.dseqrecord import Dseqrecord
|
|
22
|
+
from Bio.SeqFeature import Location
|
|
23
|
+
from pydna.dseqrecord import Dseqrecord
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
# To represent any subclass of Dseq
|
|
27
|
-
DseqType =
|
|
28
|
-
EnzymesType =
|
|
29
|
-
"EnzymesType", "
|
|
27
|
+
DseqType = TypeVar("DseqType", bound="Dseq")
|
|
28
|
+
EnzymesType = TypeVar(
|
|
29
|
+
"EnzymesType", "RestrictionBatch", Iterable["AbstractCut"], "AbstractCut"
|
|
30
30
|
)
|
|
31
|
-
CutSiteType =
|
|
32
|
-
AssemblyEdgeType =
|
|
33
|
-
AssemblySubFragmentType =
|
|
31
|
+
CutSiteType = Tuple[Tuple[int, int], Union[AbstractCut, None, _cas]]
|
|
32
|
+
AssemblyEdgeType = Tuple[int, int, "Location | None", "Location | None"]
|
|
33
|
+
AssemblySubFragmentType = Tuple[int, "Location | None", "Location | None"]
|
|
34
34
|
EdgeRepresentationAssembly = list[AssemblyEdgeType]
|
|
35
35
|
SubFragmentRepresentationAssembly = list[AssemblySubFragmentType]
|
|
36
36
|
|
|
@@ -38,7 +38,7 @@ SubFragmentRepresentationAssembly = list[AssemblySubFragmentType]
|
|
|
38
38
|
# Type alias that describes overlap between two sequences x and y
|
|
39
39
|
# the two first numbers are the positions where the overlap starts on x and y
|
|
40
40
|
# the third number is the length of the overlap
|
|
41
|
-
SequenceOverlap =
|
|
42
|
-
AssemblyAlgorithmType =
|
|
43
|
-
["
|
|
41
|
+
SequenceOverlap = Tuple[int, int, int]
|
|
42
|
+
AssemblyAlgorithmType = Callable[
|
|
43
|
+
["Dseqrecord", "Dseqrecord", int], list[SequenceOverlap]
|
|
44
44
|
]
|
pydna/utils.py
CHANGED
|
@@ -6,43 +6,30 @@
|
|
|
6
6
|
# as part of this package.
|
|
7
7
|
"""Miscellaneous functions."""
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import re as _re
|
|
15
|
-
|
|
16
|
-
# import logging as _logging
|
|
17
|
-
# import base64 as _base64
|
|
18
|
-
# import pickle as _pickle
|
|
19
|
-
# import hashlib as _hashlib
|
|
20
|
-
import keyword as _keyword
|
|
21
|
-
import collections as _collections
|
|
22
|
-
import itertools as _itertools
|
|
23
|
-
from copy import deepcopy as _deepcopy
|
|
24
|
-
|
|
25
|
-
import sys as _sys
|
|
26
|
-
import random
|
|
27
|
-
import subprocess as _subprocess
|
|
28
|
-
from bisect import bisect as _bisect
|
|
29
|
-
from math import ceil as _ceil
|
|
9
|
+
import re
|
|
10
|
+
import keyword
|
|
11
|
+
import collections
|
|
12
|
+
import itertools
|
|
13
|
+
from copy import deepcopy
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
import sys
|
|
16
|
+
import random
|
|
17
|
+
import subprocess
|
|
18
|
+
from bisect import bisect
|
|
19
|
+
from math import ceil
|
|
33
20
|
|
|
34
|
-
from
|
|
35
|
-
from
|
|
36
|
-
from
|
|
21
|
+
from pydna.codon import weights
|
|
22
|
+
from pydna.codon import rare_codons
|
|
23
|
+
from pydna.alphabet import basepair_dict
|
|
24
|
+
from pydna.alphabet import complement_table_for_dscode
|
|
25
|
+
from Bio.SeqFeature import SimpleLocation
|
|
26
|
+
from Bio.SeqFeature import CompoundLocation
|
|
27
|
+
from Bio.SeqFeature import Location
|
|
37
28
|
|
|
38
|
-
from typing import Union
|
|
29
|
+
from typing import Union, TypeVar, List
|
|
39
30
|
|
|
40
31
|
# For functions that take str or bytes as input and return str or bytes as output, matching the input type
|
|
41
|
-
StrOrBytes =
|
|
42
|
-
|
|
43
|
-
# _module_logger = _logging.getLogger("pydna." + __name__)
|
|
44
|
-
_ambiguous_dna_complement.update({"U": "A"})
|
|
45
|
-
_complement_table = _maketrans(_ambiguous_dna_complement)
|
|
32
|
+
StrOrBytes = TypeVar("StrOrBytes", str, bytes)
|
|
46
33
|
|
|
47
34
|
|
|
48
35
|
def three_frame_orfs(
|
|
@@ -55,7 +42,7 @@ def three_frame_orfs(
|
|
|
55
42
|
):
|
|
56
43
|
"""Overlapping orfs in three frames."""
|
|
57
44
|
# breakpoint()
|
|
58
|
-
limit =
|
|
45
|
+
limit = ceil(limit / 3) - 1
|
|
59
46
|
dna = dna.upper()
|
|
60
47
|
|
|
61
48
|
orfs = []
|
|
@@ -69,7 +56,7 @@ def three_frame_orfs(
|
|
|
69
56
|
|
|
70
57
|
for startindex in startdindices:
|
|
71
58
|
try:
|
|
72
|
-
stopindex = stopdindices[
|
|
59
|
+
stopindex = stopdindices[bisect(stopdindices, startindex)]
|
|
73
60
|
except IndexError:
|
|
74
61
|
pass
|
|
75
62
|
else:
|
|
@@ -90,7 +77,7 @@ def shift_location(original_location, shift, lim):
|
|
|
90
77
|
raise ValueError(
|
|
91
78
|
"Shift moves location below zero, use a `lim` to loop around if sequence is circular."
|
|
92
79
|
)
|
|
93
|
-
lim =
|
|
80
|
+
lim = sys.maxsize
|
|
94
81
|
|
|
95
82
|
for part in original_location.parts:
|
|
96
83
|
new_start = (part.start + shift) % lim
|
|
@@ -106,7 +93,7 @@ def shift_location(original_location, shift, lim):
|
|
|
106
93
|
# https://github.com/pydna-group/pydna/issues/195
|
|
107
94
|
|
|
108
95
|
if len(part) == 0:
|
|
109
|
-
newparts.append(
|
|
96
|
+
newparts.append(SimpleLocation(new_start, new_start, strand))
|
|
110
97
|
continue
|
|
111
98
|
# Join with old, case 1
|
|
112
99
|
elif strand != -1 and old_end == new_start:
|
|
@@ -119,12 +106,15 @@ def shift_location(original_location, shift, lim):
|
|
|
119
106
|
part._start = new_start
|
|
120
107
|
new_end = part.end
|
|
121
108
|
if new_start < new_end:
|
|
122
|
-
newparts.append(
|
|
109
|
+
newparts.append(SimpleLocation(new_start, new_end, strand))
|
|
123
110
|
else:
|
|
124
|
-
parttuple = (
|
|
111
|
+
parttuple = (
|
|
112
|
+
SimpleLocation(new_start, lim, strand),
|
|
113
|
+
SimpleLocation(0, new_end, strand),
|
|
114
|
+
)
|
|
125
115
|
newparts.extend(parttuple if strand != -1 else parttuple[::-1])
|
|
126
116
|
try:
|
|
127
|
-
newloc =
|
|
117
|
+
newloc = CompoundLocation(newparts)
|
|
128
118
|
except ValueError:
|
|
129
119
|
newloc, *n = newparts
|
|
130
120
|
assert len(newloc) == len(original_location)
|
|
@@ -144,7 +134,7 @@ def shift_feature(feature, shift, lim):
|
|
|
144
134
|
"""Return a new feature with shifted location."""
|
|
145
135
|
# TODO: Missing tests
|
|
146
136
|
new_location = shift_location(feature.location, shift, lim)
|
|
147
|
-
new_feature =
|
|
137
|
+
new_feature = deepcopy(feature)
|
|
148
138
|
new_feature.location = new_location
|
|
149
139
|
return new_feature
|
|
150
140
|
|
|
@@ -210,16 +200,42 @@ def smallest_rotation(s):
|
|
|
210
200
|
return s[k:] + s[:k]
|
|
211
201
|
|
|
212
202
|
|
|
213
|
-
def
|
|
203
|
+
def anneal_from_left(watson: str, crick: str) -> int:
|
|
204
|
+
"""
|
|
205
|
+
The length of the common prefix shared by two strings.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
str1 (str): The first string.
|
|
209
|
+
str2 (str): The second string.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
int: The length of the common prefix.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
result = len(
|
|
216
|
+
list(
|
|
217
|
+
itertools.takewhile(
|
|
218
|
+
lambda x: basepair_dict.get((x[0], x[1])), zip(watson, crick[::-1])
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return result
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def cai(seq: str, organism: str = "sce", weights_dict: dict = None):
|
|
214
227
|
"""docstring."""
|
|
215
|
-
from cai2 import CAI
|
|
228
|
+
from cai2 import CAI
|
|
216
229
|
|
|
217
|
-
|
|
230
|
+
if weights_dict is None:
|
|
231
|
+
weights_dict = weights
|
|
232
|
+
|
|
233
|
+
return round(CAI(seq.upper(), weights=weights_dict[organism]), 3)
|
|
218
234
|
|
|
219
235
|
|
|
220
236
|
def rarecodons(seq: str, organism="sce"):
|
|
221
237
|
"""docstring."""
|
|
222
|
-
rare =
|
|
238
|
+
rare = rare_codons[organism]
|
|
223
239
|
s = seq.upper()
|
|
224
240
|
slices = []
|
|
225
241
|
for i in range(0, len(seq) // 3):
|
|
@@ -260,13 +276,13 @@ def express(seq: str, organism="sce"):
|
|
|
260
276
|
|
|
261
277
|
def open_folder(pth):
|
|
262
278
|
"""docstring."""
|
|
263
|
-
if
|
|
264
|
-
|
|
265
|
-
elif
|
|
266
|
-
|
|
279
|
+
if sys.platform == "win32":
|
|
280
|
+
subprocess.run(["start", pth], shell=True)
|
|
281
|
+
elif sys.platform == "darwin":
|
|
282
|
+
subprocess.run(["open", pth])
|
|
267
283
|
else:
|
|
268
284
|
try:
|
|
269
|
-
|
|
285
|
+
subprocess.run(["xdg-open", pth])
|
|
270
286
|
except OSError:
|
|
271
287
|
return "no cache to open."
|
|
272
288
|
|
|
@@ -276,15 +292,15 @@ def rc(sequence: StrOrBytes) -> StrOrBytes:
|
|
|
276
292
|
|
|
277
293
|
accepts mixed DNA/RNA
|
|
278
294
|
"""
|
|
279
|
-
return sequence
|
|
295
|
+
return complement(sequence)[::-1]
|
|
280
296
|
|
|
281
297
|
|
|
282
|
-
def complement(sequence:
|
|
298
|
+
def complement(sequence: StrOrBytes) -> StrOrBytes:
|
|
283
299
|
"""Complement.
|
|
284
300
|
|
|
285
301
|
accepts mixed DNA/RNA
|
|
286
302
|
"""
|
|
287
|
-
return sequence.translate(
|
|
303
|
+
return sequence.translate(complement_table_for_dscode)
|
|
288
304
|
|
|
289
305
|
|
|
290
306
|
# def memorize(filename):
|
|
@@ -295,17 +311,16 @@ def complement(sequence: str):
|
|
|
295
311
|
|
|
296
312
|
# def decorator(f):
|
|
297
313
|
# def wrappee(*args, **kwargs):
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
# _module_logger.info(
|
|
314
|
+
|
|
315
|
+
|
|
301
316
|
# "os.environ['pydna_cached_funcs'] = %s",
|
|
302
317
|
# _os.getenv("pydna_cached_funcs", ""),
|
|
303
318
|
# )
|
|
304
319
|
# if filename not in _os.getenv("pydna_cached_funcs", ""):
|
|
305
|
-
|
|
320
|
+
|
|
306
321
|
# return f(*args, **kwargs)
|
|
307
322
|
# key = _base64.urlsafe_b64encode(_hashlib.sha1(_pickle.dumps((args, kwargs))).digest()).decode("ascii")
|
|
308
|
-
|
|
323
|
+
|
|
309
324
|
# cache = _shelve.open(
|
|
310
325
|
# _os.path.join(_os.environ["pydna_data_dir"], identifier_from_string(filename)),
|
|
311
326
|
# writeback=False,
|
|
@@ -313,17 +328,17 @@ def complement(sequence: str):
|
|
|
313
328
|
# try:
|
|
314
329
|
# result = cache[key]
|
|
315
330
|
# except KeyError:
|
|
316
|
-
|
|
331
|
+
|
|
317
332
|
# "no result for key %s in shelve %s",
|
|
318
333
|
# key,
|
|
319
334
|
# identifier_from_string(filename),
|
|
320
335
|
# )
|
|
321
336
|
# result = f(*args, **kwargs)
|
|
322
|
-
|
|
337
|
+
|
|
323
338
|
# cache[key] = result
|
|
324
|
-
|
|
339
|
+
|
|
325
340
|
# else:
|
|
326
|
-
|
|
341
|
+
|
|
327
342
|
# cache.close()
|
|
328
343
|
# return result
|
|
329
344
|
|
|
@@ -338,16 +353,16 @@ def identifier_from_string(s: str) -> str:
|
|
|
338
353
|
based on the argument s or an empty string
|
|
339
354
|
"""
|
|
340
355
|
s = s.strip()
|
|
341
|
-
s =
|
|
356
|
+
s = re.sub(r"\s+", r"_", s)
|
|
342
357
|
s.replace("-", "_")
|
|
343
|
-
s =
|
|
344
|
-
if s and not s[0].isidentifier() or
|
|
358
|
+
s = re.sub("[^0-9a-zA-Z_]", "", s)
|
|
359
|
+
if s and not s[0].isidentifier() or keyword.iskeyword(s):
|
|
345
360
|
s = "_{s}".format(s=s)
|
|
346
361
|
assert s == "" or s.isidentifier()
|
|
347
362
|
return s
|
|
348
363
|
|
|
349
364
|
|
|
350
|
-
def flatten(*args) ->
|
|
365
|
+
def flatten(*args) -> List:
|
|
351
366
|
"""Flattens an iterable of iterables.
|
|
352
367
|
|
|
353
368
|
Down to str, bytes, bytearray or any of the pydna or Biopython seq objects
|
|
@@ -357,7 +372,7 @@ def flatten(*args) -> _List:
|
|
|
357
372
|
while args:
|
|
358
373
|
top = args.pop()
|
|
359
374
|
if (
|
|
360
|
-
isinstance(top,
|
|
375
|
+
isinstance(top, collections.abc.Iterable)
|
|
361
376
|
and not isinstance(top, (str, bytes, bytearray))
|
|
362
377
|
and not hasattr(top, "reverse_complement")
|
|
363
378
|
):
|
|
@@ -653,12 +668,12 @@ def eq(*args, **kwargs):
|
|
|
653
668
|
|
|
654
669
|
if topology == "circular":
|
|
655
670
|
# force circular comparison of all given sequences
|
|
656
|
-
for s1, s2 in
|
|
671
|
+
for s1, s2 in itertools.combinations(args_string_list, 2):
|
|
657
672
|
if not (s1 in s2 + s2 or rc(s1) in s2 + s2):
|
|
658
673
|
same = False
|
|
659
674
|
elif topology == "linear":
|
|
660
675
|
# force linear comparison of all given sequences
|
|
661
|
-
for s1, s2 in
|
|
676
|
+
for s1, s2 in itertools.combinations(args_string_list, 2):
|
|
662
677
|
if not (s1 == s2 or s1 == rc(s2)):
|
|
663
678
|
same = False
|
|
664
679
|
return same
|
|
@@ -697,6 +712,7 @@ def eq(*args, **kwargs):
|
|
|
697
712
|
|
|
698
713
|
|
|
699
714
|
def cuts_overlap(left_cut, right_cut, seq_len):
|
|
715
|
+
|
|
700
716
|
# Special cases:
|
|
701
717
|
if left_cut is None or right_cut is None or left_cut == right_cut:
|
|
702
718
|
return False
|
|
@@ -718,17 +734,23 @@ def cuts_overlap(left_cut, right_cut, seq_len):
|
|
|
718
734
|
# Convert into ranges x and y and see if ranges overlap
|
|
719
735
|
x = sorted([left_watson, left_crick])
|
|
720
736
|
y = sorted([right_watson, right_crick])
|
|
721
|
-
|
|
737
|
+
# if (x[1] >= y[0]) != (y[1] <= x[0]):
|
|
738
|
+
# breakpoint()
|
|
739
|
+
return (x[1] >= y[0]) != (y[1] <= x[0]) # (x[1] > y[0]) != (y[1] < x[0])
|
|
722
740
|
|
|
723
741
|
|
|
724
|
-
def location_boundaries(loc:
|
|
742
|
+
def location_boundaries(loc: Union[SimpleLocation, CompoundLocation]):
|
|
725
743
|
if loc.strand == -1:
|
|
726
744
|
return loc.parts[-1].start, loc.parts[0].end
|
|
727
745
|
else:
|
|
728
746
|
return loc.parts[0].start, loc.parts[-1].end
|
|
729
747
|
|
|
730
748
|
|
|
731
|
-
def locations_overlap(
|
|
749
|
+
def locations_overlap(
|
|
750
|
+
loc1: Union[SimpleLocation, CompoundLocation],
|
|
751
|
+
loc2: Union[SimpleLocation, CompoundLocation],
|
|
752
|
+
seq_len,
|
|
753
|
+
):
|
|
732
754
|
start1, end1 = location_boundaries(loc1)
|
|
733
755
|
start2, end2 = location_boundaries(loc2)
|
|
734
756
|
|
|
@@ -746,7 +768,7 @@ def locations_overlap(loc1: _Union[_sl, _cl], loc2: _Union[_sl, _cl], seq_len):
|
|
|
746
768
|
[start2 - seq_len, end2],
|
|
747
769
|
]
|
|
748
770
|
|
|
749
|
-
for b1, b2 in
|
|
771
|
+
for b1, b2 in itertools.product(boundaries1, boundaries2):
|
|
750
772
|
if b1[0] < b2[1] and b1[1] > b2[0]:
|
|
751
773
|
return True
|
|
752
774
|
|
|
@@ -801,7 +823,7 @@ def limit_iterator(iterator, limit):
|
|
|
801
823
|
|
|
802
824
|
def create_location(
|
|
803
825
|
start: int, end: int, lim: int, strand: int | None = None
|
|
804
|
-
) ->
|
|
826
|
+
) -> Location:
|
|
805
827
|
"""
|
|
806
828
|
Create a location object from a start and end position.
|
|
807
829
|
If the end position is less than the start position, the location is circular. It handles negative positions.
|
|
@@ -854,6 +876,6 @@ def create_location(
|
|
|
854
876
|
while end < 0:
|
|
855
877
|
end += lim
|
|
856
878
|
if end > start:
|
|
857
|
-
return
|
|
879
|
+
return SimpleLocation(start, end, strand)
|
|
858
880
|
else:
|
|
859
|
-
return shift_location(
|
|
881
|
+
return shift_location(SimpleLocation(start, end + lim, strand), 0, lim)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydna
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.6
|
|
4
4
|
Summary: Representing double stranded DNA and functions for simulating cloning and homologous recombination between DNA molecules.
|
|
5
5
|
License: BSD
|
|
6
6
|
License-File: LICENSE.txt
|
|
@@ -23,6 +23,7 @@ Provides-Extra: clipboard
|
|
|
23
23
|
Provides-Extra: download
|
|
24
24
|
Provides-Extra: express
|
|
25
25
|
Provides-Extra: gel
|
|
26
|
+
Provides-Extra: primer-screen
|
|
26
27
|
Requires-Dist: appdirs (>=1.4.4)
|
|
27
28
|
Requires-Dist: biopython (==1.85)
|
|
28
29
|
Requires-Dist: cai2 (>=1.0.5) ; extra == "express"
|
|
@@ -30,9 +31,10 @@ Requires-Dist: matplotlib (>=3.4.3) ; extra == "gel"
|
|
|
30
31
|
Requires-Dist: networkx (>=2.8.8)
|
|
31
32
|
Requires-Dist: numpy (>1.26) ; python_version < "3.12"
|
|
32
33
|
Requires-Dist: numpy (>=2.3.0) ; python_version >= "3.12"
|
|
33
|
-
Requires-Dist: opencloning-linkml (
|
|
34
|
+
Requires-Dist: opencloning-linkml (>=0.4.9,<0.5.0)
|
|
34
35
|
Requires-Dist: pillow (>=8.4.0) ; extra == "gel"
|
|
35
36
|
Requires-Dist: prettytable (>=3.5.0)
|
|
37
|
+
Requires-Dist: pyahocorasick (>=2.2.0) ; extra == "primer-screen"
|
|
36
38
|
Requires-Dist: pydivsufsort (>=0.0.14)
|
|
37
39
|
Requires-Dist: pyfiglet (==0.8.post1)
|
|
38
40
|
Requires-Dist: pyparsing (>=2.4.7) ; extra == "download"
|
|
@@ -506,7 +508,7 @@ poetry run pre-commit install
|
|
|
506
508
|
> =================================== FAILURES ===================================
|
|
507
509
|
> ___________________ [doctest] pydna.assembly2.blunt_overlap ____________________
|
|
508
510
|
> ```
|
|
509
|
-
> This means that the doctest of the function `blunt_overlap` failed. You can run the same test locally with `
|
|
511
|
+
> This means that the doctest of the function `blunt_overlap` failed. You can run the same test locally with `pytest src/pydna --doctest-modules` (use the appropriate path to the module file). That will give you information of what's failing. Fix, and re-run until it passes!
|
|
510
512
|
|
|
511
513
|
### Creating a PR 🔗
|
|
512
514
|
|
|
@@ -531,17 +533,15 @@ To work locally with the documentation, check the [documentation README](docs/RE
|
|
|
531
533
|
See the [releases](https://github.com/pydna-group/pydna/releases) for changes and releases.
|
|
532
534
|
|
|
533
535
|
The build workflow builds a PyPI packages using poetry. This workflow is triggered by publishing a Github release manually from the Github web interface.
|
|
536
|
+
We keep future release names [here](https://docs.google.com/document/d/1PrBYKzDh6QBcqfH9ksjpgArJo3ibDhMRNcibfXtYmCc/edit?tab=t.0). Please edit to
|
|
537
|
+
reflect used release names.
|
|
534
538
|
|
|
535
|
-

|
|
536
540
|
|
|
537
541
|
## History 📜
|
|
538
542
|
|
|
539
543
|
Pydna was made public in 2012 on [Google code](https://code.google.com/archive/p/pydna).
|
|
540
544
|
|
|
541
|
-
:microbe:
|
|
542
|
-
|
|
543
|
-
:portugal:
|
|
544
|
-
|
|
545
545
|
## Who is using pydna? 🧪
|
|
546
546
|
|
|
547
547
|
Taylor, L. J., & Strebel, K. (2017).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
pydna/__init__.py,sha256=4u9wd3RbQkivNRKpEGXHT7Lrk7W7G5V64PJLOiSXKAk,7086
|
|
2
|
+
pydna/_pretty.py,sha256=S3J0z_czeP1HpR-lj5fXQo9OeZc3ONsvxEGAc-Oqvjo,885
|
|
3
|
+
pydna/_thermodynamic_data.py,sha256=ctOCzI0SclCBQVk7tG01bDv76fMeOROrp_WdVG5jp20,10885
|
|
4
|
+
pydna/all.py,sha256=jPeYTAPh5uNXnqsK-HfMFzlVXE6sSqsonHCgmt6lf2I,1502
|
|
5
|
+
pydna/alphabet.py,sha256=NOWIcsVjdPSR9aAZx5wzWD8061Pq2fOE1qAKfjqDzGg,29654
|
|
6
|
+
pydna/amplicon.py,sha256=zql6MVqoKu9XEDlEmqxCQa5oZMDk3pyayP536k-yS38,5135
|
|
7
|
+
pydna/amplify.py,sha256=DsMDJi6bRmP1CMku-jZ1_QXQ3-gbQ_P2M9kHR30zJ-Y,18738
|
|
8
|
+
pydna/assembly.py,sha256=1bAaFZDAEAlz_LB31sjAPRtAkIFK_VIXpMIQpTf_8xU,19291
|
|
9
|
+
pydna/assembly2.py,sha256=WmzygtYKLONRoccaqHM5O3xjbNzaSeOHR15EH-tVOmk,108764
|
|
10
|
+
pydna/codon.py,sha256=AZb8DjrS5lBAzNhL32GvE_P9V5DvSBbc7w9TaSeQHlc,2553
|
|
11
|
+
pydna/common_sub_strings.py,sha256=pnSx3OAjwf2uclYxv-90XYZT3bMl_d8ZTDeoGtlgtWs,11467
|
|
12
|
+
pydna/contig.py,sha256=yBF4TMq1pLL0Pe3tefl2sDzNFaeSddBpytN-z-xBeSw,14971
|
|
13
|
+
pydna/cre_lox.py,sha256=sOj9R8_oFPGWs68vc4jf6LqWOXjMsVSwtJaeKl6ZF2M,4476
|
|
14
|
+
pydna/crispr.py,sha256=iwu0Cjskzdb2r-In9633QIxQYKGmSNVOEM_dLK6lC54,3619
|
|
15
|
+
pydna/design.py,sha256=GQTyH4fKsvd6fSel0uC8DNQIulq3HMg-jYHxqsQzoLw,31743
|
|
16
|
+
pydna/dseq.py,sha256=cZN1Micwlgt8L73WjaIUlblb9KuQBxLzhM91cd5ijKo,83324
|
|
17
|
+
pydna/dseqrecord.py,sha256=jzo8jr6i4zZ6SdJJfbabdkGh_6U8PI0Ywa9LCdn91XE,49598
|
|
18
|
+
pydna/fakeseq.py,sha256=uxyu1PXF-sVasEqhmyhMcsbpTy84uUtubDiZuxHNb9c,1174
|
|
19
|
+
pydna/fusionpcr.py,sha256=tfIfGGkNoZjg_dodcEx9wTc12aLRnC7J3g-Vg3Jg1uA,2821
|
|
20
|
+
pydna/gateway.py,sha256=2AluaHCvgWwIq3f4ICK27iX1uSSpg97F3c_xv4o0lQM,8651
|
|
21
|
+
pydna/gel.py,sha256=cZ8IctRO62BXK3BQe4_8c4IgOOspHawi1WZKr0PNGss,3338
|
|
22
|
+
pydna/genbank.py,sha256=TZExxq5NVAL23Rb4y_tdbN32FFTUE_kddb_Zpv5RO6E,8574
|
|
23
|
+
pydna/genbankfixer.py,sha256=ackTNoGk9aiXdWeH4ReXjGPmhTh9kl3FkfHsnZPi_eY,20302
|
|
24
|
+
pydna/ladders.py,sha256=2Y5gcqmxCXdYFgW4MAosbqISSHD_uEQj_05ZYBREBOY,3284
|
|
25
|
+
pydna/oligonucleotide_hybridization.py,sha256=x75pVVNvUn8N0F2j8fY_oOT61VlrlFauk0Iq5QFJ2XY,4165
|
|
26
|
+
pydna/opencloning_models.py,sha256=gMWAwcxDQx7T4xXftUTKA99NHdA7CCRSMB0HFJ5UXok,23892
|
|
27
|
+
pydna/parsers.py,sha256=mAkkkIv14KFmZt0TrROtE0Z54otpB11MrM3ihhdd3Ow,7847
|
|
28
|
+
pydna/primer.py,sha256=s3CoPheB4PUuQJU_11VGEBZW62DesEiWmTC2HHzD7so,2248
|
|
29
|
+
pydna/primer_screen.py,sha256=0CodBc9JThE-qkk5Z2DacSYIvSqhsFCGARzh-Bu236U,26418
|
|
30
|
+
pydna/readers.py,sha256=9ZopQFW0CyYCmW4jy-6_w0_0BRUMLV2VAVDmHH4Ns5w,1731
|
|
31
|
+
pydna/seq.py,sha256=H-MPWo2qQG6s5N0LKSziTKmb79ORCCNHofE2Gs0R5U8,10979
|
|
32
|
+
pydna/seqrecord.py,sha256=aQtkx8Wjy_FbnD2_uVY--ULuWwLtxiVeQ0wlsZs2OXA,22816
|
|
33
|
+
pydna/sequence_picker.py,sha256=wuRfQhuMgxrK6rrNykBFbRqErk-qXBPRvmEEIM0ji4o,1305
|
|
34
|
+
pydna/sequence_regex.py,sha256=bp0JhUILRlXQ-zOjK6oz-cxjca46LBjA5wCuzga-Hmw,1238
|
|
35
|
+
pydna/threading_timer_decorator_exit.py,sha256=D91kqjKSavWDnXyc1Fo-CwPYtbmR2DjTXnBYSRXKmSA,2793
|
|
36
|
+
pydna/tm.py,sha256=kM31byeBog17xi-NnVL6yXPLyZrzpXRY9J6XfEAJa9I,11083
|
|
37
|
+
pydna/types.py,sha256=W1qY6TSRv1nIj9dxEwTlc4XB3778arSeMg713fPnT9U,1337
|
|
38
|
+
pydna/utils.py,sha256=vYJJnJlaSTVxmbusv-WDNid54nBUqs8pTFDcJG_I7tA,24989
|
|
39
|
+
pydna-5.5.6.dist-info/METADATA,sha256=jUQ1ZJ5NYSJe71DHPnRWDYF1_pUeWqoMeOcLvYjitFE,24626
|
|
40
|
+
pydna-5.5.6.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
41
|
+
pydna-5.5.6.dist-info/licenses/LICENSE.txt,sha256=u8QfcsnNXZM0UCexerK_MvyA2lPWgeGyUtSYXvLG6Oc,6119
|
|
42
|
+
pydna-5.5.6.dist-info/RECORD,,
|