Skip to content

Commit 3e6ce12

Browse files
Generalised recombinase functionality (#564)
* continuing to implement feature, vibe coded and fixed some, still need to check tests Add recombinase assembly algorithm for attB/attP * some more fixing, but excision not giving all products * tests passing * allow reversing recombinase * used recombinase class in gateway * add collection of recombinases * extra docs * improve test coverage * copilot feedback * improve test coverage * added history functionality to recombinases * add missing "limit" arg to overlap in RecombinaseCollection * enforce naming conventions of recombinase when reversing * fix tests * update pyproject * update poetry settings for gh actions * update poetry settings for gh actions --------- Co-authored-by: areebamomin <[email protected]>
1 parent fec1e74 commit 3e6ce12

12 files changed

Lines changed: 2710 additions & 1200 deletions

.github/workflows/publish-docs.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,24 @@ jobs:
1313

1414
runs-on: ubuntu-latest
1515

16-
strategy:
17-
fail-fast: false
18-
1916
steps:
2017
- uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
2120

2221
- name: Set up Python
2322
uses: actions/setup-python@v4
23+
with:
24+
python-version: "3.12"
2425

2526
- name: Session information
2627
run: |
2728
python --version
2829
2930
- name: Install Poetry
30-
uses: snok/install-poetry@v1
31-
with:
32-
virtualenvs.prefer-active-python: true
31+
run: |
32+
python -m pip install --upgrade pip
33+
pip install poetry==2.3.2
3334
3435
- name: 🔩 list Poetry settings
3536
run: poetry config --list

.github/workflows/pydna_test_and_coverage_workflow.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ jobs:
6565
run: python -c "import sys; print(sys.version)"
6666

6767
- name: Install Poetry
68-
uses: snok/[email protected]
68+
run: |
69+
python -m pip install --upgrade pip
70+
pip install poetry==2.3.2
6971
7072
- name: 🔩 list Poetry settings
7173
run: poetry config --list

docs/notebooks/history.ipynb

Lines changed: 89 additions & 3 deletions
Large diffs are not rendered by default.

poetry.lock

Lines changed: 1589 additions & 1043 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ scipy = [
6161
]
6262
seguid = ">=0.0.5"
6363
regex = ">=2024.11.6,<2027.0.0"
64-
opencloning-linkml = "^0.4.9"
64+
opencloning-linkml = "^1"
6565
[tool.poetry.extras]
6666
clipboard = ["pyperclip"]
6767
download = ["pyparsing", "requests"]

src/pydna/assembly2.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from pydna.gateway import gateway_overlap, find_gateway_sites
4040
from pydna.cre_lox import cre_loxP_overlap
4141
from pydna.alphabet import anneal_strands
42+
from pydna.recombinase import Recombinase, RecombinaseCollection
4243

4344
from typing import TYPE_CHECKING, Callable, Literal
4445
from pydna.opencloning_models import (
@@ -52,6 +53,7 @@
5253
GatewaySource,
5354
HomologousRecombinationSource,
5455
CreLoxRecombinationSource,
56+
RecombinaseSource,
5557
PCRSource,
5658
SourceInput,
5759
CRISPRSource,
@@ -1968,11 +1970,15 @@ def __init__(self, frags: [Dseqrecord], limit=25, algorithm=common_sub_strings):
19681970
self.G.add_node(1, seq=frag)
19691971

19701972
matches = algorithm(frag, frag, limit)
1973+
# Remove matches where the whole sequence matches
1974+
matches = [match for match in matches if match[2] != len(frag)]
1975+
19711976
for match in matches:
19721977
self.add_edges_from_match(match, 1, 1, frag, frag)
19731978

19741979
# To avoid duplicated outputs
1975-
self.G.remove_edges_from([(-1, -1)])
1980+
while (-1, -1) in self.G.edges():
1981+
self.G.remove_edges_from([(-1, -1)])
19761982

19771983
# These two are constrained
19781984
self.use_fragment_order = True
@@ -2801,6 +2807,72 @@ def cre_lox_excision(genome: Dseqrecord) -> list[Dseqrecord]:
28012807
return _recast_sources(products, CreLoxRecombinationSource)
28022808

28032809

2810+
def recombinase_excision(
2811+
genome: Dseqrecord,
2812+
recombinase: Recombinase | RecombinaseCollection,
2813+
) -> list[Dseqrecord]:
2814+
"""Returns the products for recombinase-mediated excision.
2815+
2816+
Parameters
2817+
----------
2818+
genome : Dseqrecord
2819+
Target genome sequence containing two recombinase sites.
2820+
recombinase : Recombinase | RecombinaseCollection
2821+
Recombinase object.
2822+
2823+
Returns
2824+
-------
2825+
list[Dseqrecord]
2826+
List containing excised plasmid and remaining genome sequence.
2827+
"""
2828+
products = common_function_excision_products(genome, None, recombinase.overlap)
2829+
products = [recombinase.annotate(p) for p in products]
2830+
return _recast_sources(products, RecombinaseSource, recombinases=recombinase)
2831+
2832+
2833+
def recombinase_integration(
2834+
genome: Dseqrecord,
2835+
inserts: list[Dseqrecord],
2836+
recombinase: Recombinase | RecombinaseCollection,
2837+
) -> list[Dseqrecord]:
2838+
"""Returns the products resulting from recombinase-mediated integration.
2839+
2840+
Parameters
2841+
----------
2842+
genome : Dseqrecord
2843+
Target genome sequence.
2844+
inserts : list[Dseqrecord]
2845+
DNA fragment(s) to insert.
2846+
recombinase : Recombinase | RecombinaseCollection
2847+
Recombinase object.
2848+
2849+
Returns
2850+
-------
2851+
list[Dseqrecord]
2852+
List of integrated DNA molecules.
2853+
2854+
Examples
2855+
--------
2856+
>>> from pydna.dseqrecord import Dseqrecord
2857+
>>> from pydna.assembly2 import recombinase_integration, recombinase_excision
2858+
>>> from pydna.recombinase import Recombinase
2859+
>>> site1 = "ATGCCCTAAaaTT"
2860+
>>> site2 = "AAaaTTTTTTTCCCT"
2861+
>>> genome = Dseqrecord(f"cccccc{site1.upper()}aaaaa")
2862+
>>> insert = Dseqrecord(f"{site2.upper()}bbbbb", circular=True)
2863+
>>> recombinase = Recombinase(site1, site2)
2864+
>>> products = recombinase_integration(genome, [insert], recombinase)
2865+
>>> len(products) >= 1
2866+
True
2867+
"""
2868+
fragments = common_handle_insertion_fragments(genome, inserts)
2869+
products = common_function_integration_products(
2870+
fragments, None, recombinase.overlap
2871+
)
2872+
products = [recombinase.annotate(p) for p in products]
2873+
return _recast_sources(products, RecombinaseSource, recombinases=recombinase)
2874+
2875+
28042876
def crispr_integration(
28052877
genome: Dseqrecord,
28062878
inserts: list[Dseqrecord],

0 commit comments

Comments
 (0)