You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hamilton.apache.org by je...@apache.org on 2026/07/02 10:22:29 UTC

(hamilton) branch main updated: Fix collect visualization edge styling (#1651)

This is an automated email from the ASF dual-hosted git repository.

jernejfrank pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hamilton.git


The following commit(s) were added to refs/heads/main by this push:
     new 5e5a4495 Fix collect visualization edge styling (#1651)
5e5a4495 is described below

commit 5e5a44951859f60c914f8fce7f8af78bdf939943
Author: Probablism <lu...@gmail.com>
AuthorDate: Thu Jul 2 06:22:25 2026 -0400

    Fix collect visualization edge styling (#1651)
---
 hamilton/graph.py   |  9 ++++++++-
 tests/test_graph.py | 27 +++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/hamilton/graph.py b/hamilton/graph.py
index 6e6caa86..a22b5185 100644
--- a/hamilton/graph.py
+++ b/hamilton/graph.py
@@ -437,6 +437,13 @@ def create_graphviz_graph(
 
         return edge_style
 
+    def _is_collect_dependency_edge(dependency_node: node.Node, target_node: node.Node) -> bool:
+        """Returns true for the edge that feeds a Collect[...] dependency."""
+        return (
+            target_node.node_role == node.NodeType.COLLECT
+            and dependency_node.name == target_node.collect_dependency
+        )
+
     def _get_legend(
         node_types: set[str], extra_legend_nodes: dict[tuple[str, str], dict[str, str]]
     ):
@@ -648,7 +655,6 @@ def create_graphviz_graph(
     # create edges
     input_sets = dict()
     for n in nodes:
-        to_type = "collect" if n.node_role == node.NodeType.COLLECT else ""
         to_modifiers = node_modifiers.get(n.name, set())
 
         input_nodes = set()
@@ -664,6 +670,7 @@ def create_graphviz_graph(
                 continue
 
             from_type = "expand" if d.node_role == node.NodeType.EXPAND else ""
+            to_type = "collect" if _is_collect_dependency_edge(d, n) else ""
             dependency_modifiers = node_modifiers.get(d.name, set())
             edge_style = _get_edge_style(from_type, to_type)
             if (
diff --git a/tests/test_graph.py b/tests/test_graph.py
index bbf473d7..b2000018 100644
--- a/tests/test_graph.py
+++ b/tests/test_graph.py
@@ -42,6 +42,7 @@ import tests.resources.display_name_functions
 import tests.resources.display_name_list_functions
 import tests.resources.dummy_functions
 import tests.resources.dummy_functions_module_override
+import tests.resources.dynamic_parallelism.parallel_collect_multiple_arguments
 import tests.resources.extract_column_nodes
 import tests.resources.extract_columns_execution_count
 import tests.resources.functions_with_generics
@@ -1198,6 +1199,32 @@ def test_function_graph_display_config_node():
     assert any(line.startswith("\tX") for line in dot.body)
 
 
+def test_function_graph_display_collect_edges_only_style_collected_dependency():
+    config = {}
+    fg = graph.FunctionGraph.from_modules(
+        tests.resources.dynamic_parallelism.parallel_collect_multiple_arguments, config=config
+    )
+
+    assert fg.nodes["summed"].node_role == NodeType.COLLECT
+    assert fg.nodes["summed"].collect_dependency == "double"
+
+    dot = fg.display(set(fg.get_nodes()), output_file_path=None, config=config)
+
+    edge_lines = {
+        line.strip().split(" [", 1)[0]: line.strip() for line in dot.body if " -> " in line
+    }
+    assert edge_lines["double -> summed"] == "double -> summed [arrowtail=crow dir=both]"
+    assert edge_lines["not_to_repeat -> summed"] == "not_to_repeat -> summed"
+    assert (
+        edge_lines["something_else_not_to_repeat -> summed"]
+        == "something_else_not_to_repeat -> summed"
+    )
+    assert (
+        edge_lines["number_to_repeat -> double"]
+        == "number_to_repeat -> double [arrowhead=crow arrowtail=none dir=both]"
+    )
+
+
 # TODO use high-level visualization dot as fixtures for reuse across tests
 def test_display_config_node():
     """Check if config is displayed by high-level hamilton.driver.display..."""