nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: cogent3/cogent3
Pull request #1843 (2024-05-01 07:04:07)

ENH: apps now work as intended for the `|` union syntax


Patch:
diff --git a/src/cogent3/app/typing.py b/src/cogent3/app/typing.py
index ca2c7bc2b..803e5ff0f 100644
--- a/src/cogent3/app/typing.py
+++ b/src/cogent3/app/typing.py
@@ -4,12 +4,20 @@ from __future__ import annotations
 
 import inspect
 import re
+import sys
 
 from typing import ForwardRef, TypeVar, Union
 
 from typing_extensions import get_args, get_origin
 
 
+if sys.version_info.minor >= 10:
+    from types import UnionType
+
+    NESTED_HINTS = (Union, UnionType, list, tuple, set)
+else:
+    NESTED_HINTS = (Union, list, tuple, set)
+
 AlignedSeqsType = TypeVar("AlignedSeqsType", "Alignment", "ArrayAlignment")
 UnalignedSeqsType = TypeVar("UnalignedSeqsType", bound="SequenceCollection")
 SeqsCollectionType = Union[AlignedSeqsType, UnalignedSeqsType]
@@ -75,7 +83,7 @@ def get_constraint_names(*hints) -> set[str | type]:
             all_hints.update(hint.__constraints__)
             continue
 
-        if get_origin(hint) in (Union, list, tuple, set):
+        if get_origin(hint) in NESTED_HINTS:
             all_hints.update(get_constraint_names(*get_args(hint)))
 
         if type(hint) == type:


Test patch:
diff --git a/tests/test_app/test_typing.py b/tests/test_app/test_typing.py
index c07c89987..8bd7ed90e 100644
--- a/tests/test_app/test_typing.py
+++ b/tests/test_app/test_typing.py
@@ -42,9 +42,14 @@ def test_get_constraint_names():
 
 def test_get_constraint_names_builtins():
     """handles built-in types"""
+    expected = {"str", "bytes"}
 
     got = get_constraint_names(Union[str, bytes])
-    assert got == {"str", "bytes"}
+    assert got == expected
+
+    if sys.version_info.minor > 9:
+        got = get_constraint_names(str | bytes)
+        assert got == expected
 
 
 def test_get_constraint_names_serilisable():
@@ -63,16 +68,27 @@ def test_get_constraint_names_identifiertype():
 
 def test_get_constraint_names_mixed_serilisable_identifiertype():
     """SerialisableType does not define any compatible types"""
+    expected = {"SerialisableType", "IdentifierType", "Alignment", "ArrayAlignment"}
 
     got = get_constraint_names(Union[SerialisableType, IdentifierType, AlignedSeqsType])
-    assert got == {"SerialisableType", "IdentifierType", "Alignment", "ArrayAlignment"}
+    assert got == expected
+
+    if sys.version_info.minor > 9:
+        got = get_constraint_names(SerialisableType | IdentifierType | AlignedSeqsType)
+        assert got == expected
 
 
 def test_hints_resolved_from_str():
     got = get_constraint_names("DnaSequence")
     assert got == {"DnaSequence"}
+
+    expected = {"SerialisableType", "DnaSequence"}
     got = get_constraint_names(Union[SerialisableType, "DnaSequence"])
-    assert got == {"SerialisableType", "DnaSequence"}
+    assert got == expected
+
+    if sys.version_info.minor > 9:
+        got = get_constraint_names(SerialisableType | "DnaSequence")
+        assert got == expected
 
 
 @pytest.mark.parametrize("container", (List, Tuple, Set))
Repository: cogent3/cogent3
Pull request #1892 (2024-06-07 01:51:05)

ENH: new get_code() handles GeneticCode instance as a value
[CHANGED] GeneticCode is now hashable, just add that to the lookup
    dictionary. Now test all different input types, int, str, instance


Patch:
diff --git a/src/cogent3/core/new_genetic_code.py b/src/cogent3/core/new_genetic_code.py
index 6f9ec2804..d3f0f7454 100644
--- a/src/cogent3/core/new_genetic_code.py
+++ b/src/cogent3/core/new_genetic_code.py
@@ -124,6 +124,12 @@ class GeneticCode:
             trinuc_alpha, self.anticodons, ncbi_code_sequence
         )
 
