Skip to content

Commit

Permalink
return error on overlapping cuts, add tests for it
Browse files Browse the repository at this point in the history
  • Loading branch information
manulera committed Jan 19, 2024
1 parent f1a38fd commit 62667ff
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
6 changes: 4 additions & 2 deletions src/pydna/dseq.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
from pydna.utils import cseguid as _cseg
from pydna.utils import rc as _rc
from pydna.utils import flatten as _flatten
from pydna.common_sub_strings import common_sub_strings as _common_sub_strings
from pydna.utils import cuts_overlap as _cuts_overlap

from pydna.common_sub_strings import common_sub_strings as _common_sub_strings
from Bio.Restriction import RestrictionBatch as _RestrictionBatch
from Bio.Restriction import CommOnly

Expand Down Expand Up @@ -1517,7 +1518,8 @@ def right_end_position(self) -> tuple[int, int]:
return len(self), len(self) - self.watson_ovhg()

def apply_cut(self, left_cut, right_cut):

if _cuts_overlap(left_cut, right_cut, len(self)):
raise ValueError("Cuts overlap")
left_watson, left_crick = left_cut[0] if left_cut is not None else self.left_end_position()
ovhg = left_cut[1].ovhg if left_cut is not None else self.ovhg
right_watson, right_crick = right_cut[0] if right_cut is not None else self.right_end_position()
Expand Down
23 changes: 23 additions & 0 deletions src/pydna/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,29 @@ def eq(*args, **kwargs):
same = False
return same

def cuts_overlap(left_cut, right_cut, seq_len):
# Special cases:
if left_cut is None or right_cut is None or left_cut == right_cut:
return False

# This block of code would not be necessary if the cuts were
# initially represented like this
(left_watson, _), enz1 = left_cut
(right_watson, _), enz2 = right_cut
left_crick = left_watson - enz1.ovhg
right_crick = right_watson - enz2.ovhg
if left_crick >= seq_len:
left_crick -= seq_len
left_watson -= seq_len
if right_crick >= seq_len:
right_crick -= seq_len
right_watson -= seq_len

# Convert into ranges x and y and see if ranges overlap
x = sorted([left_watson, left_crick])
y = sorted([right_watson, right_crick])
return (x[1] > y[0]) != (y[1] < x[0])


if __name__ == "__main__":
cached = _os.getenv("pydna_cached_funcs", "")
Expand Down
34 changes: 31 additions & 3 deletions tests/test_module_dseq.py
Original file line number Diff line number Diff line change
Expand Up @@ -847,9 +847,37 @@ def test_apply_cut():
EcoRI_cut_2 = ((11, 15), type('DynamicClass', (), {'ovhg': -4})())
assert seq.apply_cut(EcoRI_cut, EcoRI_cut_2) == Dseq.from_full_sequence_and_overhangs('AATTCaaGAATT', watson_ovhg=-4, crick_ovhg=-4)

# TODO: Overlapping cuts should return an error
EcoRI_cut_2 = ((4, 8), type('DynamicClass', (), {'ovhg': -4})())
assert seq.apply_cut(EcoRI_cut, EcoRI_cut_2)
# Overlapping cuts should return an error
seq = Dseq('aaGAATTCaa', circular=True)
first_cuts = [
((3, 7), type('DynamicClass', (), {'ovhg': -4})()),
((7, 3), type('DynamicClass', (), {'ovhg': 4})()),
# Spanning the origin
((9, 8), type('DynamicClass', (), {'ovhg': -8})()),
((8, 9), type('DynamicClass', (), {'ovhg': 8})()),
]
overlapping_cuts = [
((4, 8), type('DynamicClass', (), {'ovhg': -4})()),
((2, 6), type('DynamicClass', (), {'ovhg': -4})()),
((2, 8), type('DynamicClass', (), {'ovhg': -4})()),
((8, 4), type('DynamicClass', (), {'ovhg': 4})()),
((6, 2), type('DynamicClass', (), {'ovhg': 4})()),
((8, 2), type('DynamicClass', (), {'ovhg': 4})()),
# Spanning the origin
((7, 6), type('DynamicClass', (), {'ovhg': -8})()),
((6, 7), type('DynamicClass', (), {'ovhg': 8})()),
]

for first_cut in first_cuts:
for second_cut in overlapping_cuts:
try:
seq.apply_cut(first_cut, second_cut)
except ValueError as e:
assert e.args[0] == 'Cuts overlap'
else:
print(first_cut, second_cut)
assert False, 'Expected ValueError'


if __name__ == "__main__":
pytest.main([__file__, "-vv", "-s"])

0 comments on commit 62667ff

Please sign in to comment.