Repository: nipy/nipype

Issue:
Issues with color plotting for deep workflows
For deeply nested workflows (5 deep in my case), colors don't seem to operate as intended.

Sometimes (but perplexingly, not always) an error is thrown when `Workflow._get_dot` indexes beyond the length of `colorset`.



Discussion:

Resolving patch:
diff --git a/nipype/pipeline/engine/workflows.py b/nipype/pipeline/engine/workflows.py
--- a/nipype/pipeline/engine/workflows.py
+++ b/nipype/pipeline/engine/workflows.py
@@ -902,8 +902,13 @@ def _get_dot(self, prefix=None, hierarchy=None, colored=False,
             prefix = '  '
         if hierarchy is None:
             hierarchy = []
-        colorset = ['#FFFFC8', '#0000FF', '#B4B4FF', '#E6E6FF', '#FF0000',
-                    '#FFB4B4', '#FFE6E6', '#00A300', '#B4FFB4', '#E6FFE6']
+        colorset = ['#FFFFC8',                       # Y
+                    '#0000FF', '#B4B4FF', '#E6E6FF', # B
+                    '#FF0000', '#FFB4B4', '#FFE6E6', # R
+                    '#00A300', '#B4FFB4', '#E6FFE6', # G
+                    '#0000FF', '#B4B4FF'] # loop B
+        if level > len(colorset) - 2:
+            level = 3 # Loop back to blue
 
         dotlist = ['%slabel="%s";' % (prefix, self.name)]
         for node in nx.topological_sort(self._graph):
@@ -942,8 +947,6 @@ def _get_dot(self, prefix=None, hierarchy=None, colored=False,
                                              colored=colored,
                                              simple_form=simple_form, level=level + 3))
                 dotlist.append('}')
-                if level == 6:
-                    level = 2
             else:
                 for subnode in self._graph.successors_iter(node):
                     if node._hierarchy != subnode._hierarchy:


Test patch:
diff --git a/nipype/pipeline/engine/tests/test_engine.py b/nipype/pipeline/engine/tests/test_engine.py
--- a/nipype/pipeline/engine/tests/test_engine.py
+++ b/nipype/pipeline/engine/tests/test_engine.py
@@ -750,3 +750,70 @@ def func1(in1):
 
     os.chdir(cwd)
     rmtree(wd)
+
+
+def test_write_graph_runs():
+    cwd = os.getcwd()
+    wd = mkdtemp()
+    os.chdir(wd)
+
+    for graph in ('orig', 'flat', 'exec', 'hierarchical', 'colored'):
+        for simple in (True, False):
+            pipe = pe.Workflow(name='pipe')
+            mod1 = pe.Node(interface=TestInterface(), name='mod1')
+            mod2 = pe.Node(interface=TestInterface(), name='mod2')
+            pipe.connect([(mod1, mod2, [('output1', 'input1')])])
+            try:
+                pipe.write_graph(graph2use=graph, simple_form=simple)
+            except Exception:
+                yield assert_true, False, \
+                'Failed to plot {} {} graph'.format(
+                    'simple' if simple else 'detailed', graph)
+
+            yield assert_true, os.path.exists('graph.dot') or os.path.exists('graph_detailed.dot')
+            try:
+                os.remove('graph.dot')
+            except OSError:
+                pass
+            try:
+                os.remove('graph_detailed.dot')
+            except OSError:
+                pass
+
+    os.chdir(cwd)
+    rmtree(wd)
+
+def test_deep_nested_write_graph_runs():
+    cwd = os.getcwd()
+    wd = mkdtemp()
+    os.chdir(wd)
+
+    for graph in ('orig', 'flat', 'exec', 'hierarchical', 'colored'):
+        for simple in (True, False):
+            pipe = pe.Workflow(name='pipe')
+            parent = pipe
+            for depth in range(10):
+                sub = pe.Workflow(name='pipe_nest_{}'.format(depth))
+                parent.add_nodes([sub])
+                parent = sub
+            mod1 = pe.Node(interface=TestInterface(), name='mod1')
+            parent.add_nodes([mod1])
+            try:
+                pipe.write_graph(graph2use=graph, simple_form=simple)
+            except Exception as e:
+                yield assert_true, False, \
+                'Failed to plot {} {} deep graph: {!s}'.format(
+                    'simple' if simple else 'detailed', graph, e)
+
+            yield assert_true, os.path.exists('graph.dot') or os.path.exists('graph_detailed.dot')
+            try:
+                os.remove('graph.dot')
+            except OSError:
+                pass
+            try:
+                os.remove('graph_detailed.dot')
+            except OSError:
+                pass
+
+    os.chdir(cwd)
+    rmtree(wd)