pydna 5.5.1__py3-none-any.whl → 5.5.2__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 +116 -134
- pydna/_pretty.py +2 -14
- pydna/all.py +10 -20
- pydna/amplicon.py +25 -20
- pydna/amplify.py +46 -26
- pydna/assembly.py +50 -27
- pydna/assembly2.py +1902 -0
- pydna/common_sub_strings.py +2 -12
- pydna/contig.py +39 -22
- pydna/crispr.py +8 -13
- pydna/design.py +89 -59
- pydna/download.py +10 -18
- pydna/dseq.py +119 -59
- pydna/dseqrecord.py +88 -45
- pydna/fakeseq.py +0 -11
- pydna/fusionpcr.py +3 -1
- pydna/gateway.py +2 -2
- pydna/gel.py +8 -13
- pydna/genbank.py +33 -32
- pydna/genbankfile.py +8 -13
- pydna/genbankfixer.py +41 -28
- pydna/genbankrecord.py +11 -14
- pydna/goldengate.py +2 -2
- pydna/ladders.py +4 -11
- pydna/ligate.py +8 -14
- pydna/parsers.py +5 -12
- pydna/primer.py +3 -12
- pydna/readers.py +0 -11
- pydna/seq.py +21 -18
- pydna/seqrecord.py +19 -19
- pydna/sequence_picker.py +3 -12
- pydna/tm.py +13 -15
- pydna/types.py +41 -0
- pydna/utils.py +173 -58
- {pydna-5.5.1.dist-info → pydna-5.5.2.dist-info}/METADATA +17 -3
- pydna-5.5.2.dist-info/RECORD +43 -0
- pydna/editor.py +0 -119
- pydna/myenzymes.py +0 -51
- pydna/myprimers.py +0 -219
- pydna-5.5.1.dist-info/RECORD +0 -44
- {pydna-5.5.1.dist-info → pydna-5.5.2.dist-info}/LICENSE.txt +0 -0
- {pydna-5.5.1.dist-info → pydna-5.5.2.dist-info}/WHEEL +0 -0
pydna/types.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Types used in the pydna package.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import (
|
|
7
|
+
TYPE_CHECKING,
|
|
8
|
+
Tuple as _Tuple,
|
|
9
|
+
Union as _Union,
|
|
10
|
+
TypeVar as _TypeVar,
|
|
11
|
+
Iterable as _Iterable,
|
|
12
|
+
Callable as _Callable,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from Bio.Restriction import AbstractCut as _AbstractCut
|
|
17
|
+
from Bio.Restriction import RestrictionBatch as _RestrictionBatch
|
|
18
|
+
from pydna.dseq import Dseq
|
|
19
|
+
from Bio.SeqFeature import Location as _Location
|
|
20
|
+
from pydna.dseqrecord import Dseqrecord as _Dseqrecord
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# To represent any subclass of Dseq
|
|
24
|
+
DseqType = _TypeVar("DseqType", bound="Dseq")
|
|
25
|
+
EnzymesType = _TypeVar(
|
|
26
|
+
"EnzymesType", "_RestrictionBatch", _Iterable["_AbstractCut"], "_AbstractCut"
|
|
27
|
+
)
|
|
28
|
+
CutSiteType = _Tuple[_Tuple[int, int], _Union["_AbstractCut", None]]
|
|
29
|
+
AssemblyEdgeType = _Tuple[int, int, "_Location | None", "_Location | None"]
|
|
30
|
+
AssemblySubFragmentType = _Tuple[int, "_Location | None", "_Location | None"]
|
|
31
|
+
EdgeRepresentationAssembly = list[AssemblyEdgeType]
|
|
32
|
+
SubFragmentRepresentationAssembly = list[AssemblySubFragmentType]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Type alias that describes overlap between two sequences x and y
|
|
36
|
+
# the two first numbers are the positions where the overlap starts on x and y
|
|
37
|
+
# the third number is the length of the overlap
|
|
38
|
+
SequenceOverlap = _Tuple[int, int, int]
|
|
39
|
+
AssemblyAlgorithmType = _Callable[
|
|
40
|
+
["_Dseqrecord", "_Dseqrecord", int], list[SequenceOverlap]
|
|
41
|
+
]
|
pydna/utils.py
CHANGED
|
@@ -8,13 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
from Bio.Data.IUPACData import ambiguous_dna_complement as _ambiguous_dna_complement
|
|
10
10
|
from Bio.Seq import _maketrans
|
|
11
|
-
|
|
12
|
-
import
|
|
11
|
+
|
|
12
|
+
# import shelve as _shelve
|
|
13
|
+
# import os as _os
|
|
13
14
|
import re as _re
|
|
14
|
-
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
15
|
+
|
|
16
|
+
# import logging as _logging
|
|
17
|
+
# import base64 as _base64
|
|
18
|
+
# import pickle as _pickle
|
|
19
|
+
# import hashlib as _hashlib
|
|
18
20
|
import keyword as _keyword
|
|
19
21
|
import collections as _collections
|
|
20
22
|
import itertools as _itertools
|
|
@@ -31,13 +33,14 @@ from pydna.codon import rare_codons as _rare_codons
|
|
|
31
33
|
|
|
32
34
|
from Bio.SeqFeature import SimpleLocation as _sl
|
|
33
35
|
from Bio.SeqFeature import CompoundLocation as _cl
|
|
36
|
+
from Bio.SeqFeature import Location as _Location
|
|
34
37
|
|
|
35
38
|
from typing import Union as _Union, TypeVar as _TypeVar, List as _List
|
|
36
39
|
|
|
37
40
|
# For functions that take str or bytes as input and return str or bytes as output, matching the input type
|
|
38
41
|
StrOrBytes = _TypeVar("StrOrBytes", str, bytes)
|
|
39
42
|
|
|
40
|
-
_module_logger = _logging.getLogger("pydna." + __name__)
|
|
43
|
+
# _module_logger = _logging.getLogger("pydna." + __name__)
|
|
41
44
|
_ambiguous_dna_complement.update({"U": "A"})
|
|
42
45
|
_complement_table = _maketrans(_ambiguous_dna_complement)
|
|
43
46
|
|
|
@@ -71,7 +74,9 @@ def three_frame_orfs(
|
|
|
71
74
|
pass
|
|
72
75
|
else:
|
|
73
76
|
if stopindex - startindex >= limit:
|
|
74
|
-
orfs.append(
|
|
77
|
+
orfs.append(
|
|
78
|
+
(frame, startindex * 3 + frame, (stopindex + 1) * 3 + frame)
|
|
79
|
+
)
|
|
75
80
|
# print(stopindex, startindex, limit)
|
|
76
81
|
return orfs
|
|
77
82
|
|
|
@@ -82,13 +87,17 @@ def shift_location(original_location, shift, lim):
|
|
|
82
87
|
strand = original_location.strand
|
|
83
88
|
if lim is None:
|
|
84
89
|
if min(original_location) + shift < 0:
|
|
85
|
-
raise ValueError(
|
|
90
|
+
raise ValueError(
|
|
91
|
+
"Shift moves location below zero, use a `lim` to loop around if sequence is circular."
|
|
92
|
+
)
|
|
86
93
|
lim = _sys.maxsize
|
|
87
94
|
|
|
88
95
|
for part in original_location.parts:
|
|
89
96
|
new_start = (part.start + shift) % lim
|
|
90
97
|
new_end = (part.end + shift) % lim or lim
|
|
91
|
-
old_start, old_end = (
|
|
98
|
+
old_start, old_end = (
|
|
99
|
+
(newparts[-1].start, newparts[-1].end) if len(newparts) else (None, None)
|
|
100
|
+
)
|
|
92
101
|
|
|
93
102
|
# The "join with old" cases are for features with multiple parts
|
|
94
103
|
# in which consecutive parts do not have any bases between them.
|
|
@@ -278,49 +287,49 @@ def complement(sequence: str):
|
|
|
278
287
|
return sequence.translate(_complement_table)
|
|
279
288
|
|
|
280
289
|
|
|
281
|
-
def memorize(filename):
|
|
282
|
-
|
|
290
|
+
# def memorize(filename):
|
|
291
|
+
# """Cache functions and classes.
|
|
283
292
|
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
# see pydna.download
|
|
294
|
+
# """
|
|
286
295
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
296
|
+
# def decorator(f):
|
|
297
|
+
# def wrappee(*args, **kwargs):
|
|
298
|
+
# _module_logger.info("#### memorizer ####")
|
|
299
|
+
# _module_logger.info("cache filename = %s", filename)
|
|
300
|
+
# _module_logger.info(
|
|
301
|
+
# "os.environ['pydna_cached_funcs'] = %s",
|
|
302
|
+
# _os.getenv("pydna_cached_funcs", ""),
|
|
303
|
+
# )
|
|
304
|
+
# if filename not in _os.getenv("pydna_cached_funcs", ""):
|
|
305
|
+
# _module_logger.info("cache filename not among cached functions, made it new!")
|
|
306
|
+
# return f(*args, **kwargs)
|
|
307
|
+
# key = _base64.urlsafe_b64encode(_hashlib.sha1(_pickle.dumps((args, kwargs))).digest()).decode("ascii")
|
|
308
|
+
# _module_logger.info("key = %s", key)
|
|
309
|
+
# cache = _shelve.open(
|
|
310
|
+
# _os.path.join(_os.environ["pydna_data_dir"], identifier_from_string(filename)),
|
|
311
|
+
# writeback=False,
|
|
312
|
+
# )
|
|
313
|
+
# try:
|
|
314
|
+
# result = cache[key]
|
|
315
|
+
# except KeyError:
|
|
316
|
+
# _module_logger.info(
|
|
317
|
+
# "no result for key %s in shelve %s",
|
|
318
|
+
# key,
|
|
319
|
+
# identifier_from_string(filename),
|
|
320
|
+
# )
|
|
321
|
+
# result = f(*args, **kwargs)
|
|
322
|
+
# _module_logger.info("made it new!")
|
|
323
|
+
# cache[key] = result
|
|
324
|
+
# _module_logger.info("saved result under key %s", key)
|
|
325
|
+
# else:
|
|
326
|
+
# _module_logger.info("found %s in cache", key)
|
|
327
|
+
# cache.close()
|
|
328
|
+
# return result
|
|
320
329
|
|
|
321
|
-
|
|
330
|
+
# return wrappee
|
|
322
331
|
|
|
323
|
-
|
|
332
|
+
# return decorator
|
|
324
333
|
|
|
325
334
|
|
|
326
335
|
def identifier_from_string(s: str) -> str:
|
|
@@ -505,7 +514,11 @@ def randomORF(length, maxlength=None):
|
|
|
505
514
|
starts = ("ATG",)
|
|
506
515
|
stops = ("TAA", "TAG", "TGA")
|
|
507
516
|
|
|
508
|
-
return
|
|
517
|
+
return (
|
|
518
|
+
random.choice(starts)
|
|
519
|
+
+ "".join([random.choice(cdns) for x in range(length)])
|
|
520
|
+
+ random.choice(stops)
|
|
521
|
+
)
|
|
509
522
|
|
|
510
523
|
|
|
511
524
|
def randomprot(length, maxlength=None):
|
|
@@ -614,7 +627,9 @@ def eq(*args, **kwargs):
|
|
|
614
627
|
if kwargs["circular"] is False:
|
|
615
628
|
topology = "linear"
|
|
616
629
|
else:
|
|
617
|
-
topology = set(
|
|
630
|
+
topology = set(
|
|
631
|
+
[arg.circular if hasattr(arg, "circular") else None for arg in args]
|
|
632
|
+
)
|
|
618
633
|
|
|
619
634
|
if len(topology) != 1:
|
|
620
635
|
raise ValueError("sequences have different topologies")
|
|
@@ -625,7 +640,10 @@ def eq(*args, **kwargs):
|
|
|
625
640
|
topology = "circular"
|
|
626
641
|
|
|
627
642
|
args = [arg.seq if hasattr(arg, "seq") else arg for arg in args]
|
|
628
|
-
args_string_list = [
|
|
643
|
+
args_string_list = [
|
|
644
|
+
arg.watson.lower() if hasattr(arg, "watson") else str(arg).lower()
|
|
645
|
+
for arg in args
|
|
646
|
+
]
|
|
629
647
|
|
|
630
648
|
length = set((len(s) for s in args_string_list))
|
|
631
649
|
|
|
@@ -735,10 +753,107 @@ def locations_overlap(loc1: _Union[_sl, _cl], loc2: _Union[_sl, _cl], seq_len):
|
|
|
735
753
|
return False
|
|
736
754
|
|
|
737
755
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
756
|
+
def sum_is_sticky(
|
|
757
|
+
three_prime_end: tuple[str, str],
|
|
758
|
+
five_prime_end: tuple[str, str],
|
|
759
|
+
partial: bool = False,
|
|
760
|
+
) -> int:
|
|
761
|
+
"""Return the overlap length if the 3' end of seq1 and 5' end of seq2 ends are sticky and compatible for ligation.
|
|
762
|
+
Return 0 if they are not compatible."""
|
|
763
|
+
type_seq1, sticky_seq1 = three_prime_end
|
|
764
|
+
type_seq2, sticky_seq2 = five_prime_end
|
|
765
|
+
|
|
766
|
+
if (
|
|
767
|
+
"blunt" != type_seq2
|
|
768
|
+
and type_seq2 == type_seq1
|
|
769
|
+
and str(sticky_seq2) == str(rc(sticky_seq1))
|
|
770
|
+
):
|
|
771
|
+
return len(sticky_seq1)
|
|
772
|
+
|
|
773
|
+
if not partial:
|
|
774
|
+
return 0
|
|
775
|
+
|
|
776
|
+
if type_seq1 != type_seq2 or type_seq2 == "blunt":
|
|
777
|
+
return 0
|
|
778
|
+
elif type_seq2 == "5'":
|
|
779
|
+
sticky_seq1 = str(rc(sticky_seq1))
|
|
780
|
+
elif type_seq2 == "3'":
|
|
781
|
+
sticky_seq2 = str(rc(sticky_seq2))
|
|
782
|
+
|
|
783
|
+
ovhg_len = min(len(sticky_seq1), len(sticky_seq2))
|
|
784
|
+
# [::-1] to try the longest overhangs first
|
|
785
|
+
for i in range(1, ovhg_len + 1)[::-1]:
|
|
786
|
+
if sticky_seq1[-i:] == sticky_seq2[:i]:
|
|
787
|
+
return i
|
|
788
|
+
else:
|
|
789
|
+
return 0
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
def limit_iterator(iterator, limit):
|
|
793
|
+
"""
|
|
794
|
+
Call the function with an iterator to raise an error if the number of items is greater than the limit.
|
|
795
|
+
"""
|
|
796
|
+
for i, x in enumerate(iterator):
|
|
797
|
+
if i >= limit:
|
|
798
|
+
raise ValueError(f"Too many possible paths (more than {limit})")
|
|
799
|
+
yield x
|
|
800
|
+
|
|
801
|
+
|
|
802
|
+
def create_location(
|
|
803
|
+
start: int, end: int, lim: int, strand: int | None = None
|
|
804
|
+
) -> _Location:
|
|
805
|
+
"""
|
|
806
|
+
Create a location object from a start and end position.
|
|
807
|
+
If the end position is less than the start position, the location is circular. It handles negative positions.
|
|
808
|
+
|
|
809
|
+
Parameters
|
|
810
|
+
----------
|
|
811
|
+
start : int
|
|
812
|
+
The start position of the location.
|
|
813
|
+
end : int
|
|
814
|
+
The end position of the location.
|
|
815
|
+
lim : int
|
|
816
|
+
The length of the sequence.
|
|
817
|
+
strand : int, optional
|
|
818
|
+
The strand of the location. None, 1 or -1.
|
|
742
819
|
|
|
743
|
-
|
|
744
|
-
|
|
820
|
+
Returns
|
|
821
|
+
-------
|
|
822
|
+
location : Location
|
|
823
|
+
The location object. Can be a SimpleLocation or a CompoundLocation if the feature spans the origin of
|
|
824
|
+
a circular sequence.
|
|
825
|
+
|
|
826
|
+
Examples
|
|
827
|
+
--------
|
|
828
|
+
>>> from pydna.utils import create_location
|
|
829
|
+
>>> str(create_location(0, 5, 10,-1))
|
|
830
|
+
'[0:5](-)'
|
|
831
|
+
>>> str(create_location(0, 5, 10,+1))
|
|
832
|
+
'[0:5](+)'
|
|
833
|
+
>>> str(create_location(0, 5, 10))
|
|
834
|
+
'[0:5]'
|
|
835
|
+
>>> str(create_location(8, 2, 10))
|
|
836
|
+
'join{[8:10], [0:2]}'
|
|
837
|
+
>>> str(create_location(8, 2, 10,-1))
|
|
838
|
+
'join{[0:2](-), [8:10](-)}'
|
|
839
|
+
>>> str(create_location(-2, 2, 10))
|
|
840
|
+
'join{[8:10], [0:2]}'
|
|
841
|
+
|
|
842
|
+
Note this special case, 0 is the same as len(seq)
|
|
843
|
+
>>> str(create_location(5, 0, 10))
|
|
844
|
+
'[5:10]'
|
|
845
|
+
|
|
846
|
+
Note the special case where if start and end are the same,
|
|
847
|
+
the location spans the entire sequence (it's not empty).
|
|
848
|
+
>>> str(create_location(5, 5, 10))
|
|
849
|
+
'join{[5:10], [0:5]}'
|
|
850
|
+
|
|
851
|
+
"""
|
|
852
|
+
while start < 0:
|
|
853
|
+
start += lim
|
|
854
|
+
while end < 0:
|
|
855
|
+
end += lim
|
|
856
|
+
if end > start:
|
|
857
|
+
return _sl(start, end, strand)
|
|
858
|
+
else:
|
|
859
|
+
return shift_location(_sl(start, end + lim, strand), 0, lim)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pydna
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.2
|
|
4
4
|
Summary: Representing double stranded DNA and functions for simulating cloning and homologous recombination between DNA molecules.
|
|
5
5
|
License: BSD
|
|
6
6
|
Author: Björn F. Johansson
|
|
@@ -36,6 +36,7 @@ Requires-Dist: pydivsufsort (>=0.0.14)
|
|
|
36
36
|
Requires-Dist: pyfiglet (==0.8.post1)
|
|
37
37
|
Requires-Dist: pyparsing (>=2.4.7) ; extra == "download"
|
|
38
38
|
Requires-Dist: pyperclip (>=1.8.2) ; extra == "clipboard"
|
|
39
|
+
Requires-Dist: regex (>=2024.11.6,<2025.0.0)
|
|
39
40
|
Requires-Dist: requests (>=2.26.0) ; extra == "download"
|
|
40
41
|
Requires-Dist: scipy (>=1.11.3) ; (python_version >= "3.12") and (extra == "gel")
|
|
41
42
|
Requires-Dist: scipy (>=1.9.3) ; (python_version < "3.12") and (extra == "gel")
|
|
@@ -46,11 +47,11 @@ Project-URL: Homepage, https://github.com/pydna-group/pydna#-pydna
|
|
|
46
47
|
Project-URL: Repository, https://github.com/pydna-group/pydna/tree/master
|
|
47
48
|
Description-Content-Type: text/markdown
|
|
48
49
|
|
|
49
|
-
# 
|
|
50
51
|
|
|
51
52
|
| [](https://github.com/pydna-group/pydna/actions/workflows/pydna_test_and_coverage_workflow.yml) | [](https://codecov.io/gh/BjornFJohansson/pydna/branch/master) | [](https://badge.fury.io/py/pydna) | [](https://groups.google.com/g/pydna) |
|
|
52
53
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
-
| [](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml) | [](https://github.com/pydna-group/pydna/issues) |
|
|
54
|
+
| [](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml) | [](https://github.com/pydna-group/pydna/issues) | [](https://github.com/pydna-group/pydna/stargazers) | |
|
|
54
55
|
|
|
55
56
|
<!-- docs/index.rst-start -->
|
|
56
57
|
|
|
@@ -524,6 +525,19 @@ pre-commit install
|
|
|
524
525
|
> **TIP:** The hooks are a series of checks that will be run before you commit your code. If any of the checks fail, the commit will not be allowed. Some of them auto-fix the code (e.g., `black` formatting), so you can simply do `git add .` and commit again. Others like `flake8` will prevent the commit to happen until the code is compliant. For instance, if you import a module in a file and not use it, `flake8` will complain. For a full list of checks, see `.pre-commit-config.yaml`.
|
|
525
526
|
5. Push the changes to your fork
|
|
526
527
|
|
|
528
|
+
> **TIP:** The continuous integration pipeline also runs doctests. These are tests that validate that the docstring examples are correct. For example, the docstring of the function `pydna.utils.smallest_rotation` looks like this:
|
|
529
|
+
> ```python
|
|
530
|
+
> >>> from pydna.utils import smallest_rotation
|
|
531
|
+
> >>> smallest_rotation("taaa")
|
|
532
|
+
> 'aaat'
|
|
533
|
+
> ```
|
|
534
|
+
> doctest will fail if `smallest_rotation("taaa")` does not return `'aaat'`. If you make changes to some function, you may break the doctests, and this can be a bit hard to understand. If this happens, the CI tests will fail, with a message similar to this:
|
|
535
|
+
> ```
|
|
536
|
+
> =================================== FAILURES ===================================
|
|
537
|
+
> ___________________ [doctest] pydna.assembly2.blunt_overlap ____________________
|
|
538
|
+
> ```
|
|
539
|
+
> This means that the doctest of the function `blunt_overlap` failed. You can run the same test locally with `python -m doctest src/pydna/assembly2.py` (use the appropriate path to the module file). That will give you information of what's failing. Fix, and re-run until it passes!
|
|
540
|
+
|
|
527
541
|
### Creating a PR 🔗
|
|
528
542
|
|
|
529
543
|
* From your fork, make a PR towards the branch `dev_bjorn` in the original repository.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
pydna/__init__.py,sha256=a08F6v_bfQLzTDkNEbjeSa3hadO_cuKH0mIAkoG1AtU,12867
|
|
2
|
+
pydna/_pretty.py,sha256=foRAxdL3Jiupuz7l38THBQZ7tu-5vQvLm1cKdDa7n6A,923
|
|
3
|
+
pydna/_thermodynamic_data.py,sha256=9_w-97DpkbsWbJGHVijscMvUS_SFt7GxzrxspMRPZSg,10903
|
|
4
|
+
pydna/all.py,sha256=xwkbR5Hn52xVfgLAXaEfIgtNwaBiGDbJFTQfa3Zi6HQ,1781
|
|
5
|
+
pydna/amplicon.py,sha256=X4rCSanhlx1sPN0eAtcdAWg4Lw0WKrGQhv-cgZKtM3s,5343
|
|
6
|
+
pydna/amplify.py,sha256=XRwmSMQ2d8Y379zcl56yU6rt1xWbSulWWHLqp-HRDc0,19260
|
|
7
|
+
pydna/assembly.py,sha256=LM3-s3YysFvvkl1oooxoBjYLt0iMc3E1YLoqE05i8bs,19936
|
|
8
|
+
pydna/assembly2.py,sha256=IN5tP5-FgdqLpIkAEKy9tXDG8x58iIgc6FUZSMr5kS0,73307
|
|
9
|
+
pydna/codon.py,sha256=Nxa_03n2wxXKACkzi55bUN1t5xUU_DBRRwh_ZLIb9_o,2568
|
|
10
|
+
pydna/common_sub_strings.py,sha256=A-gJeA2cEyxKKGQSFoC0BKvcXUGJZcnTUU7x2xlAovc,11574
|
|
11
|
+
pydna/conftest.py,sha256=T5-WmHWLZH3U3ifxnbxd_oOiIFibIrkYpwNacCHlvsY,999
|
|
12
|
+
pydna/contig.py,sha256=m0XV0LO2AbArPZkjyNCf8c7_b1EkXFtYwXHJVG4eTY8,8168
|
|
13
|
+
pydna/crispr.py,sha256=iwu0Cjskzdb2r-In9633QIxQYKGmSNVOEM_dLK6lC54,3619
|
|
14
|
+
pydna/design.py,sha256=BAoPb4M47yMkF1p4gGmbomZPFyTl0WuXkv8sLSY_Tg0,29067
|
|
15
|
+
pydna/download.py,sha256=7tT8LAEy2Vg05spGfbCea_sol6pUOOBVbxOtyk2mnlQ,1085
|
|
16
|
+
pydna/dseq.py,sha256=2N_dXX36niJItQzN61PoVweee0xxP04_amJ3cSExAtU,54232
|
|
17
|
+
pydna/dseqrecord.py,sha256=9OSuxLqcJse3aIVZCVeN7bZCyohGt7WEXnnv1qBIcB0,47983
|
|
18
|
+
pydna/fakeseq.py,sha256=uxyu1PXF-sVasEqhmyhMcsbpTy84uUtubDiZuxHNb9c,1174
|
|
19
|
+
pydna/fusionpcr.py,sha256=tfIfGGkNoZjg_dodcEx9wTc12aLRnC7J3g-Vg3Jg1uA,2821
|
|
20
|
+
pydna/gateway.py,sha256=9OL6o9J_FQoQ5VExmHMIPDtDiJs3_4-whcS755BPCG0,8451
|
|
21
|
+
pydna/gel.py,sha256=QE1uhnjWXLl2nXgbLs_1TWbPixgUa0-z_UR8jZaByS4,3371
|
|
22
|
+
pydna/genbank.py,sha256=rH677VRklVKBpPoFRsl7Az8fZvrC8TD2lNrNoHO1wU8,8856
|
|
23
|
+
pydna/genbankfile.py,sha256=ii0_RjNGnsfUThTN01c5PRLSZkQT3WlYDvoC6R6ozAA,1451
|
|
24
|
+
pydna/genbankfixer.py,sha256=rJ7qMODx19aha6g8eHC28UfaCsRM983XbTMZXNRdE8w,20510
|
|
25
|
+
pydna/genbankrecord.py,sha256=u9RNBifT0j8rrA2bSdWajodtwm42P52_VlXsxKeOvq0,5566
|
|
26
|
+
pydna/goldengate.py,sha256=LvUWAzhG9OhN1wil7osJIkjfqwBLh355a18wCZO1TvI,1778
|
|
27
|
+
pydna/ladders.py,sha256=fKfWwezxQpX4qon9YCMIwchMBkGVOu8-C02xXk90J-E,3310
|
|
28
|
+
pydna/ligate.py,sha256=ap8xgS4aL9cH4w38e-kpw1Ixa7DtpYF_ipezrVXiHG0,1965
|
|
29
|
+
pydna/parsers.py,sha256=RFYBCKrlqWzZbRWhg5lQE3pp31KjUNAarpAgHJuUpaU,6690
|
|
30
|
+
pydna/primer.py,sha256=k9Z_fHfBcY_rGnOtfys806rQBDtvyKwKl2ceSx3_w9A,2272
|
|
31
|
+
pydna/readers.py,sha256=tKoonGlIB9ACeOMnzjhbCya1ooqhFMQIO_G36azN81E,1575
|
|
32
|
+
pydna/seq.py,sha256=82gR1g2D8jfPy1So1pSJvlXYk4zkcKx_qmgvcydxth4,7579
|
|
33
|
+
pydna/seqrecord.py,sha256=rzUUecmf9RkQdYHL0b9Pu8ofXM3Q6KThm-wv43jH0SA,23368
|
|
34
|
+
pydna/sequence_picker.py,sha256=Pco9IrUwNSiS0wQ5hp5FMfE9kIN9XOwXasKLF9OA6DM,1402
|
|
35
|
+
pydna/threading_timer_decorator_exit.py,sha256=D91kqjKSavWDnXyc1Fo-CwPYtbmR2DjTXnBYSRXKmSA,2793
|
|
36
|
+
pydna/tm.py,sha256=0r4Aqjt_qanfqR8GNvqNh8us7AKM6JvaQKqHYsswAz4,11143
|
|
37
|
+
pydna/types.py,sha256=OE7iwA3b7f_T6EoBnYbRl9CFrY6OrSkAfuyY-R5kins,1389
|
|
38
|
+
pydna/user_cloning.py,sha256=VSpYX1tdbcD_PzEt69Jz6Lud-yAkYMVXnzVd4v2usnE,692
|
|
39
|
+
pydna/utils.py,sha256=BwBOcr2HyLAPAVdpXEEDiwJ5MeWW096EOekvfyXxbDM,25227
|
|
40
|
+
pydna-5.5.2.dist-info/LICENSE.txt,sha256=u8QfcsnNXZM0UCexerK_MvyA2lPWgeGyUtSYXvLG6Oc,6119
|
|
41
|
+
pydna-5.5.2.dist-info/METADATA,sha256=TsRUcktnk2S-D4iZncIM54BSgQJ3XYv8EyTRfAAOgJI,25439
|
|
42
|
+
pydna-5.5.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
43
|
+
pydna-5.5.2.dist-info/RECORD,,
|
pydna/editor.py
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
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
|
-
"""This module provides a class for opening a sequence using an editor
|
|
8
|
-
that accepts a file as a command line argument.
|
|
9
|
-
|
|
10
|
-
ApE - A plasmid Editor [#]_ is and excellent editor for this purpose.
|
|
11
|
-
|
|
12
|
-
References
|
|
13
|
-
----------
|
|
14
|
-
|
|
15
|
-
.. [#] http://biologylabs.utah.edu/jorgensen/wayned/ape/
|
|
16
|
-
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
import time as _time
|
|
20
|
-
import tempfile as _tempfile
|
|
21
|
-
import os as _os
|
|
22
|
-
import subprocess as _subprocess
|
|
23
|
-
import operator as _operator
|
|
24
|
-
import string as _string
|
|
25
|
-
import copy as _copy
|
|
26
|
-
import uuid as _uuid
|
|
27
|
-
|
|
28
|
-
_wl = "{}{}-_.()".format(_string.ascii_letters, _string.digits)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class Editor:
|
|
32
|
-
"""
|
|
33
|
-
The Editor class needs to be instantiated before use.
|
|
34
|
-
|
|
35
|
-
Parameters
|
|
36
|
-
----------
|
|
37
|
-
|
|
38
|
-
shell_command_for_editor : str
|
|
39
|
-
String containing the path to the editor
|
|
40
|
-
|
|
41
|
-
tmpdir : str, optional
|
|
42
|
-
String containing path to the temprary directory where sequence
|
|
43
|
-
files are stored before opening.
|
|
44
|
-
|
|
45
|
-
Examples
|
|
46
|
-
--------
|
|
47
|
-
|
|
48
|
-
>>> import pydna
|
|
49
|
-
>>> #ape = pydna.Editor("tclsh8.6 /home/bjorn/.ApE/apeextractor/ApE.vfs/lib/app-AppMain/AppMain.tcl")
|
|
50
|
-
>>> #ape.open("aaa") # This command opens the sequence in the ApE editor
|
|
51
|
-
|
|
52
|
-
"""
|
|
53
|
-
|
|
54
|
-
def __init__(self, shell_command_for_editor, tmpdir=None):
|
|
55
|
-
self.path_to_editor = shell_command_for_editor
|
|
56
|
-
self.tmpdir = tmpdir or _os.path.join(_tempfile.gettempdir(), "ApE")
|
|
57
|
-
try:
|
|
58
|
-
_os.makedirs(self.tmpdir)
|
|
59
|
-
except OSError:
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
def open(self, seq_to_open):
|
|
63
|
-
"""Open a sequence for editing in an external (DNA) editor.
|
|
64
|
-
|
|
65
|
-
Parameters
|
|
66
|
-
----------
|
|
67
|
-
args : SeqRecord or Dseqrecord object
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
seq = _copy.deepcopy(seq_to_open)
|
|
71
|
-
for feature in seq.features:
|
|
72
|
-
qf = feature.qualifiers
|
|
73
|
-
if "label" not in qf:
|
|
74
|
-
try:
|
|
75
|
-
qf["label"] = qf["note"]
|
|
76
|
-
except KeyError:
|
|
77
|
-
qf["label"] = ["feat{}".format(len(feature))]
|
|
78
|
-
if "ApEinfo_fwdcolor" not in qf:
|
|
79
|
-
qf["ApEinfo_fwdcolor"] = "#ffff49"
|
|
80
|
-
if "ApEinfo_revcolor" not in qf:
|
|
81
|
-
qf["ApEinfo_revcolor"] = "#ffe6cc"
|
|
82
|
-
|
|
83
|
-
seq.features.sort(key=_operator.attrgetter("location.start"))
|
|
84
|
-
name = "{name}.gb".format(
|
|
85
|
-
name="".join(c for c in seq.name.strip().replace(" ", "_") if c in _wl)
|
|
86
|
-
or _uuid.uuid3(_uuid.NAMESPACE_DNS, seq.name)
|
|
87
|
-
)
|
|
88
|
-
tdir = _tempfile.mkdtemp(dir=self.tmpdir)
|
|
89
|
-
tpth = _os.path.join(tdir, name)
|
|
90
|
-
|
|
91
|
-
with open(tpth, "w") as f:
|
|
92
|
-
f.write(seq.format("gb"))
|
|
93
|
-
|
|
94
|
-
_subprocess.Popen(
|
|
95
|
-
"{} {}".format(self.path_to_editor, tpth),
|
|
96
|
-
shell=True,
|
|
97
|
-
stdout=_tempfile.TemporaryFile(),
|
|
98
|
-
stderr=_tempfile.TemporaryFile(),
|
|
99
|
-
).pid
|
|
100
|
-
_time.sleep(0.5)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
apeloader = Editor(_os.getenv("pydna_ape"))
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def ape(*args, **kwargs):
|
|
107
|
-
"""docstring."""
|
|
108
|
-
return apeloader.open(*args, **kwargs)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if __name__ == "__main__":
|
|
112
|
-
import os as _os
|
|
113
|
-
|
|
114
|
-
cached = _os.getenv("pydna_cached_funcs", "")
|
|
115
|
-
_os.environ["pydna_cached_funcs"] = ""
|
|
116
|
-
import doctest
|
|
117
|
-
|
|
118
|
-
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
|
|
119
|
-
_os.environ["pydna_cached_funcs"] = cached
|
pydna/myenzymes.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
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
|
-
"""This module establish a RestrictionBatch based on enzymes found in a text file specified in the enzymes entry
|
|
8
|
-
in the python.ini file or by the environment variable pydna_enzymes.
|
|
9
|
-
|
|
10
|
-
The text file will be searched for all enzymes in the biopython
|
|
11
|
-
AllEnzymes batch which is located in the Bio.Restriction package.
|
|
12
|
-
|
|
13
|
-
The pydna.myenzymes.myenzymes contains a new restriction batch with the enzymes contained
|
|
14
|
-
within the file specified.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
import os as _os
|
|
18
|
-
import re as _re
|
|
19
|
-
from Bio.Restriction import AllEnzymes as _AllEnzymes
|
|
20
|
-
from Bio.Restriction import RestrictionBatch as _RestrictionBatch
|
|
21
|
-
import logging as _logging
|
|
22
|
-
import traceback as _traceback
|
|
23
|
-
|
|
24
|
-
_module_logger = _logging.getLogger("pydna." + __name__)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
_text = ""
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
try:
|
|
31
|
-
with open(_os.environ["pydna_enzymes"], encoding="utf-8") as _f:
|
|
32
|
-
_text = _f.read()
|
|
33
|
-
except FileNotFoundError:
|
|
34
|
-
_module_logger.warning("%s not found.", _os.environ["pydna_enzymes"])
|
|
35
|
-
except IsADirectoryError:
|
|
36
|
-
_module_logger.warning("%s is a directory.", _os.environ["pydna_enzymes"])
|
|
37
|
-
except IOError:
|
|
38
|
-
_module_logger.warning("%s found, but could not be read.", _os.environ["pydna_enzymes"])
|
|
39
|
-
except Exception:
|
|
40
|
-
_module_logger.warning(_traceback.format_exc())
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
myenzymes = _RestrictionBatch([e for e in _AllEnzymes if str(e).lower() in _re.split(r"\W+", _text.lower())])
|
|
44
|
-
|
|
45
|
-
if __name__ == "__main__":
|
|
46
|
-
cache = _os.getenv("pydna_cache")
|
|
47
|
-
_os.environ["pydna_cache"] = "nocache"
|
|
48
|
-
import doctest
|
|
49
|
-
|
|
50
|
-
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
|
|
51
|
-
_os.environ["pydna_cache"] = cache
|