Repository: cogent3/cogent3
Pull request #2097 (2024-11-05 03:38:07)

BUG: moltype.to_degenerates now checks if moltype has degen alpha
## Summary by Sourcery

Fix moltype.to_degenerates to ensure it checks for a degenerate alphabet before processing, and add a test for HTML generation from text moltype alignments.

Bug Fixes:
- Fix moltype.to_degenerates to check if moltype has a degenerate alphabet before proceeding.

Tests:
- Add a test for producing HTML from text moltype alignments.


Patch:
diff --git a/src/cogent3/core/new_moltype.py b/src/cogent3/core/new_moltype.py
index f809af932..393029c5e 100644
--- a/src/cogent3/core/new_moltype.py
+++ b/src/cogent3/core/new_moltype.py
@@ -637,7 +637,7 @@ class MolType:
                 f"{seq[:4]!r} not valid for moltype {self.name!r}"
             )
         return self.is_degenerate(
-            self.degen_gapped_alphabet.to_indices(seq), validate=False
+            self.most_degen_alphabet().to_indices(seq), validate=False
         )
 
     @is_degenerate.register
@@ -647,23 +647,22 @@ class MolType:
                 f"{seq[:4]!r} not valid for moltype {self.name!r}"
             )
         return self.is_degenerate(
-            self.degen_gapped_alphabet.to_indices(seq), validate=False
+            self.most_degen_alphabet().to_indices(seq), validate=False
         )
 
     @is_degenerate.register
     def _(self, seq: numpy.ndarray, validate: bool = True) -> bool:
-        # what index is the first degenerate character
+        if self.degen_alphabet is None:
+            return False
+
         if validate and not self.is_valid(seq):
             raise new_alphabet.AlphabetError(
                 f"{seq[:4]!r} not valid for moltype {self.name!r}"
             )
 
-        for index, val in enumerate(self.degen_gapped_alphabet):
-            if val in self.ambiguities:
-                break
-        else:
-            return False
-        return (seq >= index).any()
+        first_degen = self.most_degen_alphabet().gap_index + 1
+
+        return (seq >= first_degen).any()
 
     @functools.singledispatchmethod
     def is_gapped(self, seq, validate: bool = True) -> bool:


Test patch:
diff --git a/tests/test_core/test_new_alignment.py b/tests/test_core/test_new_alignment.py
index d06ace747..0ad729fc1 100644
--- a/tests/test_core/test_new_alignment.py
+++ b/tests/test_core/test_new_alignment.py
@@ -3396,6 +3396,29 @@ def test_alignment_to_html():
     assert got.find(ref_row) < got.find(other_row)
 
 
+def test_alignment_to_html_text_moltype():
+    """exercising producing html for text moltype"""
+    seqs = {"seq1": "ACG", "seq2": "-CT"}
+
+    aln = new_alignment.make_aligned_seqs(seqs, moltype="text")
+    got = aln.to_html(ref_name="longest")
+    ref_row = (
+        '<tr><td class="label">seq1</td>'
+        '<td><span class="A_text">A</span>'
+        '<span class="C_text">C</span>'
+        '<span class="G_text">G</span></td></tr>'
+    )
+    other_row = (
+        '<tr><td class="label">seq2</td>'
+        '<td><span class="ambig_text">-</span>'
+        '<span class="C_text">.</span>'
+        '<span class="T_text">T</span></td></tr>'
+    )
+
+    assert ref_row in got
+    assert other_row in got
+
+
 def test_alignment_repr():
     data = {
         "ENSMUSG00000056468": "GCCAGGGGGAAAA",
diff --git a/tests/test_core/test_new_moltype.py b/tests/test_core/test_new_moltype.py
index b311525e8..06d7f7ad8 100644
--- a/tests/test_core/test_new_moltype.py
+++ b/tests/test_core/test_new_moltype.py
@@ -143,6 +143,20 @@ def test_is_degenerate_invalid():
         new_moltype.RNA.is_degenerate(list("GAG"))
 
 
+@pytest.mark.parametrize("data_type", (str, bytes, numpy.ndarray))
+@pytest.mark.parametrize(
+    "seq",
+    (
+        "",
+        "QWERTYUIOPASDFGHJKLZXCVBNM",
+    ),
+)
+def test_text_moltype_is_not_degenerate(seq, data_type):
+    """Text moltype should not be degenerate"""
+    seq = make_typed(seq, data_type, new_moltype.ASCII)
+    assert not new_moltype.ASCII.is_degenerate(seq)
+
+
 @pytest.mark.parametrize("data_type", (str, bytes, numpy.ndarray))
 @pytest.mark.parametrize(
     "seq",