• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cogent3 / cogent3 / 24544474967

17 Apr 2026 02:23AM UTC coverage: 90.01% (-0.7%) from 90.684%
24544474967

push

github

web-flow
Merge pull request #2622 from GavinHuttley/develop

ENH: adopt scinexus for app infrastructure and utility functions

290 of 305 new or added lines in 59 files covered. (95.08%)

177 existing lines in 7 files now uncovered.

27536 of 30592 relevant lines covered (90.01%)

5.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.0
/src/cogent3/format/sequence.py
1
import abc
6✔
2
import contextlib
6✔
3
import os
6✔
4
import pathlib
6✔
5
import typing
6✔
6
from collections.abc import Callable
6✔
7

8
from cogent3.format import clustal, fasta, gde, paml, phylip
6✔
9
from cogent3.parse.record import FileFormatError
6✔
10

11
SeqsTypes = "CollectionBase[typing.Any] | Sequence"
6✔
12

13
FORMATTERS: dict[str, Callable[..., str]] = {
6✔
14
    "phylip": phylip.alignment_to_phylip,
15
    "paml": paml.alignment_to_paml,
16
    "fasta": fasta.seqs_to_fasta,
17
    "gde": gde.alignment_to_gde,
18
    "clustal": clustal.clustal_from_alignment,
19
}
20

21

22
class SequenceWriterBase(abc.ABC):
6✔
23
    """Base class for sequence format parsers."""
24

25
    @property
6✔
26
    @abc.abstractmethod
6✔
27
    def name(self) -> str:
6✔
28
        """name of the format"""
29
        ...
30

31
    @property
6✔
32
    def supports_unaligned(self) -> bool:
6✔
33
        """True if the writer supports unaligned sequences"""
34
        return True
×
35

36
    @property
6✔
37
    def supports_aligned(self) -> bool:
6✔
38
        """True if the writer supports aligned sequences"""
39
        return True
×
40

41
    @property
6✔
42
    @abc.abstractmethod
6✔
43
    def supported_suffixes(self) -> set[str]:
6✔
44
        """Return list of file suffixes this parser supports"""
45
        ...
46

47
    def formatted(self, seqcoll: SeqsTypes, **kwargs: typing.Any) -> str:
6✔
48
        """returns a string representation of the sequence collection
49

50
        Parameters
51
        ----------
52
        seqcoll
53
            sequence, or a sequence collection, to format, must have a to_dict() method
54
        """
55
        formatter = FORMATTERS[self.name]
6✔
56
        return formatter(seqcoll.to_dict(), **kwargs)
6✔
57

58
    def write(
6✔
59
        self,
60
        *,
61
        path: pathlib.Path | str,
62
        seqcoll: SeqsTypes,
63
        **kwargs: typing.Any,
64
    ) -> pathlib.Path | str:
65
        """returns a path after writing the sequence collection to a file
66
        Parameters
67
        ----------
68
        path
69
            path to the file to write
70
        seqcoll
71
            sequence collection to write, must have a to_dict() method
72
        kwargs
73
            additional arguments to pass to the formatter
74
        """
75
        from scinexus.io_util import atomic_write
6✔
76

77
        output = self.formatted(seqcoll, **kwargs)
6✔
78
        with atomic_write(path, mode="wt") as f:
6✔
79
            f.write(output)
6✔
80
        return path
6✔
81

82

83
class FastaWriter(SequenceWriterBase):
6✔
84
    @property
6✔
85
    def name(self) -> str:
6✔
86
        return "fasta"
6✔
87

88
    @property
6✔
89
    def supported_suffixes(self) -> set[str]:
6✔
90
        return {"fasta", "fa", "fna", "faa", "mfa"}
6✔
91

92

93
class GdeWriter(SequenceWriterBase):
6✔
94
    @property
6✔
95
    def name(self) -> str:
6✔
96
        return "gde"
6✔
97

98
    @property
6✔
99
    def supported_suffixes(self) -> set[str]:
6✔
100
        return {"gde"}
6✔
101

102

103
class PhylipWriter(SequenceWriterBase):
6✔
104
    """Parser for PHYLIP format sequence files."""
105

106
    @property
6✔
107
    def name(self) -> str:
6✔
108
        return "phylip"
6✔
109

110
    @property
6✔
111
    def supported_suffixes(self) -> set[str]:
6✔
112
        return {"phylip", "phy"}
6✔
113

114

115
class PamlWriter(SequenceWriterBase):
6✔
116
    """Parser for PAML format sequence files."""
117

118
    @property
6✔
119
    def name(self) -> str:
6✔
120
        return "paml"
6✔
121

122
    @property
6✔
123
    def supported_suffixes(self) -> set[str]:
6✔
124
        return {"paml"}
6✔
125

126

127
def save_to_filename(alignment, filename, format, **kw) -> None:
6✔
128
    """Arguments:
129
    - alignment: to be written
130
    - filename: name of the sequence alignment file
131
    - format: the multiple sequence file format
132
    """
NEW
133
    from scinexus.io_util import atomic_write
×
134

135
    if format is None:
×
136
        msg = "format not known"
×
137
        raise FileFormatError(msg)
×
138

139
    with atomic_write(filename, mode="wt") as f:
×
140
        try:
×
141
            write_alignment_to_file(f, alignment, format, **kw)
×
142
        except Exception:
×
143
            with contextlib.suppress(Exception):
×
144
                os.unlink(filename)
×
145
            raise
×
146

147

148
def write_alignment_to_file(f, alignment, format, **kw) -> None:
6✔
149
    format = format.lower()
×
150
    if format not in FORMATTERS:
×
151
        msg = f"Unsupported file format {format}"
×
152
        raise FileFormatError(msg)
×
153
    contents = FORMATTERS[format](alignment, **kw)
×
154
    f.write(contents)
×
155
    f.close()
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc