Repository: cogent3/cogent3
Pull request #1115 (2022-08-15 03:03:54)

Composable refactor
address app inheritance and fix return type hints in evo


Patch:
diff --git a/src/cogent3/app/composable.py b/src/cogent3/app/composable.py
index 5ab97a2ae..bc7ef6573 100644
--- a/src/cogent3/app/composable.py
+++ b/src/cogent3/app/composable.py
@@ -1021,6 +1021,7 @@ def is_composable(obj):
     return __app_registry.get(get_object_provenance(obj), False)
 
 
+@UI.display_wrap
 def _apply_to(
     self,
     dstore,
diff --git a/src/cogent3/app/evo.py b/src/cogent3/app/evo.py
index 41bb06d59..984f72a1a 100644
--- a/src/cogent3/app/evo.py
+++ b/src/cogent3/app/evo.py
@@ -19,6 +19,8 @@ from .result import (
 from .typing import (
     AlignedSeqsType,
     BootstrapResultType,
+    HypothesisResultType,
+    ModelCollectionResultType,
     ModelResultType,
     SerialisableType,
     TabularResultType,
@@ -292,10 +294,8 @@ class _InitFrom:
         return other
 
 
-@define_app
-class model_collection:
-    """Fits a collection of models. Returns a
-    model_collection_result."""
+class _ModelCollectionBase:
+    """Base class for fitting collections of models."""
 
     def __init__(self, null, *alternates, sequential=True, init_alt=None):
         """
@@ -334,9 +334,6 @@ class model_collection:
         self._init_alt = init_alt
         self._sequential = sequential
 
-    def _make_result(self, aln):
-        return model_collection_result(source=aln.info)
-
     def _initialised_alt(self, null, aln):
         if callable(self._init_alt):
             init_func = self._init_alt
@@ -352,7 +349,7 @@ class model_collection:
             null = result
         return results
 
-    T = Union[SerialisableType, AlignedSeqsType]
+    T = Union[SerialisableType, ModelCollectionResultType, HypothesisResultType]
 
     def main(self, aln: AlignedSeqsType) -> T:
         try:
@@ -383,11 +380,21 @@ class model_collection:
         return result
 
 
-class hypothesis(model_collection):
+@define_app
+class model_collection(_ModelCollectionBase):
+    """Fits a collection of models. Returns a
+    model_collection_result."""
+
+    def _make_result(self, aln: AlignedSeqsType) -> ModelCollectionResultType:
+        return model_collection_result(source=aln.info)
+
+
+@define_app
+class hypothesis(_ModelCollectionBase):
     """Specify a hypothesis through defining two models. Returns a
     hypothesis_result."""
 
-    def _make_result(self, aln):
+    def _make_result(self, aln: AlignedSeqsType) -> HypothesisResultType:
         return hypothesis_result(name_of_null=self.null.name, source=aln.info)
 
 
@@ -442,14 +449,9 @@ class ancestral_states:
     """Computes ancestral state probabilities from a model result. Returns a dict
     with a DictArray for each node."""
 
-    _data_types = "model_result"
-
-    def __init__(self):
-        pass
-
-    T = Union[SerialisableType, ModelResultType]
-
-    def main(self, result: ModelResultType) -> T:
+    def main(
+        self, result: ModelResultType
+    ) -> Union[SerialisableType, TabularResultType]:
         """returns a tabular_result of posterior probabilities of ancestral states"""
         anc = result.lf.reconstruct_ancestral_seqs()
         fl = result.lf.get_full_length_likelihoods()
@@ -582,7 +584,7 @@ class natsel_neutral:
         )
         self._hyp = hypothesis(null, alt)
 
-    T = Union[SerialisableType, AlignedSeqsType]
+    T = Union[SerialisableType, HypothesisResultType]
 
     def main(self, data: AlignedSeqsType) -> T:
         return self._hyp(data)
@@ -768,7 +770,7 @@ class natsel_zhang:
         alt_args["param_rules"] = rules
         return model(**alt_args)
 
-    T = Union[SerialisableType, AlignedSeqsType]
+    T = Union[SerialisableType, HypothesisResultType]
 
     def main(self, aln: AlignedSeqsType, *args, **kwargs) -> T:
         null_result = self.null(aln)
@@ -918,7 +920,7 @@ class natsel_sitehet:
         alt_args["param_rules"] = rules
         return model(**alt_args)
 
-    T = Union[SerialisableType, ModelResultType]
+    T = Union[SerialisableType, HypothesisResultType]
 
     def main(self, aln: AlignedSeqsType, *args, **kwargs) -> T:
         null_result = self.null(aln)
@@ -1082,7 +1084,7 @@ class natsel_timehet:
         )
         self._hyp = hypothesis(null, alt)
 
-    T = Union[SerialisableType, AlignedSeqsType]
+    T = Union[SerialisableType, HypothesisResultType]
 
     def main(self, data: AlignedSeqsType) -> T:
         return self._hyp(data)
diff --git a/src/cogent3/app/typing.py b/src/cogent3/app/typing.py
index 8ec150fd6..3ff524d58 100644
--- a/src/cogent3/app/typing.py
+++ b/src/cogent3/app/typing.py
@@ -26,6 +26,9 @@ TreeType = TypeVar("TreeType", "TreeNode", "PhyloNode")
 SerialisableType = TypeVar("SerialisableType")
 BootstrapResultType = TypeVar("BootstrapResultType", bound="bootstrap_result")
 HypothesisResultType = TypeVar("HypothesisResultType", bound="hypothesis_result")
+ModelCollectionResultType = TypeVar(
+    "ModelCollectionResultType", bound="model_collection_result"
+)
 ModelResultType = TypeVar("ModelResultType", bound="model_result")
 TabularResultType = TypeVar("TabularResultType", bound="tabular_result")
 GenericResultType = TypeVar("GenericResultType", bound="generic_result")


Test patch:
diff --git a/tests/test_app/test_composable.py b/tests/test_app/test_composable.py
index 2ad298fb8..aeffa1d59 100644
--- a/tests/test_app/test_composable.py
+++ b/tests/test_app/test_composable.py
@@ -76,7 +76,6 @@ class TestComposableBase(TestCase):
         # should be able to compose a new one now
         aseqfunc1 + aseqfunc3
 
-    @pytest.mark.xfail
     def test_apply_to(self):
         """correctly applies iteratively"""
         from cogent3.core.alignment import SequenceCollection