2323from contextlib import contextmanager
2424from contextvars import ContextVar
2525from functools import partial
26+ from itertools import chain
2627from typing import (
2728 Any ,
2829 Callable ,
@@ -162,7 +163,7 @@ def _monkey_patch_for_jupyter(pydot):
162163
163164 def parse_dot_data (s ):
164165 """Patched to fix pydot/pydot#171 by letting ex bubble-up."""
165- global top_graphs # lint : disable=variable-not-defined-globally
166+ global top_graphs # pylint : disable=variable-not-defined-globally
166167
167168 top_graphs = list ()
168169 graphparser = dot_parser .graph_definition ()
@@ -176,6 +177,18 @@ def parse_dot_data(s):
176177_monkey_patch_for_jupyter (pydot )
177178
178179
180+ def is_nx_node_dependent (graph , nx_node ):
181+ """Return true if node's edges are not :term:`subdoc` only. """
182+ return any (
183+ 1
184+ for _src , _dst , subdoc in chain (
185+ graph .in_edges (nx_node , data = "subdoc" ),
186+ graph .out_edges (nx_node , data = "subdoc" ),
187+ )
188+ if not subdoc
189+ )
190+
191+
179192# TODO: move to base.py, to reduce fan-in imports (and be frank with module diagram).
180193def graphviz_html_string (
181194 s , * , repl_nl = None , repl_colon = None , xmltext = None ,
@@ -691,6 +704,9 @@ class Theme:
691704 #: When true, plot also :term:`execution steps`, linking operations and evictions
692705 #: with green dotted lines labeled with numbers denoting the execution order.
693706 show_steps = False
707+ #: When true, plot also :term:`hierarchical data` nodes that
708+ #: are not directly linked to operations.
709+ show_chaindocs = False
694710 kw_step = {
695711 "style" : "dotted" , # Note: Step styles are not *remerged*.`
696712 "color" : Ref ("steps_color" ),
@@ -1094,9 +1110,15 @@ def build_pydot(self, plot_args: PlotArgs) -> pydot.Dot:
10941110 if plot_args .name :
10951111 dot .set_name (as_identifier (plot_args .name ))
10961112
1113+ hidden = set ()
1114+
10971115 ## NODES
10981116 #
10991117 for nx_node , data in graph .nodes .data (True ):
1118+ if not theme .show_chaindocs and not is_nx_node_dependent (graph , nx_node ):
1119+ hidden .add (nx_node )
1120+ continue
1121+
11001122 plot_args = base_plot_args ._replace (nx_item = nx_node , nx_attrs = data )
11011123 dot_node = self ._make_node (plot_args )
11021124 plot_args = plot_args ._replace (dot_item = dot_node )
@@ -1106,6 +1128,9 @@ def build_pydot(self, plot_args: PlotArgs) -> pydot.Dot:
11061128 ## EDGES
11071129 #
11081130 for src , dst , data in graph .edges .data (True ):
1131+ if src in hidden or dst in hidden :
1132+ continue
1133+
11091134 plot_args = base_plot_args ._replace (nx_item = (src , dst ), nx_attrs = data )
11101135 dot .add_edge (self ._make_edge (plot_args ))
11111136
@@ -1156,7 +1181,7 @@ def _make_node(self, plot_args: PlotArgs) -> pydot.Node:
11561181
11571182 3. Set tooltips with the solution-values for data-nodes.
11581183 """
1159- theme = plot_args .theme
1184+ theme : Theme = plot_args .theme
11601185 graph = plot_args .graph
11611186 nx_node = plot_args .nx_item
11621187 node_attrs = plot_args .nx_attrs
0 commit comments