Repository: cogent3/cogent3

Issue:
DOC: add docstring for new_moltype.MolType.get_degenerate_positions
check the tests of this method to see what the arguments mean.

Be sure and use the correct docstring style - we adhere to numpydoc style, but we do not type hint parameters.

The easiest way to do this is to install `autoDocstring - Python Docstring Generator` extension on VS Code and specify numpydoc as the style.

Hints from issue comments:
@je634: I would like to try this issue.@je634: Here is doctoring generated by the tool autoDocstring - Python Docstring Generator extension on VS Code in numpy style:

<img width="551" alt="Image" src="https://github.com/user-attachments/assets/da68020d-1b5b-48f9-8c82-9bda74da0298" />

Solution patch:
diff --git a/src/cogent3/app/sample.py b/src/cogent3/app/sample.py
index 555e4e481..1539efb46 100644
--- a/src/cogent3/app/sample.py
+++ b/src/cogent3/app/sample.py
@@ -968,16 +968,18 @@ class omit_bad_seqs:
         """
         if moltype:
             moltype = cogent3.get_moltype(moltype)
-        assert moltype.label.lower() in "dna rna protein protein_with_stop", (
-            "moltype must be one of DNA, RNA or PROTEIN"
-        )
+        valid_moltypes = {"dna", "rna", "protein", "protein_with_stop"}
+        if moltype.label.lower() not in valid_moltypes:
+            msg = f"Invalid moltype: {moltype.label!r}. Moltype must be one of {', '.join(valid_moltypes)}"
+            raise new_moltype.MolTypeError(msg)
+
         # refactor: design, this should raise a MolTypeError
         self._quantile = quantile
         self._gap_fraction = gap_fraction
         self._ambig_fraction = ambig_fraction
         self._moltype = moltype
 
-    T = Union[SerialisableType, AlignedSeqsType]
+    T = SerialisableType | AlignedSeqsType
 
     def main(self, aln: AlignedSeqsType) -> T:
         if self._moltype and self._moltype != aln.moltype:
diff --git a/src/cogent3/core/new_alignment.py b/src/cogent3/core/new_alignment.py
index 15e9b7ad9..6debb1060 100644
--- a/src/cogent3/core/new_alignment.py
+++ b/src/cogent3/core/new_alignment.py
@@ -1575,13 +1575,13 @@ class SequenceCollection:
 
         Used in likelihood calculations.
         """
-        # refactor: performance
         result = {}
+        alpha = self.moltype.most_degen_alphabet()
         for name in self.names:
             result[name] = ambig = {}
-            for i, motif in enumerate(self.seqs[name]):
-                if self.moltype.is_ambiguity(motif):
-                    ambig[i] = motif
+            array = numpy.array(self.seqs[name])
+            for i in numpy.where(array > alpha.gap_index)[0]:
+                ambig[i] = alpha[array[i]]
         return result
 
     def trim_stop_codons(self, gc: Any = 1, strict: bool = False):
diff --git a/src/cogent3/core/new_moltype.py b/src/cogent3/core/new_moltype.py
index 3f7095a7f..dad9de90f 100644
--- a/src/cogent3/core/new_moltype.py
+++ b/src/cogent3/core/new_moltype.py
@@ -857,7 +857,18 @@ class MolType:
         seq: StrORBytesORArray,
         include_gap: bool = True,
         validate: bool = True,
-    ) -> list[int]:  # refactor: docstring
+    ) -> list[int]:
+        """Return List of position indexs of degenerate characters in the sequence.
+
+        Parameters
+        ----------
+        seq : StrORBytesORArray
+            the sequence to be used for getting degenerate positions
+        include_gap : bool, optional
+            if True, then the gap state together with ’canonical’ sates (A,C,G,T for DNA) will be considered non-ambiguous.
+        validate : bool, optional
+            if True, checks the sequence is validated for the alphabet
+        """
         if validate and not self.is_valid(seq):
             msg = f"{seq[:4]!r} not valid for moltype {self.name!r}"
             raise new_alphabet.AlphabetError(
diff --git a/src/cogent3/core/new_sequence.py b/src/cogent3/core/new_sequence.py
index 52574a8cc..aeb75b6e1 100644
--- a/src/cogent3/core/new_sequence.py
+++ b/src/cogent3/core/new_sequence.py
@@ -2093,7 +2093,7 @@ class NucleicAcidSequenceMixin:
 
         if not incomplete_ok and "X" in pep:
             msg = (
-                "A codon is incomplete or contains an ambiguity, set incomplete_ok=True to "
+                f"{self.name!r} has an incomplete codon or contains an ambiguity, set incomplete_ok=True to "
                 "allow translation"
             )
             raise new_alphabet.AlphabetError(
diff --git a/src/cogent3/util/misc.py b/src/cogent3/util/misc.py
index 3991d4162..4dcacfcf2 100644
--- a/src/cogent3/util/misc.py
+++ b/src/cogent3/util/misc.py
@@ -16,6 +16,8 @@ from warnings import warn
 import numpy
 from numpy import array, finfo, float64, ndarray, zeros
 
+from cogent3.util import warning as c3warn
+
 if typing.TYPE_CHECKING:
     from collections.abc import Callable
 
@@ -380,8 +382,15 @@ class ConstraintError(Exception):
     """Raised when constraint on a container is violated."""
 
 
+@c3warn.deprecated_callable(
+    version="2025.06",
+    reason="The function is not used in cogent3 and will be removed in a future release.",
+)
 def identity(x):
-    """Identity function: useful for avoiding special handling for None."""
+    """Deprecated: This function will be removed in a future release.
+
+    Identity function: useful for avoiding special handling for None.
+    """
     return x
 
 


Test patch:
diff --git a/tests/test_app/test_sample.py b/tests/test_app/test_sample.py
index 13c9b99c1..ec5c23856 100644
--- a/tests/test_app/test_sample.py
+++ b/tests/test_app/test_sample.py
@@ -6,6 +6,7 @@ import pytest
 import cogent3
 from cogent3.app import composable, sample
 from cogent3.app.composable import NotCompleted
+from cogent3.core.new_moltype import MolTypeError
 
 DNA = cogent3.get_moltype("dna")
 
@@ -499,6 +500,11 @@ def test_omit_bad_seqs(bad_gap_data, gap_fraction, quantile, expected_keys):
     assert got.to_dict() == expected
 
 
+def test_omit_bad_seqs_error():
+    with pytest.raises(MolTypeError):
+        sample.omit_bad_seqs(moltype="text")
+
+
 @pytest.fixture
 def bad_ambig_gap_data():
     return {