+    def __hash__(self):
+        return hash(self.name)
+
+    def __eq__(self, other):
+        return self.name == other.name
+
     @property
     def stop_codons(self) -> SetStr:
         return self._stop_codons
@@ -399,6 +405,7 @@ for mapping in code_mapping:
     code = GeneticCode(**dict(zip(_mapping_cols, mapping)))
     _CODES[code.ID] = code
     _CODES[code.name] = code
+    _CODES[code] = code
 
 
 DEFAULT = _CODES[1]
@@ -413,7 +420,7 @@ def get_code(code_id: StrORInt = 1) -> GeneticCode:
         genetic code identifier, name, number or string(number), defaults to
         standard genetic code
     """
-    with contextlib.suppress(ValueError):
+    with contextlib.suppress((ValueError, TypeError)):
         code_id = int(code_id)
 
     if code_id not in _CODES:


Test patch:
diff --git a/tests/test_core/test_new_genetic_code.py b/tests/test_core/test_new_genetic_code.py
index dae0a42d4..4bfd0a245 100644
--- a/tests/test_core/test_new_genetic_code.py
+++ b/tests/test_core/test_new_genetic_code.py
@@ -1,8 +1,11 @@
+import pytest
+
 from cogent3.core import new_genetic_code
 
 
-def test_get_code():
-    code = new_genetic_code.get_code(1)
+@pytest.mark.parametrize("gc", (1, "Standard", new_genetic_code.DEFAULT))
+def test_get_code(gc):
+    code = new_genetic_code.get_code(gc)
     assert code.name == "Standard"
Repository: cogent3/cogent3
Pull request #1899 (2024-06-14 22:24:33)

ENH: DataStoreDirectory.write now handles compression
[FIXED] would fail with invalid newline character if a
    compression was indicated via the file suffix.


Patch:
diff --git a/src/cogent3/app/data_store.py b/src/cogent3/app/data_store.py
index 9b2e1c02e..2854cdf82 100644
--- a/src/cogent3/app/data_store.py
+++ b/src/cogent3/app/data_store.py
@@ -488,6 +488,7 @@ class DataStoreDirectory(DataStoreABC):
         sfx, cmp = get_format_suffixes(unique_id)
         if sfx != suffix:
             unique_id = f"{Path(unique_id).stem}.{suffix}"
+            sfx, cmp = get_format_suffixes(unique_id)
 
         unique_id = (
             unique_id.replace(self.suffix, suffix)
@@ -496,8 +497,9 @@ class DataStoreDirectory(DataStoreABC):
         )
         if suffix != "log" and unique_id in self:
             return None
-
-        with open_(self.source / subdir / unique_id, mode="w", newline="\n") as out:
+        newline = None if cmp else "\n"
+        mode = "wt" if cmp else "w"
+        with open_(self.source / subdir / unique_id, mode=mode, newline=newline) as out:
             out.write(data)
 
         if subdir == _LOG_TABLE:


Test patch:
diff --git a/tests/test_app/test_data_store.py b/tests/test_app/test_data_store.py
index fac1d8d1c..9f0e03c3c 100644
--- a/tests/test_app/test_data_store.py
+++ b/tests/test_app/test_data_store.py
@@ -731,3 +731,15 @@ def test_write_multiple_times_apply_to(app_dstore_in):
     orig_length = len(app.data_store)
     app.apply_to(dstore_in)
     assert len(app.data_store) == orig_length
+
+
+def test_directory_data_store_write_compressed(tmp_path):
+    from cogent3 import get_app, make_aligned_seqs, open_data_store
+
+    out = open_data_store(base_path=tmp_path / "demo", suffix="fa.gz", mode="w")
+    writer = get_app("write_seqs", data_store=out)
+    seqs = make_aligned_seqs(
+        dict(s1="CG--T", s2="CGTTT"), moltype="dna", info=dict(source="test")
+    )
+    got = writer(seqs)  # pylint: disable=not-callable
+    assert got, got
Repository: cogent3/cogent3
Pull request #1931 (2024-06-28 00:53:58)

DEV: raise not implemented error on top level stringified type hint
Not a fix to #1897, though raises an error when it detects a top-level stringified type hint for either the input or output of an app.

By top-level, I mean it doesn't check nested type hints - i.e. patterns like `typing.Union["int","str"]`. To do so would break the current behaviour of `cogent3.app.typing.get_constraint_names`. It would be better to properly resolve the issue instead.

It will detect the problem-causing `from __future__ import annotations` pattern and raise a `NotImplementedError` for now.


Patch:
diff --git a/src/cogent3/app/composable.py b/src/cogent3/app/composable.py
index 48d7fed47..cdec26aa0 100644
--- a/src/cogent3/app/composable.py
+++ b/src/cogent3/app/composable.py
@@ -153,6 +153,21 @@ def _get_raw_hints(main_func, min_params):
     if depth > 2:
         raise TypeError(msg.format("return_type", return_type, depth))
 
+    if isinstance(first_param_type, str):
+        msg = (
+            "Apps do not yet support string type hints "
+            "(such as those caused by __future__ annotations). "
+            f"Bad type hint: {first_param_type}"
+        )
+        raise NotImplementedError(msg)
+    if isinstance(return_type, str):
+        msg = (
+            "Apps do not yet support string type hints "
+            "(such as those caused by __future__ annotations). "
+            f"Bad type hint: {return_type}"
+        )
+        raise NotImplementedError(msg)
+
     return first_param_type, return_type
 
 


Test patch:
diff --git a/tests/test_app/test_composable.py b/tests/test_app/test_composable.py
index 4066bbb75..3e7e567d8 100644
--- a/tests/test_app/test_composable.py
+++ b/tests/test_app/test_composable.py
@@ -1129,3 +1129,17 @@ def test_copies_doc_from_func():
 
     assert delme2.__doc__ == "my docstring"
     assert delme2.__init__.__doc__.split() == ["Notes", "-----", "body"]
+
+
+def test_bad_wrap():
+    def foo(a: "str") -> int:
+        return int(a)
+
+    with pytest.raises(NotImplementedError):
+        define_app(foo)
+
+    def bar(a: str) -> "int":
+        return int(a)
+
+    with pytest.raises(NotImplementedError):
+        define_app(bar)
Repository: cogent3/cogent3
Pull request #1943 (2024-07-07 03:14:43)

BUG: fixed bug in composable apply_to and as_completed methods


Patch:
diff --git a/src/cogent3/app/composable.py b/src/cogent3/app/composable.py
index cdec26aa0..556a3e78e 100644
--- a/src/cogent3/app/composable.py
+++ b/src/cogent3/app/composable.py
@@ -20,7 +20,12 @@ from cogent3.util import parallel as PAR
 from cogent3.util import progress_display as UI
 from cogent3.util.misc import docstring_to_summary_rest, get_object_provenance
 
-from .data_store import DataMember, get_data_source, get_unique_id
+from .data_store import (
+    DataMember,
+    DataStoreABC,
+    get_data_source,
+    get_unique_id,
+)
 
 
 _builtin_seqs = list, set, tuple
@@ -702,6 +707,8 @@ def _as_completed(self, dstore, parallel=False, par_kw=None, **kwargs) -> Genera
 
     if isinstance(dstore, str):
         dstore = [dstore]
+    elif isinstance(dstore, DataStoreABC):
+        dstore = dstore.completed
     mapped = _proxy_input(dstore)
     if not mapped:
         return mapped
@@ -778,6 +785,8 @@ def _apply_to(
 
     if isinstance(dstore, (str, Path)):  # one filename
         dstore = [dstore]
+    elif isinstance(dstore, DataStoreABC):
+        dstore = dstore.completed
 
     # todo this should fail if somebody provides data that cannot produce a unique_id
     inputs = {}


Test patch:
diff --git a/tests/test_app/test_data_store.py b/tests/test_app/test_data_store.py
index 9f0e03c3c..b2a2eb6d6 100644
--- a/tests/test_app/test_data_store.py
+++ b/tests/test_app/test_data_store.py
@@ -10,8 +10,8 @@ import pytest
 
 from scitrack import get_text_hexdigest
 
-import cogent3.app.io as io_app
-
+from cogent3.app import io as io_app
+from cogent3.app import sample as sample_app
 from cogent3.app.composable import NotCompleted
 from cogent3.app.data_store import (
     _MD5_TABLE,
@@ -743,3 +743,13 @@ def test_directory_data_store_write_compressed(tmp_path):
     )
     got = writer(seqs)  # pylint: disable=not-callable
     assert got, got
+
+
+def test_apply_to_not_completed(nc_dstore, tmp_path):
+    loader = io_app.load_unaligned()
+    num_seqs = sample_app.take_n_seqs(number=3, fixed_choice=False)
+    out_dstore = io_app.open_data_store(tmp_path / "output", suffix="fa", mode="w")
+    writer = io_app.write_seqs(data_store=out_dstore, format="fasta")
+    app = loader + num_seqs + writer
+    fini = app.apply_to(nc_dstore)
+    assert 0 < len(fini.completed) <= len(nc_dstore.completed)
Repository: cogent3/cogent3
Pull request #1955 (2024-07-18 04:04:17)

MAINT: fixed regression and address deprecation warning


Patch:
diff --git a/src/cogent3/app/dist.py b/src/cogent3/app/dist.py
index 043343f20..92ffb22df 100644
--- a/src/cogent3/app/dist.py
+++ b/src/cogent3/app/dist.py
@@ -214,10 +214,7 @@ def jaccard_dist(seq_coll: UnalignedSeqsType, k: int = 10) -> PairwiseDistanceTy
     {('s1', 's2'): 0.4, ('s2', 's1'): 0.4}
     """
 
