Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions bigtree/tree/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -1556,9 +1556,22 @@ def tree_to_newick(
if isinstance(attr_sep, NewickCharacter):
attr_sep = attr_sep.value

def _serialize(item: Any) -> Any:
"""Serialize item if it contains special Newick characters

Args:
item (Any): item to serialize

Returns:
(Any)
"""
if isinstance(item, str) and set(item).intersection(NewickCharacter.values()):
item = f"""'{item.replace(NewickCharacter.ATTR_QUOTE, '"')}'"""
return item

node_name_str = ""
if (intermediate_node_name) or (not intermediate_node_name and tree.is_leaf):
node_name_str = tree.node_name
node_name_str = _serialize(tree.node_name)
if length_attr and not tree.is_root:
if not tree.get_attr(length_attr):
raise ValueError(f"Length attribute does not exist for node {tree}")
Expand All @@ -1567,7 +1580,11 @@ def tree_to_newick(
attr_str = ""
if attr_list:
attr_str = attr_sep.join(
[f"{k}={tree.get_attr(k)}" for k in attr_list if tree.get_attr(k)]
[
f"{_serialize(k)}={_serialize(tree.get_attr(k))}"
for k in attr_list
if tree.get_attr(k)
]
)
if attr_str:
attr_str = f"[{attr_prefix}{attr_str}]"
Expand Down
4 changes: 4 additions & 0 deletions bigtree/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,7 @@ class NewickCharacter(str, Enum):
ATTR_QUOTE = "'"
SEP = ":"
NODE_SEP = ","

@classmethod
def values(cls) -> List[str]:
return [c.value for c in cls]
17 changes: 17 additions & 0 deletions tests/tree/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -1635,6 +1635,23 @@ def test_tree_to_newick_intermediate_node_name(tree_node):
expected_str = "((d[&&NHX:age=40],(g[&&NHX:age=10],h[&&NHX:age=6])[&&NHX:age=35])[&&NHX:age=65],(f[&&NHX:age=38])[&&NHX:age=60])[&&NHX:age=90]"
assert newick_str == expected_str

@staticmethod
def test_tree_to_newick_special_character():
from bigtree.node.node import Node

root = Node("(root)")
b = Node("[b]", parent=root)
c = Node("c:", parent=root)
_ = Node("d,", parent=b)
e = Node(":e", parent=b)
_ = Node("f=", parent=c)
_ = Node('"g"', parent=e)
_ = Node("'h'", parent=e)

newick_str = tree_to_newick(root)
expected_str = """(('d,',("g",'"h"')':e')'[b]',('f=')'c:')'(root)'"""
assert newick_str == expected_str

@staticmethod
def test_tree_to_newick_phylogenetic(phylogenetic_tree):
newick_str = tree_to_newick(
Expand Down