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/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
- import shelve as _shelve
12
- import os as _os
11
+
12
+ # import shelve as _shelve
13
+ # import os as _os
13
14
  import re as _re
14
- import logging as _logging
15
- import base64 as _base64
16
- import pickle as _pickle
17
- import hashlib as _hashlib
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((frame, startindex * 3 + frame, (stopindex + 1) * 3 + frame))
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("Shift moves location below zero, use a `lim` to loop around if sequence is circular.")
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 = (newparts[-1].start, newparts[-1].end) if len(newparts) else (None, None)
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
- """Cache functions and classes.
290
+ # def memorize(filename):
291
+ # """Cache functions and classes.
283
292
 
284
- see pydna.download
285
- """
293
+ # see pydna.download
294
+ # """
286
295
 
287
- def decorator(f):
288
- def wrappee(*args, **kwargs):
289
- _module_logger.info("#### memorizer ####")
290
- _module_logger.info("cache filename = %s", filename)
291
- _module_logger.info(
292
- "os.environ['pydna_cached_funcs'] = %s",
293
- _os.getenv("pydna_cached_funcs", ""),
294
- )
295
- if filename not in _os.getenv("pydna_cached_funcs", ""):
296
- _module_logger.info("cache filename not among cached functions, made it new!")
297
- return f(*args, **kwargs)
298
- key = _base64.urlsafe_b64encode(_hashlib.sha1(_pickle.dumps((args, kwargs))).digest()).decode("ascii")
299
- _module_logger.info("key = %s", key)
300
- cache = _shelve.open(
301
- _os.path.join(_os.environ["pydna_data_dir"], identifier_from_string(filename)),
302
- writeback=False,
303
- )
304
- try:
305
- result = cache[key]
306
- except KeyError:
307
- _module_logger.info(
308
- "no result for key %s in shelve %s",
309
- key,
310
- identifier_from_string(filename),
311
- )
312
- result = f(*args, **kwargs)
313
- _module_logger.info("made it new!")
314
- cache[key] = result
315
- _module_logger.info("saved result under key %s", key)
316
- else:
317
- _module_logger.info("found %s in cache", key)
318
- cache.close()
319
- return result
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
- return wrappee
330
+ # return wrappee
322
331
 
323
- return decorator
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 random.choice(starts) + "".join([random.choice(cdns) for x in range(length)]) + random.choice(stops)
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([arg.circular if hasattr(arg, "circular") else None for arg in args])
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 = [arg.watson.lower() if hasattr(arg, "watson") else str(arg).lower() for arg in args]
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
- if __name__ == "__main__":
739
- cached = _os.getenv("pydna_cached_funcs", "")
740
- _os.environ["pydna_cached_funcs"] = ""
741
- import doctest
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
- doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
744
- _os.environ["pydna_cached_funcs"] = cached
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.1
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
- # ![icon](https://github.com/pydna-group/pydna/blob/master/docs/_static/pydna.resized.png?raw=true) pydna
50
+ # ![icon](https://github.com/pydna-group/pydna/blob/master/docs/_static/banner.png?raw=true)
50
51
 
51
52
  | [![Tests & Coverage](https://github.com/pydna-group/pydna/actions/workflows/pydna_test_and_coverage_workflow.yml/badge.svg?branch=dev_bjorn)](https://github.com/pydna-group/pydna/actions/workflows/pydna_test_and_coverage_workflow.yml) | [![codecov](https://codecov.io/gh/BjornFJohansson/pydna/branch/master/graph/badge.svg)](https://codecov.io/gh/BjornFJohansson/pydna/branch/master) | [![PyPI version](https://badge.fury.io/py/pydna.svg)](https://badge.fury.io/py/pydna) | [![Google group : pydna](https://img.shields.io/badge/Google%20Group-pydna-blue.svg)](https://groups.google.com/g/pydna) |
52
53
  | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
53
- | [![Documentation Status](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml/badge.svg)](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml) | [![GitHub issues](https://img.shields.io/github/issues/BjornFJohansson/pydna.svg)](https://github.com/pydna-group/pydna/issues) | [![Anaconda-Server Badge2](https://anaconda.org/bjornfjohansson/pydna/badges/license.svg)](https://anaconda.org/bjornfjohansson/pydna) | [![GitHub stars](https://img.shields.io/github/stars/BjornFJohansson/pydna.svg)](https://github.com/pydna-group/pydna/stargazers) |
54
+ | [![Documentation Status](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml/badge.svg)](https://github.com/pydna-group/pydna/actions/workflows/publish-docs.yml) | [![GitHub issues](https://img.shields.io/github/issues/BjornFJohansson/pydna.svg)](https://github.com/pydna-group/pydna/issues) | [![GitHub stars](https://img.shields.io/github/stars/BjornFJohansson/pydna.svg)](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