Repository: cogent3/cogent3
Pull request #2168 (2024-12-13 04:08:02)

ENH: assign data paths to new type sequence collections source attribute
## Summary by Sourcery

Enhance sequence collections by introducing a 'source' attribute to store data paths, replacing the previous 'info["source"]' usage, and add tests to ensure correct assignment and propagation of this attribute.

Enhancements:
- Assign data paths to the new 'source' attribute in sequence collections, replacing the previous use of 'info["source"]'.

Tests:
- Add tests to verify that the 'source' attribute is correctly assigned and propagates through sequence collections.


Patch:
diff --git a/src/cogent3/core/new_alignment.py b/src/cogent3/core/new_alignment.py
index b0ece4bca..12c037a79 100644
--- a/src/cogent3/core/new_alignment.py
+++ b/src/cogent3/core/new_alignment.py
@@ -645,6 +645,7 @@ class SequenceCollection:
             "info": self.info.copy(),
             "annotation_db": self.annotation_db,
             "is_reversed": self._is_reversed,
+            "source": self.source,
         }
 
     @property
@@ -2512,17 +2513,12 @@ def make_unaligned_seqs(
     # refactor: design
     # rename offset to offsets as it could track potentially multiple offsets
 
-    # refactor: design
-    # define a source attribute rather than storing as .info["source"]
-    # this will also replace the previous name attribute
-
     # refactor: design
     # currently reversal can only be applied to the entire collection.
     # This should be made more flexible.
 
     moltype = new_moltype.get_moltype(moltype)
     alphabet = moltype.most_degen_alphabet()
-
     if len(data) == 0:
         raise ValueError("data must be at least one sequence.")
 
@@ -2591,7 +2587,8 @@ def _(
         raise ValueError(f"Setting offset is not supported for {data=}")
 
     info = info if isinstance(info, dict) else {}
-    info["source"] = str(source) if source else str(info.get("source", "unknown"))
+    source = str(source) if source else str(info.get("source", "unknown"))
+    info["source"] = source
     seqs = SequenceCollection(
         seqs_data=data,
         moltype=moltype,
@@ -4054,6 +4051,7 @@ class Alignment(SequenceCollection):
             "info": self.info.copy(),
             "annotation_db": self.annotation_db,
             "slice_record": self._slice_record,
+            "source": self.source,
         }
 
     @singledispatchmethod
@@ -6552,7 +6550,8 @@ def _(
         raise ValueError(f"Setting offset is not supported for {data=}")
 
     info = info if isinstance(info, dict) else {}
-    info["source"] = str(source) if source else str(info.get("source", "unknown"))
+    source = str(source) if source else str(info.get("source", "unknown"))
+    info["source"] = source
 
     sr = (
         new_sequence.SliceRecord(parent_len=data.align_len, step=-1)


Test patch:
diff --git a/tests/test_core/test_new_alignment.py b/tests/test_core/test_new_alignment.py
index 7f0783453..34e3bc669 100644
--- a/tests/test_core/test_new_alignment.py
+++ b/tests/test_core/test_new_alignment.py
@@ -726,11 +726,11 @@ def test_sequence_collection_init_ordered(ordered1, ordered2):
 
 
 @pytest.mark.parametrize("load_cls", [load_aligned_seqs, load_unaligned_seqs])
-def test_sequence_collection_info_source(load_cls):
-    """info.source exists if load seqs given a filename"""
+def test_sequence_collection_source(load_cls):
+    """.source exists if load seqs given a filename"""
     path = pathlib.Path("data/brca1.fasta")
     seqs = load_cls(path, moltype="dna", new_type=True)
-    assert seqs.info.source == str(path)
+    assert seqs.source == str(path)
 
 
 @pytest.mark.parametrize(
@@ -2561,6 +2561,7 @@ def test_sequence_collection_to_rich_dict():
             "moltype": seqs.moltype.label,
             "name_map": seqs._name_map,
             "info": seqs.info,
+            "source": "unknown",
         },
     }
     assert got == expect
@@ -2578,6 +2579,7 @@ def test_sequence_collection_to_rich_dict_reversed_seqs():
             "moltype": seqs.moltype.label,
             "name_map": seqs._name_map,
             "info": seqs.info,
+            "source": "unknown",
         },
         "type": get_object_provenance(seqs),
         "version": __version__,
@@ -5536,3 +5538,15 @@ def test_coerce_moltype_obj(mk_cls):
     )
     coll = mk_cls(data, moltype="text")
     coll = coll.to_moltype("rna")
+
+
+@pytest.mark.parametrize(
+    "mk_cls",
+    [load_aligned_seqs, load_unaligned_seqs],
+)
+def test_source_propagates(mk_cls, DATA_DIR):
+    fn = DATA_DIR / "brca1.fasta"
+    coll = mk_cls(fn, moltype="dna", new_type=True)
+    assert coll.source == str(fn)
+    subcoll = coll.take_seqs(["Human", "Chimpanzee"])
+    assert subcoll.source == str(fn)