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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.19.4] - 2024-08-15
### Changed:
- Docs: Clean CSS for playground.
- Misc: Refactor tests for tree_to_mermaid`.
- Misc: Refactor tests for `tree_to_mermaid`.
- Misc: Allow untyped calls in mypy type checking due to ImageFont.truetype call.
### Fixed:
- Tree Exporter: `tree_to_mermaid` fix where the node colour is added wrongly to the wrong node.
- Misc: Fix and update code examples in docstring.
- Misc: Fix test cases for pydot due to code upgrade.

## [0.19.3] - 2024-07-09
### Fixed:
Expand Down
86 changes: 86 additions & 0 deletions tests/dag/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,22 @@ def test_dag_to_dot_fill_colour(dag_node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_dag_to_dot_fill_colour2(dag_node):
graph = dag_to_dot(dag_node, node_colour="gold")
expected = """strict digraph G {\nrankdir=TB;\nc [label=c, style=filled, fillcolor=gold];\na [label=a, style=filled, fillcolor=gold];\na -> c;\nd [label=d, style=filled, fillcolor=gold];\na [label=a, style=filled, fillcolor=gold];\na -> d;\nc [label=c, style=filled, fillcolor=gold];\nb [label=b, style=filled, fillcolor=gold];\nb -> c;\nd [label=d, style=filled, fillcolor=gold];\nc [label=c, style=filled, fillcolor=gold];\nc -> d;\nf [label=f, style=filled, fillcolor=gold];\nc [label=c, style=filled, fillcolor=gold];\nc -> f;\ng [label=g, style=filled, fillcolor=gold];\nc [label=c, style=filled, fillcolor=gold];\nc -> g;\ne [label=e, style=filled, fillcolor=gold];\nd [label=d, style=filled, fillcolor=gold];\nd -> e;\nf [label=f, style=filled, fillcolor=gold];\nd [label=d, style=filled, fillcolor=gold];\nd -> f;\nh [label=h, style=filled, fillcolor=gold];\ng [label=g, style=filled, fillcolor=gold];\ng -> h;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/dag_fill_colour.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -212,6 +228,22 @@ def test_dag_to_dot_edge_colour(dag_node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_dag_to_dot_edge_colour2(dag_node):
graph = dag_to_dot(dag_node, edge_colour="red")
expected = """strict digraph G {\nrankdir=TB;\nc [label=c];\na [label=a];\na -> c [color=red];\nd [label=d];\na [label=a];\na -> d [color=red];\nc [label=c];\nb [label=b];\nb -> c [color=red];\nd [label=d];\nc [label=c];\nc -> d [color=red];\nf [label=f];\nc [label=c];\nc -> f [color=red];\ng [label=g];\nc [label=c];\nc -> g [color=red];\ne [label=e];\nd [label=d];\nd -> e [color=red];\nf [label=f];\nd [label=d];\nd -> f [color=red];\nh [label=h];\ng [label=g];\ng -> h [color=red];\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/dag_edge_colour.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
def test_dag_to_dot_node_shape(dag_node):
graph = dag_to_dot(dag_node, node_shape="triangle")
Expand Down Expand Up @@ -239,6 +271,22 @@ def test_dag_to_dot_node_attr(dag_node_style):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_dag_to_dot_node_attr2(dag_node_style):
graph = dag_to_dot(dag_node_style, node_attr="node_style")
expected = """strict digraph G {\nrankdir=TB;\nc [label=c, style=filled, fillcolor=blue];\na [label=a, style=filled, fillcolor=gold];\na -> c;\nd [label=d, style=filled, fillcolor=green];\na [label=a, style=filled, fillcolor=gold];\na -> d;\nc [label=c, style=filled, fillcolor=blue];\nb [label=b, style=filled, fillcolor=blue];\nb -> c;\nd [label=d, style=filled, fillcolor=green];\nc [label=c, style=filled, fillcolor=blue];\nc -> d;\nf [label=f, style=filled, fillcolor=green];\nc [label=c, style=filled, fillcolor=blue];\nc -> f;\ng [label=g, style=filled, fillcolor=red];\nc [label=c, style=filled, fillcolor=blue];\nc -> g;\ne [label=e, style=filled, fillcolor=green];\nd [label=d, style=filled, fillcolor=green];\nd -> e;\nf [label=f, style=filled, fillcolor=green];\nd [label=d, style=filled, fillcolor=green];\nd -> f;\nh [label=h, style=filled, fillcolor=red];\ng [label=g, style=filled, fillcolor=red];\ng -> h;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/dag_node_attr.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -254,6 +302,22 @@ def test_dag_to_dot_edge_attr(dag_node_style):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_dag_to_dot_edge_attr2(dag_node_style):
graph = dag_to_dot(dag_node_style, edge_attr="edge_style")
expected = """strict digraph G {\nrankdir=TB;\nc [label=c];\na [label=a];\na -> c [style=bold, label=c];\nd [label=d];\na [label=a];\na -> d [style=bold, label=1];\nc [label=c];\nb [label=b];\nb -> c [style=bold, label=c];\nd [label=d];\nc [label=c];\nc -> d [style=bold, label=1];\nf [label=f];\nc [label=c];\nc -> f [style=bold, label=3];\ng [label=g];\nc [label=c];\nc -> g [style=bold, label=4];\ne [label=e];\nd [label=d];\nd -> e [style=bold, label=2];\nf [label=f];\nd [label=d];\nd -> f [style=bold, label=3];\nh [label=h];\ng [label=g];\ng -> h [style=bold, label=5];\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/dag_edge_attr.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -274,3 +338,25 @@ def test_dag_to_dot_attr_override(dag_node):
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_dag_to_dot_attr_override2(dag_node):
dag_node.children[0].set_attrs(
{
"node_style": {"style": "filled", "fillcolor": "blue"},
"edge_style": {"style": "bold"},
}
)
graph = dag_to_dot(dag_node, node_attr="node_style", edge_attr="edge_style")
expected = """strict digraph G {\nrankdir=TB;\nc [label=c, style=filled, fillcolor=blue];\na [label=a];\na -> c [style=bold];\nd [label=d];\na [label=a];\na -> d;\nc [label=c, style=filled, fillcolor=blue];\nb [label=b];\nb -> c [style=bold];\nd [label=d];\nc [label=c, style=filled, fillcolor=blue];\nc -> d;\nf [label=f];\nc [label=c, style=filled, fillcolor=blue];\nc -> f;\ng [label=g];\nc [label=c, style=filled, fillcolor=blue];\nc -> g;\ne [label=e];\nd [label=d];\nd -> e;\nf [label=f];\nd [label=d];\nd -> f;\nh [label=h];\ng [label=g];\ng -> h;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/dag_attr_override.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"
125 changes: 125 additions & 0 deletions tests/tree/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,22 @@ def test_tree_to_dot_fill_colour(tree_node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_fill_colour2(tree_node):
graph = tree_to_dot(tree_node, node_colour="gold")
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a, style=filled, fillcolor=gold];\nb0 [label=b, style=filled, fillcolor=gold];\na0 -> b0;\nd0 [label=d, style=filled, fillcolor=gold];\nb0 -> d0;\ne0 [label=e, style=filled, fillcolor=gold];\nb0 -> e0;\ng0 [label=g, style=filled, fillcolor=gold];\ne0 -> g0;\nh0 [label=h, style=filled, fillcolor=gold];\ne0 -> h0;\nc0 [label=c, style=filled, fillcolor=gold];\na0 -> c0;\nf0 [label=f, style=filled, fillcolor=gold];\nc0 -> f0;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_fill_colour.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
def test_tree_to_dot_edge_colour(tree_node):
graph = tree_to_dot(tree_node, edge_colour="red")
Expand Down Expand Up @@ -1717,6 +1733,22 @@ def test_tree_to_dot_node_attr(tree_node_style):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_node_attr2(tree_node_style):
graph = tree_to_dot(tree_node_style, node_attr="node_style")
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a, style=filled, fillcolor=gold];\nb0 [label=b, style=filled, fillcolor=blue];\na0 -> b0;\nd0 [label=d, style=filled, fillcolor=green];\nb0 -> d0;\ng0 [label=g, style=filled, fillcolor=red];\nd0 -> g0;\ne0 [label=e, style=filled, fillcolor=green];\nb0 -> e0;\nh0 [label=h, style=filled, fillcolor=red];\ne0 -> h0;\nc0 [label=c, style=filled, fillcolor=blue];\na0 -> c0;\nf0 [label=f, style=filled, fillcolor=green];\nc0 -> f0;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_node_attr.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -1741,6 +1773,31 @@ def get_node_attr(node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_node_attr_callable2(tree_node_style_callable):
def get_node_attr(node):
if node.get_attr("style") and node.style == 1:
return {"style": "filled", "fillcolor": "gold"}
elif node.get_attr("style") and node.style == "two":
return {"style": "filled", "fillcolor": "blue"}
elif node.node_name in ["d", "e", "f"]:
return {"style": "filled", "fillcolor": "green"}
return {"style": "filled", "fillcolor": "red"}

graph = tree_to_dot(tree_node_style_callable, node_attr=get_node_attr)
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a, style=filled, fillcolor=gold];\nb0 [label=b, style=filled, fillcolor=blue];\na0 -> b0;\nd0 [label=d, style=filled, fillcolor=green];\nb0 -> d0;\ng0 [label=g, style=filled, fillcolor=red];\nd0 -> g0;\ne0 [label=e, style=filled, fillcolor=green];\nb0 -> e0;\nh0 [label=h, style=filled, fillcolor=red];\ne0 -> h0;\nc0 [label=c, style=filled, fillcolor=red];\na0 -> c0;\nf0 [label=f, style=filled, fillcolor=green];\nc0 -> f0;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_node_attr_callable.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -1756,6 +1813,22 @@ def test_tree_to_dot_edge_attr(tree_node_style):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_edge_attr2(tree_node_style):
graph = tree_to_dot(tree_node_style, edge_attr="edge_style")
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a];\nb0 [label=b];\na0 -> b0 [style=bold, label=b];\nd0 [label=d];\nb0 -> d0 [style=bold, label=1];\ng0 [label=g];\nd0 -> g0 [style=bold, label=4];\ne0 [label=e];\nb0 -> e0 [style=bold, label=2];\nh0 [label=h];\ne0 -> h0 [style=bold, label=5];\nc0 [label=c];\na0 -> c0 [style=bold, label=c];\nf0 [label=f];\nc0 -> f0 [style=bold, label=3];\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_edge_attr.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand Down Expand Up @@ -1785,6 +1858,36 @@ def get_edge_attr(node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_edge_attr_callable2(tree_node_style_callable):
def get_edge_attr(node):
if node.get_attr("style") and node.style == 1:
return {"style": "bold", "label": "a"}
elif node.get_attr("style") and node.style == "two":
return {"style": "bold", "label": "b"}
elif node.get_attr("style") and node.style == ("three"):
return {"style": "bold", "label": "c"}
elif node.node_name in ["d", "e", "f", "g", "h"]:
return {
"style": "bold",
"label": ["d", "e", "f", "g", "h"].index(node.node_name) + 1,
}
raise Exception("Node with invalid edge_attr not covered")

graph = tree_to_dot(tree_node_style_callable, edge_attr=get_edge_attr)
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a];\nb0 [label=b];\na0 -> b0 [style=bold, label=b];\nd0 [label=d];\nb0 -> d0 [style=bold, label=1];\ng0 [label=g];\nd0 -> g0 [style=bold, label=4];\ne0 [label=e];\nb0 -> e0 [style=bold, label=2];\nh0 [label=h];\ne0 -> h0 [style=bold, label=5];\nc0 [label=c];\na0 -> c0 [style=bold, label=c];\nf0 [label=f];\nc0 -> f0 [style=bold, label=3];\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_edge_attr_callable.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ >= "3.0.0", reason="Results have different ordering"
Expand All @@ -1806,6 +1909,28 @@ def test_tree_to_dot_attr_override(tree_node):
expected_str in actual
), f"Expected {expected_str} not in actual string"

@staticmethod
@pytest.mark.skipif(
pydot.__version__ < "3.0.0",
reason="Results have different ordering (new pydot)",
)
def test_tree_to_dot_attr_override2(tree_node):
tree_node.children[0].set_attrs(
{
"node_style": {"style": "filled", "fillcolor": "blue"},
"edge_style": {"style": "bold"},
}
)
graph = tree_to_dot(tree_node, node_attr="node_style", edge_attr="edge_style")
expected = """strict digraph G {\nrankdir=TB;\na0 [label=a];\nb0 [label=b, style=filled, fillcolor=blue];\na0 -> b0 [style=bold];\nd0 [label=d];\nb0 -> d0;\ne0 [label=e];\nb0 -> e0;\ng0 [label=g];\ne0 -> g0;\nh0 [label=h];\ne0 -> h0;\nc0 [label=c];\na0 -> c0;\nf0 [label=f];\nc0 -> f0;\n}\n"""
actual = graph.to_string()
if LOCAL:
graph.write_png("tests/tree_attr_override.png")
for expected_str in expected.split():
assert (
expected_str in actual
), f"Expected {expected_str} not in actual string"


class TestTreeToPillow:
@staticmethod
Expand Down