-    kmers = {
-        name: set(seq_coll.named_seqs[name].iter_kmers(k, strict=True))
-        for name in seq_coll.names
-    }
+    kmers = {seq.name: set(seq.iter_kmers(k, strict=True)) for seq in seq_coll.seqs}
     seq_names = sorted(kmers.keys())
     num_seqs = len(seq_names)
 
diff --git a/src/cogent3/parse/sequence.py b/src/cogent3/parse/sequence.py
index 8047558d7..2fc9b2365 100644
--- a/src/cogent3/parse/sequence.py
+++ b/src/cogent3/parse/sequence.py
@@ -91,7 +91,7 @@ class LineBasedParser:
 
     @__call__.register
     def _(self, data: str, **kwargs) -> ParserOutputType:
-        return self(pathlib.Path(data), **kwargs)
+        yield from self._parse(iter_splitlines(data), **kwargs)
 
     @__call__.register
     def _(self, data: pathlib.Path, **kwargs) -> ParserOutputType:
diff --git a/src/cogent3/util/io.py b/src/cogent3/util/io.py
index f53f65c21..176a62793 100644
--- a/src/cogent3/util/io.py
+++ b/src/cogent3/util/io.py
@@ -365,11 +365,14 @@ def iter_splitlines(
     -----
     Loads chunks of data from the file, yields one line at a time
     """
-    path = Path(path)
-    if chunk_size and path.stat().st_size < chunk_size:
-        # file is smaller than provided chunk_size, just
-        # load it all
+    if _urls.search(str(path)):
         chunk_size = None
+    else:
+        path = Path(path)
+        if chunk_size and path.stat().st_size < chunk_size:
+            # file is smaller than provided chunk_size, just
+            # load it all
+            chunk_size = None
 
     with open_(path) as infile:
         last = ""


Test patch:
diff --git a/tests/test_parse/test_sequence.py b/tests/test_parse/test_sequence.py
index 0e6975593..c95bf623f 100644
--- a/tests/test_parse/test_sequence.py
+++ b/tests/test_parse/test_sequence.py
@@ -53,3 +53,14 @@ def test_line_based_wrap_load_valid_input_types(gde_data):
 def test_get_unknown_format():
     with pytest.raises(ValueError):
         sequence.get_parser("nope")
+
+
+@pytest.mark.internet
+def test_line_based_url(DATA_DIR):
+    fname = "long_testseqs.fasta"
+    url = f"https://raw.githubusercontent.com/cogent3/cogent3/develop/doc/data/{fname}"
+
+    parser = sequence.LineBasedParser(lambda x: x)
+    from_url = list(parser(url))
+    from_local = list(parser(DATA_DIR / fname))
+    assert from_url == from_local