pydna 5.5.1__py3-none-any.whl → 5.5.3__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 +2627 -0
- pydna/common_sub_strings.py +2 -12
- pydna/contig.py +39 -22
- pydna/cre_lox.py +130 -0
- 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 +154 -152
- 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 +25 -9
- pydna/primer.py +3 -12
- pydna/readers.py +0 -11
- pydna/seq.py +21 -18
- pydna/seqrecord.py +20 -20
- pydna/sequence_picker.py +3 -12
- pydna/sequence_regex.py +44 -0
- pydna/tm.py +13 -15
- pydna/types.py +41 -0
- pydna/utils.py +173 -58
- {pydna-5.5.1.dist-info → pydna-5.5.3.dist-info}/METADATA +22 -18
- pydna-5.5.3.dist-info/RECORD +45 -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.3.dist-info}/LICENSE.txt +0 -0
- {pydna-5.5.1.dist-info → pydna-5.5.3.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pydna
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.3
|
|
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
|
-
| [](https://github.com/pydna-group/pydna/actions/workflows/pydna_test_and_coverage_workflow.yml) | [](https://codecov.io/gh/pydna-group/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/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
|
|
|
@@ -437,15 +438,11 @@ Feedback & suggestions are very welcome! Please create an issue with your questi
|
|
|
437
438
|
|
|
438
439
|
If you don't have a github account, you can get in touch through the [google group](https://groups.google.com/d/forum/pydna) for pydna.
|
|
439
440
|
|
|
440
|
-
Below are the instructions for developers who want to contribute to pydna. Please direct pull requests towards the `
|
|
441
|
+
Below are the instructions for developers who want to contribute to pydna. Please direct pull requests towards the `master` branch.
|
|
441
442
|
|
|
442
443
|
### Fork the repository and set up a dev branch 🍴
|
|
443
444
|
|
|
444
|
-
Fork the
|
|
445
|
-
|
|
446
|
-

|
|
447
|
-
|
|
448
|
-
Create your branch starting from `dev_bjorn`, and if your changes are related to an issue, call the branch `issue_<number>`.
|
|
445
|
+
Fork the repository. Create your branch starting from `master`, and if your changes are related to an issue, call the branch `issue_<number>`.
|
|
449
446
|
|
|
450
447
|
```bash
|
|
451
448
|
# Clone the repository
|
|
@@ -454,12 +451,6 @@ git clone https://github.com/<your-username>/pydna.git
|
|
|
454
451
|
# Change to the repository directory
|
|
455
452
|
cd pydna
|
|
456
453
|
|
|
457
|
-
# Go to the dev_bjorn branch
|
|
458
|
-
git checkout -b dev_bjorn
|
|
459
|
-
|
|
460
|
-
# Pull the current version of dev_bjorn
|
|
461
|
-
git pull origin dev_bjorn
|
|
462
|
-
|
|
463
454
|
# Create your own branch
|
|
464
455
|
git checkout -b issue_<number>
|
|
465
456
|
```
|
|
@@ -524,9 +515,22 @@ pre-commit install
|
|
|
524
515
|
> **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
516
|
5. Push the changes to your fork
|
|
526
517
|
|
|
518
|
+
> **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:
|
|
519
|
+
> ```python
|
|
520
|
+
> >>> from pydna.utils import smallest_rotation
|
|
521
|
+
> >>> smallest_rotation("taaa")
|
|
522
|
+
> 'aaat'
|
|
523
|
+
> ```
|
|
524
|
+
> 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:
|
|
525
|
+
> ```
|
|
526
|
+
> =================================== FAILURES ===================================
|
|
527
|
+
> ___________________ [doctest] pydna.assembly2.blunt_overlap ____________________
|
|
528
|
+
> ```
|
|
529
|
+
> 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!
|
|
530
|
+
|
|
527
531
|
### Creating a PR 🔗
|
|
528
532
|
|
|
529
|
-
* From your fork, make a PR towards the branch `
|
|
533
|
+
* From your fork, make a PR towards the branch `master` in the original repository.
|
|
530
534
|
* Mention the issue number in the PR description (e.g., `Closes #123`).
|
|
531
535
|
* Remember to click the "Allow edits from maintainers" checkbox so that we can make some changes to the PR if necessary.
|
|
532
536
|
|
|
@@ -582,7 +586,7 @@ Wang, Y., Xue, H., Pourcel, C., Du, Y., & Gautheret, D. (2021).
|
|
|
582
586
|
In Cold Spring Harbor Laboratory (p. 2021.01.17.427048). [DOI](https://doi.org/10.1101/2021.01.17.427048)
|
|
583
587
|
[PubMed](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8180056)
|
|
584
588
|
|
|
585
|
-
[
|
|
589
|
+
[OpenCloning](https://opencloning.org), a web application for designing and documenting DNA cloning strategies.
|
|
586
590
|
|
|
587
591
|
[An Automated Protein Synthesis Pipeline with Transcriptic and Snakemake](http://blog.booleanbiotech.com/transcriptic_protein_synthesis_pipeline.html)
|
|
588
592
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
pydna/__init__.py,sha256=aHQGRBpQdABGgHMwN6CfDMKGvsOpCVSs2f5I5ir5DMw,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=gyn-R2Jz3D6Pze27iE3IZ6JLvnqg72my9r-J34ZjWqI,98202
|
|
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/cre_lox.py,sha256=sOj9R8_oFPGWs68vc4jf6LqWOXjMsVSwtJaeKl6ZF2M,4476
|
|
14
|
+
pydna/crispr.py,sha256=iwu0Cjskzdb2r-In9633QIxQYKGmSNVOEM_dLK6lC54,3619
|
|
15
|
+
pydna/design.py,sha256=BAoPb4M47yMkF1p4gGmbomZPFyTl0WuXkv8sLSY_Tg0,29067
|
|
16
|
+
pydna/download.py,sha256=7tT8LAEy2Vg05spGfbCea_sol6pUOOBVbxOtyk2mnlQ,1085
|
|
17
|
+
pydna/dseq.py,sha256=2N_dXX36niJItQzN61PoVweee0xxP04_amJ3cSExAtU,54232
|
|
18
|
+
pydna/dseqrecord.py,sha256=9OSuxLqcJse3aIVZCVeN7bZCyohGt7WEXnnv1qBIcB0,47983
|
|
19
|
+
pydna/fakeseq.py,sha256=uxyu1PXF-sVasEqhmyhMcsbpTy84uUtubDiZuxHNb9c,1174
|
|
20
|
+
pydna/fusionpcr.py,sha256=tfIfGGkNoZjg_dodcEx9wTc12aLRnC7J3g-Vg3Jg1uA,2821
|
|
21
|
+
pydna/gateway.py,sha256=iwNHrDIOqKl6_3Rdz7z7c8n_kcLQZuuu72oxNIjyyL8,8686
|
|
22
|
+
pydna/gel.py,sha256=QE1uhnjWXLl2nXgbLs_1TWbPixgUa0-z_UR8jZaByS4,3371
|
|
23
|
+
pydna/genbank.py,sha256=rH677VRklVKBpPoFRsl7Az8fZvrC8TD2lNrNoHO1wU8,8856
|
|
24
|
+
pydna/genbankfile.py,sha256=ii0_RjNGnsfUThTN01c5PRLSZkQT3WlYDvoC6R6ozAA,1451
|
|
25
|
+
pydna/genbankfixer.py,sha256=rJ7qMODx19aha6g8eHC28UfaCsRM983XbTMZXNRdE8w,20510
|
|
26
|
+
pydna/genbankrecord.py,sha256=u9RNBifT0j8rrA2bSdWajodtwm42P52_VlXsxKeOvq0,5566
|
|
27
|
+
pydna/goldengate.py,sha256=LvUWAzhG9OhN1wil7osJIkjfqwBLh355a18wCZO1TvI,1778
|
|
28
|
+
pydna/ladders.py,sha256=fKfWwezxQpX4qon9YCMIwchMBkGVOu8-C02xXk90J-E,3310
|
|
29
|
+
pydna/ligate.py,sha256=ap8xgS4aL9cH4w38e-kpw1Ixa7DtpYF_ipezrVXiHG0,1965
|
|
30
|
+
pydna/parsers.py,sha256=FW2RhZrxWX9wUYJcmt0qi1n7DZP0RfsNaXHhG0eVTWg,7319
|
|
31
|
+
pydna/primer.py,sha256=k9Z_fHfBcY_rGnOtfys806rQBDtvyKwKl2ceSx3_w9A,2272
|
|
32
|
+
pydna/readers.py,sha256=tKoonGlIB9ACeOMnzjhbCya1ooqhFMQIO_G36azN81E,1575
|
|
33
|
+
pydna/seq.py,sha256=82gR1g2D8jfPy1So1pSJvlXYk4zkcKx_qmgvcydxth4,7579
|
|
34
|
+
pydna/seqrecord.py,sha256=haLp1316KQkROkQ0HkJp-RfgWN-eDyLPzTGFLjgXwHk,23363
|
|
35
|
+
pydna/sequence_picker.py,sha256=Pco9IrUwNSiS0wQ5hp5FMfE9kIN9XOwXasKLF9OA6DM,1402
|
|
36
|
+
pydna/sequence_regex.py,sha256=DYuAJkWm_GqEGMpd0pE-Mad_B6POUP8gwjSCFR8RfXw,1259
|
|
37
|
+
pydna/threading_timer_decorator_exit.py,sha256=D91kqjKSavWDnXyc1Fo-CwPYtbmR2DjTXnBYSRXKmSA,2793
|
|
38
|
+
pydna/tm.py,sha256=0r4Aqjt_qanfqR8GNvqNh8us7AKM6JvaQKqHYsswAz4,11143
|
|
39
|
+
pydna/types.py,sha256=OE7iwA3b7f_T6EoBnYbRl9CFrY6OrSkAfuyY-R5kins,1389
|
|
40
|
+
pydna/user_cloning.py,sha256=VSpYX1tdbcD_PzEt69Jz6Lud-yAkYMVXnzVd4v2usnE,692
|
|
41
|
+
pydna/utils.py,sha256=BwBOcr2HyLAPAVdpXEEDiwJ5MeWW096EOekvfyXxbDM,25227
|
|
42
|
+
pydna-5.5.3.dist-info/LICENSE.txt,sha256=u8QfcsnNXZM0UCexerK_MvyA2lPWgeGyUtSYXvLG6Oc,6119
|
|
43
|
+
pydna-5.5.3.dist-info/METADATA,sha256=Orph3mLdDMwKy21DsEucxGYPyLHX9p9FimxYMiaidYU,25090
|
|
44
|
+
pydna-5.5.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
45
|
+
pydna-5.5.3.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
|
pydna/myprimers.py
DELETED
|
@@ -1,219 +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
|
-
"""
|
|
8
|
-
Provides a practical way to access a list of primer sequences in a text file.
|
|
9
|
-
|
|
10
|
-
The path of a text file can be specified in the pydna.ini file or by the
|
|
11
|
-
´pydna_primers´ environment variable.
|
|
12
|
-
|
|
13
|
-
The file is expected to contain sequences in FASTA, Genbank or EMBL formats or
|
|
14
|
-
any format readable by the parse_primers function.
|
|
15
|
-
|
|
16
|
-
The primer list is expected to follow the convension below. The primer name is
|
|
17
|
-
expected to begin with the number.
|
|
18
|
-
|
|
19
|
-
can have the format below for example:
|
|
20
|
-
|
|
21
|
-
::
|
|
22
|
-
|
|
23
|
-
>2_third_primer
|
|
24
|
-
tgagtagtcgtagtcgtcgtat
|
|
25
|
-
|
|
26
|
-
>1_second_primer
|
|
27
|
-
tgatcgtcatgctgactatactat
|
|
28
|
-
|
|
29
|
-
>0_first_primer
|
|
30
|
-
ctaggatcgtagatctagctg
|
|
31
|
-
...
|
|
32
|
-
|
|
33
|
-
The primerlist funtion returns a list of :class:`pydna.primer.Primer` objects
|
|
34
|
-
primerdict returns a dict where the key is the id of the object.
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
import os as _os
|
|
38
|
-
import re as _re
|
|
39
|
-
from typing import Iterable
|
|
40
|
-
from pathlib import Path
|
|
41
|
-
import copy as _copy
|
|
42
|
-
from keyword import iskeyword as _iskeyword
|
|
43
|
-
from pydna.parsers import parse_primers as _parse_primers
|
|
44
|
-
from pydna._pretty import pretty_str as _pretty_str
|
|
45
|
-
from collections import UserList as _UserList
|
|
46
|
-
from pydna.utils import open_folder as _open_folder
|
|
47
|
-
from builtins import __dict__ as _kw
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class PrimerList(_UserList):
|
|
51
|
-
"""Read a text file with primers.
|
|
52
|
-
|
|
53
|
-
The primers can be of any format readable by the parse_primers
|
|
54
|
-
function. Lines beginning with # are ignored. Path defaults to
|
|
55
|
-
the path given by the pydna_primers environment variable.
|
|
56
|
-
|
|
57
|
-
The primer list does not accept new primers. Use the
|
|
58
|
-
assign_numbers_to_new_primers method and paste the new
|
|
59
|
-
primers at the top of the list.
|
|
60
|
-
|
|
61
|
-
The primer list remembers the numbers of accessed primers.
|
|
62
|
-
The indices of accessed primers are stored in the .accessed
|
|
63
|
-
property.
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
def __init__(
|
|
67
|
-
self,
|
|
68
|
-
initlist: Iterable = None,
|
|
69
|
-
path: (str, Path) = None,
|
|
70
|
-
*args,
|
|
71
|
-
identifier: str = "p",
|
|
72
|
-
**kwargs,
|
|
73
|
-
):
|
|
74
|
-
if initlist:
|
|
75
|
-
self.data = initlist
|
|
76
|
-
self.path = None
|
|
77
|
-
else:
|
|
78
|
-
self.path = Path(path or _os.environ["pydna_primers"])
|
|
79
|
-
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
-
self.data = _parse_primers(self.path.read_text())[::-1]
|
|
81
|
-
# super().__init__(*args, **kwargs)
|
|
82
|
-
self.accessed_indices = []
|
|
83
|
-
if identifier.isidentifier() and not _iskeyword(identifier) and identifier not in _kw:
|
|
84
|
-
self.identifier = identifier
|
|
85
|
-
else:
|
|
86
|
-
raise ValueError(f"{identifier} is not a valid identifier.")
|
|
87
|
-
|
|
88
|
-
def __getitem__(self, i):
|
|
89
|
-
"""Save indices of accessed items."""
|
|
90
|
-
if isinstance(i, slice):
|
|
91
|
-
result = self.__class__(self.data[i])
|
|
92
|
-
for ind in range(i.start, i.stop, i.step or 1):
|
|
93
|
-
if ind not in self.accessed_indices:
|
|
94
|
-
self.accessed_indices.append(ind)
|
|
95
|
-
else:
|
|
96
|
-
try:
|
|
97
|
-
result = self.data[i]
|
|
98
|
-
except IndexError as e:
|
|
99
|
-
raise (e)
|
|
100
|
-
else:
|
|
101
|
-
if i not in self.accessed_indices:
|
|
102
|
-
self.accessed_indices.append(i)
|
|
103
|
-
return result
|
|
104
|
-
|
|
105
|
-
def __setitem__(self, i, item):
|
|
106
|
-
"""Items already present can be set to the same value."""
|
|
107
|
-
if abs(i) > len(self):
|
|
108
|
-
raise IndexError(f"index {i} does not exist.")
|
|
109
|
-
else:
|
|
110
|
-
if str(item.seq).lower() != str(self.data[i].seq).lower():
|
|
111
|
-
raise ValueError("Cannot change existing primer.")
|
|
112
|
-
if i not in self.accessed_indices:
|
|
113
|
-
self.accessed_indices.append(i)
|
|
114
|
-
|
|
115
|
-
@property
|
|
116
|
-
def accessed(self):
|
|
117
|
-
"""docstring."""
|
|
118
|
-
return [self.data[i] for i in self.accessed_indices]
|
|
119
|
-
|
|
120
|
-
def assign_numbers(self, lst: list):
|
|
121
|
-
"""Find new primers in lst.
|
|
122
|
-
|
|
123
|
-
Returns a string containing new primers with their assigned
|
|
124
|
-
numbers. This string can be copied and pasted to the primer
|
|
125
|
-
text file.
|
|
126
|
-
"""
|
|
127
|
-
new = []
|
|
128
|
-
found = []
|
|
129
|
-
oldstrs = [str(p.seq).upper() for p in self.data]
|
|
130
|
-
# no = len(oldstrs)
|
|
131
|
-
no, *rest = self.data[-1].name.split("_")
|
|
132
|
-
no = int(no) + 1
|
|
133
|
-
for p in lst[::-1]:
|
|
134
|
-
try:
|
|
135
|
-
i = oldstrs.index(str(p.seq).upper())
|
|
136
|
-
except ValueError:
|
|
137
|
-
i = no + len(new)
|
|
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("_")
|
|
141
|
-
newprimer = _copy.copy(p)
|
|
142
|
-
newprimer.id = f"{i}_{suffix}"
|
|
143
|
-
new.append(newprimer)
|
|
144
|
-
else:
|
|
145
|
-
found.append(self[i])
|
|
146
|
-
new = new[::-1]
|
|
147
|
-
newold = new + found
|
|
148
|
-
return _pretty_str("\n".join([p.format("primer") for p in newold]))
|
|
149
|
-
|
|
150
|
-
def pydna_code_from_list(self, lst: list):
|
|
151
|
-
"""Pydna code for a list of primer objects."""
|
|
152
|
-
indices = []
|
|
153
|
-
prstrs = [str(p.seq).upper() for p in self.data]
|
|
154
|
-
err = None
|
|
155
|
-
for p in lst:
|
|
156
|
-
try:
|
|
157
|
-
prstrs.index(str(p.seq).upper())
|
|
158
|
-
except ValueError as e:
|
|
159
|
-
print(f"{p.format('primer')}")
|
|
160
|
-
err = e
|
|
161
|
-
else:
|
|
162
|
-
indices.append(self.data.index(p))
|
|
163
|
-
if err:
|
|
164
|
-
raise ValueError("At least one primer not in list.")
|
|
165
|
-
|
|
166
|
-
curly = "{}"
|
|
167
|
-
msg = "from pydna.parsers import parse_primers\n\n"
|
|
168
|
-
msg += f"{self.identifier} = {curly}\n\n"
|
|
169
|
-
msg += ", ".join(f"{self.identifier}[{i}]" for i in indices)
|
|
170
|
-
msg += " = parse_primers('''\n\n"
|
|
171
|
-
msg += "\n".join(self[i].format("primer") for i in indices)
|
|
172
|
-
msg += "\n''')"
|
|
173
|
-
return _pretty_str(msg)
|
|
174
|
-
|
|
175
|
-
def open_folder(self):
|
|
176
|
-
"""Open folder where primer file is located."""
|
|
177
|
-
if self.path:
|
|
178
|
-
_open_folder(self.path.parent)
|
|
179
|
-
else:
|
|
180
|
-
raise ValueError("path property not set.")
|
|
181
|
-
|
|
182
|
-
code = pydna_code_from_list
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
def check_primer_numbers(pl: list = None):
|
|
186
|
-
"""Find primers whose number do not match position in list."""
|
|
187
|
-
if not pl:
|
|
188
|
-
pl = PrimerList()
|
|
189
|
-
primers_with_wrong_number = []
|
|
190
|
-
for i, p in enumerate(pl):
|
|
191
|
-
if not p.name.startswith(str(i)):
|
|
192
|
-
primers_with_wrong_number.append(p)
|
|
193
|
-
return primers_with_wrong_number
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
def undefined_sequence(pl: list = None):
|
|
197
|
-
"""Primers in list with N or n instead of a sequence."""
|
|
198
|
-
if not pl:
|
|
199
|
-
pl = PrimerList()
|
|
200
|
-
return [p for p in pl if _re.match("N+", str(p.seq.upper()))]
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
def find_duplicate_primers(pl: list = None):
|
|
204
|
-
"""Find a list of lists with duplicated primer sequences."""
|
|
205
|
-
if not pl:
|
|
206
|
-
pl = PrimerList()
|
|
207
|
-
pg = {}
|
|
208
|
-
for p in pl:
|
|
209
|
-
pg.setdefault(str(p.seq).upper(), []).append(p)
|
|
210
|
-
return [pl for ps, pl in pg.items() if len(pl) > 1]
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if __name__ == "__main__":
|
|
214
|
-
cache = _os.getenv("pydna_cache")
|
|
215
|
-
_os.environ["pydna_cache"] = "nocache"
|
|
216
|
-
import doctest
|
|
217
|
-
|
|
218
|
-
doctest.testmod(verbose=True, optionflags=doctest.ELLIPSIS)
|
|
219
|
-
_os.environ["pydna_cache"] = cache
|
pydna-5.5.1.dist-info/RECORD
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
pydna/__init__.py,sha256=_BLqB7eQOWvmMql1-tqh-BSkXMYerYFDdAYpwAAxWEw,13391
|
|
2
|
-
pydna/_pretty.py,sha256=eMjYTy19AUTA-nntiYEql3FPVe4WzTLSZ4f0Jd6SLfo,1172
|
|
3
|
-
pydna/_thermodynamic_data.py,sha256=9_w-97DpkbsWbJGHVijscMvUS_SFt7GxzrxspMRPZSg,10903
|
|
4
|
-
pydna/all.py,sha256=i2dR9cAccr3AdtAMMeH4jazpVjxWjfvu8LB3LD9hwgY,2099
|
|
5
|
-
pydna/amplicon.py,sha256=09_R_-LNFydsTR9GxNtcFDVjypglp28mbWpnrOJEcq0,5466
|
|
6
|
-
pydna/amplify.py,sha256=fjj0GU9Hx7_fzCaqrb1n5T2gQ7mvm5frraQ7b64zdFA,18965
|
|
7
|
-
pydna/assembly.py,sha256=Rewf15nWeeUeMwJl5Laxm4nms_UC3zXI7qMtEbBGRpE,19532
|
|
8
|
-
pydna/codon.py,sha256=Nxa_03n2wxXKACkzi55bUN1t5xUU_DBRRwh_ZLIb9_o,2568
|
|
9
|
-
pydna/common_sub_strings.py,sha256=JD0gXGMymcQCsQtq1OlKuI4QIykKXbo5-OJ8N9XAuuE,11841
|
|
10
|
-
pydna/conftest.py,sha256=T5-WmHWLZH3U3ifxnbxd_oOiIFibIrkYpwNacCHlvsY,999
|
|
11
|
-
pydna/contig.py,sha256=aNKtXGaHY0c70tEEMHk_9lDMk9DQzjFRO6LK-GPtdwc,7991
|
|
12
|
-
pydna/crispr.py,sha256=VbOe_nDVmdhlnYvnnmukJJhRMZr1gk1UK6qOJCCs988,3824
|
|
13
|
-
pydna/design.py,sha256=IE04UXQqAkpX1leTZN8KPJjdd9gj1Es_4HNcTZML5Dw,28781
|
|
14
|
-
pydna/download.py,sha256=uFoAvWYXr1C30071p9OweMdemvKO-1F2pB5CHo2k31U,1307
|
|
15
|
-
pydna/dseq.py,sha256=-iOK3uHplIgYfwbImHzJA2aZ8uXsBwXZbWwkO01-IMM,53601
|
|
16
|
-
pydna/dseqrecord.py,sha256=qVWbAWT672TNYAx_R_TDqbTnlxbL2mN-c-I14PyetR0,47238
|
|
17
|
-
pydna/editor.py,sha256=Au3kZ5DYqa6MYB3kXJePHOJOioSJ5w7jtlZ8kfHvKm8,3471
|
|
18
|
-
pydna/fakeseq.py,sha256=eGeivoppQ8vSPQqggxFXARmUhU6oEBfdnIkGDR_v9UQ,1449
|
|
19
|
-
pydna/fusionpcr.py,sha256=uRuHbwmZ2H5SThDUYW-KwxMB1DwqA2Khof7KXiMdxN8,2783
|
|
20
|
-
pydna/gateway.py,sha256=bZUASiC93WKwofnqBmG6cKahxXHnXHA26K64BAf8HMM,8447
|
|
21
|
-
pydna/gel.py,sha256=yCPXNlF-3HzPVjQxa_NZjpC9UXTPfZBzCmW9m5kWkHo,3554
|
|
22
|
-
pydna/genbank.py,sha256=mPtictXh-oOEdj2H6MtDYP2eaJK7vnap-QS33pEMRIk,8933
|
|
23
|
-
pydna/genbankfile.py,sha256=wpCBYTysYqFwrGBIN8ean2MAAocdA_7OJ9avHEho9fw,1652
|
|
24
|
-
pydna/genbankfixer.py,sha256=D2-I30fIPMndyefNzOaeAYbu6LGHC_SnOyjxJhhMfYw,20695
|
|
25
|
-
pydna/genbankrecord.py,sha256=EWRQ9EB7xzIAVqk7ico0bL_JSjR92LXt0UTSQ0HztEI,5771
|
|
26
|
-
pydna/goldengate.py,sha256=Fmkbb4kkau9h1o6I-MiQZ0rNHwmD4-1IK0BJaL1MblI,1774
|
|
27
|
-
pydna/ladders.py,sha256=2SaTq7_Y8XLvEBK0UqgkB9aWePzG0g84cPiNtJfggx0,3574
|
|
28
|
-
pydna/ligate.py,sha256=41YYBcZxGl1PzABZoi-zkRlRcR6sYCzT4rp5HE45A9Q,2205
|
|
29
|
-
pydna/myenzymes.py,sha256=v9e9JnykCw6lqoZ4zNcW6cH3DGtGxl0O1WXzJkyhhyk,1834
|
|
30
|
-
pydna/myprimers.py,sha256=JtpdejRP6MkKplLv-p5NCMzxBTepYN0CVHSxxP3ms3Q,7362
|
|
31
|
-
pydna/parsers.py,sha256=UlG5xh4EsNNPaPK4SoEE6KL52YxkQfLvKJiDm3DB5s4,6927
|
|
32
|
-
pydna/primer.py,sha256=7AYWIhr1YbjG0Gak94bU6bTlsRUgWCIbKTndgsgWZ2M,2533
|
|
33
|
-
pydna/readers.py,sha256=dsihpjRopxWeeab1bRAK4TRHMBLYK_SpK1j2llHrHk0,1850
|
|
34
|
-
pydna/seq.py,sha256=Qwbx1uo-JIYXIuySXevNtxoDgohENHTTTCjPMnn4yjo,7703
|
|
35
|
-
pydna/seqrecord.py,sha256=oz2YZR3_tkNj7YgdWmJr0MyREJZvCgvmq1g8hzuzNyQ,23530
|
|
36
|
-
pydna/sequence_picker.py,sha256=jYbGUparKyIhDD30BzT2x-olnPpDjv-xvyaeIk6_V4E,1659
|
|
37
|
-
pydna/threading_timer_decorator_exit.py,sha256=D91kqjKSavWDnXyc1Fo-CwPYtbmR2DjTXnBYSRXKmSA,2793
|
|
38
|
-
pydna/tm.py,sha256=YVXgDMB_AxNyzPR9fYT1YWlfy80paKTl2r0bkLFNxU8,11330
|
|
39
|
-
pydna/user_cloning.py,sha256=VSpYX1tdbcD_PzEt69Jz6Lud-yAkYMVXnzVd4v2usnE,692
|
|
40
|
-
pydna/utils.py,sha256=7CQ8tkVcTcNY_k1eBbP242ax3X2MMZGZ1cPhHcDY8y4,22084
|
|
41
|
-
pydna-5.5.1.dist-info/LICENSE.txt,sha256=u8QfcsnNXZM0UCexerK_MvyA2lPWgeGyUtSYXvLG6Oc,6119
|
|
42
|
-
pydna-5.5.1.dist-info/METADATA,sha256=mJGG_H_ccfb97ELbilQYUxFvjIVBNYt7X2Tdwe2Pp0Y,24485
|
|
43
|
-
pydna-5.5.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
44
|
-
pydna-5.5.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|