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

rmcar17 / cogent3 / 16667762705

01 Aug 2025 06:10AM UTC coverage: 88.174% (-0.03%) from 88.204%
16667762705

push

github

rmcar17
MAINT: Use attributes for tree length and support, and callback for merging params with prune, removed unused semi-private attributes

51 of 61 new or added lines in 8 files covered. (83.61%)

6 existing lines in 3 files now uncovered.

29062 of 32960 relevant lines covered (88.17%)

5.29 hits per line

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

31.91
/src/cogent3/parse/tree_xml.py
1
#!/usr/bin/env python
2
"""Parses a simple but verbose XML representation of a phylogenetic tree, with
3
elements <clade>, <name>, <param> and <value>.  XML attributes are not used,
4
so the syntax of parameter names is not restricted at all.
5

6
Newick
7
------
8
((a,b:3),c);
9

10
XML
11
---
12
<clade>
13
  <clade>
14
    <clade>
15
       <name>a</name>
16
    </clade>
17
    <clade>
18
       <name>b</name>
19
       <param><name>length</name><value>3.0</value></param>
20
    </clade>
21
  </clade>
22
  <clade>
23
     <name>c</name>
24
  </clade>
25
</clade>
26

27
Parameters are inherited by contained clades unless overridden.
28

29
"""
30

31
import xml.sax
6✔
32
from collections.abc import Callable, Sequence
6✔
33
from typing import TYPE_CHECKING, Any
6✔
34

35
if TYPE_CHECKING:  # pragma: no cover
36
    from cogent3.core.tree import PhyloNode
37

38

39
class TreeHandler(xml.sax.ContentHandler):
6✔
40
    def __init__(
6✔
41
        self,
42
        tree_builder: Callable[
43
            [
44
                Sequence["PhyloNode"],
45
                str | None,
46
                dict[str, Any],
47
                float | None,
48
                float | None,
49
            ],
50
            "PhyloNode",
51
        ],
52
    ) -> None:
53
        self.build_edge = tree_builder
×
54

55
    def startDocument(self) -> None:
6✔
56
        self.stack = [({}, None, None)]
×
57
        self.data = {"clades": [], "params": {}}
×
58
        self.in_clade = False
×
59
        self.current = None
×
60

61
    def startElement(self, name, attrs) -> None:
6✔
62
        self.parent = self.data
×
63
        self.stack.append((self.data, self.in_clade, self.current))
×
64
        self.current = ""
×
65
        if name == "clade":
×
66
            self.data = {
×
67
                "params": self.data["params"].copy(),
68
                "clades": [],
69
                "name": None,
70
            }
71
            self.in_clade = True
×
72
        else:
73
            self.data = {}
×
74
            self.in_clade = False
×
75

76
    def characters(self, text) -> None:
6✔
77
        self.current += str(text)
×
78

79
    def endElement(self, name) -> None:
6✔
80
        getattr(self, f"process_{name}")(self.current, **self.data)
×
81
        (self.data, self.in_clade, self.current) = self.stack.pop()
×
82
        self.parent = self.stack[-1][0]
×
83

84
    def endDocument(self) -> None:
6✔
85
        pass
×
86

87
    def process_clade(self, text, name, params, clades) -> None:
6✔
NEW
88
        length = params.pop("length", None)
×
NEW
89
        support = params.pop("support", None)
×
NEW
90
        edge = self.build_edge(clades, name, params, length, support)
×
UNCOV
91
        self.parent["clades"].append(edge)
×
92

93
    def process_param(self, text, name, value) -> None:
6✔
94
        self.parent["params"][name] = value
×
95

96
    def process_name(self, text) -> None:
6✔
97
        self.parent["name"] = text.strip()
×
98

99
    def process_value(self, text) -> None:
6✔
100
        if text == "None":
×
101
            self.parent["value"] = None
×
102
        else:
103
            self.parent["value"] = float(text)
×
104

105

106
def parse_string(
6✔
107
    text: str,
108
    tree_builder: Callable[
109
        [Sequence["PhyloNode"], str | None, dict[str, Any], float | None, float | None],
110
        "PhyloNode",
111
    ],
112
) -> "PhyloNode":
113
    handler = TreeHandler(tree_builder)
×
114
    xml.sax.parseString(text.encode("utf8"), handler)
×
115
    trees = handler.data["clades"]
×
116
    assert len(trees) == 1, trees
×
117
    return trees[0]
×
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