Repository: cogent3/cogent3
Pull request #1704 (2024-01-31 01:28:08)

ENH: added SeqView.copy()
[NEW] sliced argument controls whether truncating the represented
     sequence or leaves as is.


Patch:
diff --git a/src/cogent3/core/sequence.py b/src/cogent3/core/sequence.py
index 58e16427f..bc4fbf996 100644
--- a/src/cogent3/core/sequence.py
+++ b/src/cogent3/core/sequence.py
@@ -1216,7 +1216,7 @@ class Sequence(SequenceI):
             drops annotation_db when True
         """
         annotation_offset = self.annotation_offset if sliced else self._seq.offset
-        data = self._seq.value if sliced else self._seq[:]
+        data = self._seq.copy(sliced=sliced)
         new = self.__class__(
             data,
             name=self.name,
@@ -1630,8 +1630,8 @@ class Sequence(SequenceI):
         drawer.layout.update(xaxis=xaxis, yaxis=yaxis)
         return drawer
 
-    def parent_coordinates(self) -> Tuple[int, int]:
-        """returns start, stop of this sequence on its parent
+    def parent_coordinates(self) -> Tuple[int, int, int]:
+        """returns start, stop, strand of this sequence on its parent
 
         Notes
         -----
@@ -1640,9 +1640,11 @@ class Sequence(SequenceI):
 
         Returns
         -------
-        start, end of this sequence on the parent
+        start, end, strand of this sequence on the parent. strand is either
+        -1 or 1.
         """
-        return self._seq.parent_start, self._seq.parent_stop
+        strand = -1 if self._seq.reverse else 1
+        return self._seq.parent_start, self._seq.parent_stop, strand
 
 
 class ProteinSequence(Sequence):
@@ -2244,6 +2246,9 @@ class SeqView:
                 offset=self.offset,
             )
 
+        if segment.start is segment.stop is segment.step is None:
+            return self.copy(sliced=False)
+
         if len(self) == 0:
             return self
 
@@ -2314,6 +2319,25 @@ class SeqView:
         sv = cls(**init_args)
         return sv
 
+    def copy(self, sliced=False):
+        """returns copy
+
+        Parameters
+        ----------
+        sliced
+            if True, the underlying sequence is truncated and the start/stop
+            adjusted
+        """
+        if not sliced:
+            return self.__class__(
+                self.seq,
+                start=self.start,
+                stop=self.stop,
+                step=self.step,
+                offset=self.offset,
+            )
+        return self.from_rich_dict(self.to_rich_dict())
+
 
 _zero_slice = SeqView(seq="")
 


Test patch:
diff --git a/tests/test_core/test_sequence.py b/tests/test_core/test_sequence.py
index 380dcd76a..99f4cd146 100644
--- a/tests/test_core/test_sequence.py
+++ b/tests/test_core/test_sequence.py
@@ -2173,6 +2173,24 @@ def test_relative_position_step_GT_one(integer_seq):
     assert got == 4
 
 
+@pytest.mark.parametrize("sliced", (False, True))
+@pytest.mark.parametrize("rev", (False, True))
+def test_seqview_copy(sliced, rev, integer_seq):
+    raw_data = integer_seq.seq
+    if rev:
+        integer_seq = integer_seq[::-1]
+        raw_data = raw_data[::-1]
+
+    slice_start = 2
+    slice_end = 4
+    sv = integer_seq[slice_start:slice_end]
+    copied = sv.copy(sliced=sliced)
+
+    assert copied.value == raw_data[slice_start:slice_end]
+    assert copied.reverse == integer_seq.reverse
+    assert sliced and copied.seq is not sv.seq or copied.seq is integer_seq.seq
+
+
 def test_relative_position_with_remainder(integer_seq):
     """tests relative_position when the index given is excluded from the view as it falls on
     a position that is 'stepped over'"""
@@ -2584,7 +2602,7 @@ def test_copied_parent_coordinates(sliced, rev, start_stop):
     # matches original
     assert copied.parent_coordinates() == seq.parent_coordinates()
     # and expected
-    assert copied.parent_coordinates() == (start, stop)
+    assert copied.parent_coordinates() == (start, stop, -1 if rev else 1)
 
 
 @pytest.mark.parametrize("rev", (False, True))
@@ -2594,4 +2612,4 @@ def test_parent_coordinates(rev):
     if rev:
         seq = seq.rc()
 
-    assert seq.parent_coordinates() == (0, 0)
+    assert seq.parent_coordinates() == (0, 0, 1)