Repository: cogent3/cogent3
Pull request #2071 (2024-10-28 02:49:48)

ENH: new_moltype.MolType.make_seq handles Sequence instances
[NEW] if the input data is already a Sequence, returns seq if bound
    moltype is self or returns Sequence.to_moltype(self)

## Summary by Sourcery

Enhance MolType.make_seq to handle Sequence instances by checking if the sequence's moltype matches the current moltype, returning the sequence if it matches, or converting it to the specified moltype if it does not. Add tests to ensure correct functionality for both valid and invalid moltype conversions.

New Features:
- Enhance MolType.make_seq to handle Sequence instances by returning the sequence if the moltype matches or converting it to the specified moltype.

Tests:
- Add tests for MolType.make_seq to verify behavior when handling Sequence instances with matching and non-matching moltypes.


Patch:
diff --git a/src/cogent3/core/new_moltype.py b/src/cogent3/core/new_moltype.py
index c0222a45c..dc78a0a7a 100644
--- a/src/cogent3/core/new_moltype.py
+++ b/src/cogent3/core/new_moltype.py
@@ -565,9 +565,8 @@ class MolType:
             additional keyword arguments that may be required for creating the
             Sequence object
         """
-        # refactor: design
-        # for Sequence and SeqView object, a consistency check with moltype/alphabet
-        # should be performed
+        if hasattr(seq, "moltype"):
+            return seq if seq.moltype is self else seq.to_moltype(self)
 
         if check_seq:
             assert self.is_valid(
diff --git a/src/cogent3/core/new_sequence.py b/src/cogent3/core/new_sequence.py
index 57b114de6..5cde13ae2 100644
--- a/src/cogent3/core/new_sequence.py
+++ b/src/cogent3/core/new_sequence.py
@@ -1177,8 +1177,8 @@ class Sequence:
 
         if not moltype.most_degen_alphabet().is_valid(seq):
             raise ValueError(
-                f"Changing from old moltype={self.moltype.label} to new "
-                f"moltype={moltype.label} is not valid for this data"
+                f"Changing from old moltype={self.moltype.label!r} to new "
+                f"moltype={moltype.label!r} is not valid for this data"
             )
         sv = SeqView(seq=seq, alphabet=moltype.most_degen_alphabet())
         new = self.__class__(moltype=moltype, seq=sv, name=self.name, info=self.info)


Test patch:
diff --git a/tests/test_core/test_new_moltype.py b/tests/test_core/test_new_moltype.py
index c83ba2d6a..a2bbd6217 100644
--- a/tests/test_core/test_new_moltype.py
+++ b/tests/test_core/test_new_moltype.py
@@ -572,3 +572,27 @@ def test_char_order(moltype, zero, second, last):
     assert attr[0] == zero
     assert attr[2] == second
     assert attr[-1] == last
+
+
+@pytest.mark.parametrize(
+    "seq",
+    [
+        mt.make_seq(seq="ACGG")
+        for mt in (
+            new_moltype.DNA,
+            new_moltype.RNA,
+            new_moltype.PROTEIN,
+            new_moltype.ASCII,
+        )
+    ],
+)
+def test_make_seq_with_seq(seq):
+    new_seq = new_moltype.DNA.make_seq(seq=seq)
+    assert new_seq.moltype is new_moltype.DNA
+    assert (new_seq is seq) if seq.moltype.name == "dna" else seq is not new_seq
+
+
+def test_make_seq_with_seq_invalid_moltype():
+    seq = new_moltype.PROTEIN.make_seq(seq="ACQK")
+    with pytest.raises(ValueError):
+        _ = new_moltype.DNA.make_seq(seq=seq)