Repository: cogent3/cogent3
Pull request #2017 (2024-09-06 06:21:02)

MAINT: bug fix and minor edits


Patch:
diff --git a/changelog.d/20240827_152735_Gavin.Huttley.md b/changelog.d/20240827_152735_Gavin.Huttley.md
deleted file mode 100644
index 7a1f5b89e..000000000
--- a/changelog.d/20240827_152735_Gavin.Huttley.md
+++ /dev/null
@@ -1,40 +0,0 @@
-<!--
-A new scriv changelog fragment.
-
-Uncomment the section that is right (remove the HTML comment wrapper).
--->
-
-
-### Contributors
-
-- @GavinHuttley
-
-<!--
-### ENH
-
-- A bullet item for the ENH category.
-
--->
-### BUG
-
-- Fixed issue where read only zipped data stores would include files
-  starting with a "._"
-
-<!--
-### DOC
-
-- A bullet item for the DOC category.
-
--->
-<!--
-### Deprecations
-
-- A bullet item for the Deprecations category.
-
--->
-<!--
-### Discontinued
-
-- A bullet item for the Discontinued category.
-
--->
diff --git a/changelog.d/20240903_173911_Gavin.Huttley.md b/changelog.d/20240903_173911_Gavin.Huttley.md
deleted file mode 100644
index bce8c5380..000000000
--- a/changelog.d/20240903_173911_Gavin.Huttley.md
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
-A new scriv changelog fragment.
-
-Uncomment the section that is right (remove the HTML comment wrapper).
--->
-
-<!--
-### Contributors
-
-- A bullet item for the Contributors category.
-
--->
-
-### ENH
-
-- Use loky for parallel processing instead of multiprocessing alone. loky is more robust
-    and supports parallel execution in jupyter notebooks .. yay!
-
-
-<!--
-### BUG
-
-- A bullet item for the BUG category.
-
--->
-<!--
-### DOC
-
-- A bullet item for the DOC category.
-
--->
-<!--
-### Deprecations
-
-- A bullet item for the Deprecations category.
-
--->
-<!--
-### Discontinued
-
-- A bullet item for the Discontinued category.
-
--->
diff --git a/changelog.md b/changelog.md
index fe5fabd7c..5f6c01976 100755
--- a/changelog.md
+++ b/changelog.md
@@ -1,4 +1,28 @@
 
+<a id='changelog-2024.7.19a6'></a>
+# Changes in release "2024.7.19a6"
+
+Mainly a bug fix release. But one important new feature is support for parallelisation
+in jupyter notebooks!
+
+## Contributors
+
+- @GavinHuttley
+
+## ENH
+
+- Use loky for parallel processing instead of multiprocessing alone. loky is more robust
+  and supports parallel execution in jupyter notebooks .. yay!
+- Using a feature to slice a sequence or an alignment should now preserve the annotation
+  when possible (a feature has a single contiguous span), or removes the annotation_db
+  when not querying it could be misleading.
+
+## BUG
+
+- Fixed issue where read only zipped data stores would include files
+  starting with a "._"
+- Fixed regression affecting app.as_completed()
+
 <a id='changelog-2024.7.19a5'></a>
 # Changes in release "2024.7.19a5"
 
diff --git a/src/cogent3/core/annotation.py b/src/cogent3/core/annotation.py
index fa2b8f8c2..1051e8ab4 100644
--- a/src/cogent3/core/annotation.py
+++ b/src/cogent3/core/annotation.py
@@ -96,18 +96,24 @@ class Feature:
         present in the sequence, then this method will fail.
         """
         fmap = self.map
-        if not (complete or fmap.complete):
+
+        if not complete and not fmap.complete:
             fmap = fmap.without_gaps()
         if not allow_gaps:
             result = self.parent[fmap]
-            if self.reversed:
-                result = result.rc()
-            return result
-
+            return self._do_seq_slice(result)
         # all slicing now requires start < end
         result = self.parent[fmap.start : fmap.end]
+        return self._do_seq_slice(result)
+
+    def _do_seq_slice(self, result):
+        offset = self.map.start if self.map.num_spans == 1 else 0
         if self.reversed:
             result = result.rc()
+        result.annotation_offset = offset
+        if self.map.num_spans > 1:
+            # db querying will be incorrect so make sure it can't be done
+            result.annotation_db = None
         return result
 
     def without_lost_spans(self):
diff --git a/src/cogent3/core/location.py b/src/cogent3/core/location.py
index 85492a6f9..dbae426d5 100644
--- a/src/cogent3/core/location.py
+++ b/src/cogent3/core/location.py
@@ -1683,6 +1683,10 @@ class FeatureMap(MapABC):
     def spans(self) -> Iterator[SeqSpanTypes]:
         yield from self._spans
 
+    @property
+    def num_spans(self):
+        return len(self._spans)
+
     def get_covering_span(self) -> "FeatureMap":
         span = (self.start, self.end)
         return self.__class__.from_locations(


Test patch:
diff --git a/tests/test_core/test_annotation_db.py b/tests/test_core/test_annotation_db.py
index 1dc332ab2..ca0bdbab1 100644
--- a/tests/test_core/test_annotation_db.py
+++ b/tests/test_core/test_annotation_db.py
@@ -598,6 +598,45 @@ def test_get_slice():
     assert str(got) == str(seq[5:10])
 
 
+def test_get_slice_annotation_offset():
+    """get_slice should return the same as slicing the sequence directly"""
+    seq = Sequence("ATTGTACGCCCCTGA", name="test_seq")
+    feature_data = {
+        "biotype": "CDS",
+        "name": "fake",
+        "spans": [
+            (5, 10),
+        ],
+        "strand": "+",
+    }
+    feature = seq.make_feature(feature_data)
+    assert feature.map.num_spans == 1
+    got = feature.get_slice()
+    assert got.annotation_offset == 5
+
+
+def test_get_slice_annotation_offset_not_set():
+    # annotation offset is set to zero if a feature spans disjoint segments
+    # plus the annotation db is set to None
+    seq = Sequence("ATTGTACGCCCCTGA", name="test_seq")
+    feature_data = {
+        "biotype": "CDS",
+        "name": "fake",
+        "spans": [
+            (5, 7),
+            (9, 11),
+        ],
+        "strand": "+",
+    }
+    feature = seq.make_feature(feature_data)
+    assert feature.map.num_spans == 2
+    got = feature.get_slice()
+    assert got.annotation_offset == 0
+    assert got.annotation_db is None
+    f = list(got.get_features(biotype="gene"))
+    assert not f
+
+
 def test_feature_get_children(seq_db):
     feat = list(seq_db.get_features(name="Transcript:B0019.1"))[0]
     new_feat_5pUTR = list(feat.get_children(biotype="five_prime_UTR"))