-
Notifications
You must be signed in to change notification settings - Fork 532
FIX: Node __repr__ and detailed graph expansion #2669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
eadb14f
431be35
7ed4ea2
f860ad6
052ee72
879ea28
670c39e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ | |
from builtins import open | ||
from copy import deepcopy | ||
from glob import glob | ||
import os | ||
import os, pdb | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't seem to use |
||
|
||
|
||
import pytest | ||
|
@@ -441,6 +441,7 @@ def test_write_graph_runs(tmpdir): | |
|
||
assert os.path.exists('graph.dot') or os.path.exists( | ||
'graph_detailed.dot') | ||
|
||
try: | ||
os.remove('graph.dot') | ||
except OSError: | ||
|
@@ -484,6 +485,154 @@ def test_deep_nested_write_graph_runs(tmpdir): | |
pass | ||
|
||
|
||
# examples of dot files used in the following test | ||
dotfile_orig = ['strict digraph {\n', | ||
'"mod1 (engine)";\n', | ||
'"mod2 (engine)";\n', | ||
'"mod1 (engine)" -> "mod2 (engine)";\n', | ||
'}\n'] | ||
|
||
dotfile_detailed_orig = ['digraph structs {\n', | ||
'node [shape=record];\n', | ||
'pipemod1 [label="{IN}|{ mod1 | engine | }|{OUT|<outoutput1> output1}"];\n', | ||
'pipemod2 [label="{IN|<ininput1> input1}|{ mod2 | engine | }|{OUT}"];\n', | ||
'pipemod1:outoutput1:e -> pipemod2:ininput1:w;\n', | ||
'}'] | ||
|
||
dotfile_flat = dotfile_orig | ||
dotfile_detailed_flat = dotfile_detailed_orig | ||
|
||
dotfile_exec = dotfile_orig | ||
dotfile_detailed_exec = dotfile_detailed_orig | ||
|
||
|
||
dotfile_hierarchical = ['digraph pipe{\n', | ||
' label="pipe";\n', | ||
' pipe_mod1[label="mod1 (engine)"];\n', | ||
' pipe_mod2[label="mod2 (engine)"];\n', | ||
' pipe_mod1 -> pipe_mod2;\n', | ||
'}'] | ||
|
||
dotfile_colored = ['digraph pipe{\n', | ||
' label="pipe";\n', | ||
' pipe_mod1[label="mod1 (engine)", style=filled, fillcolor="#FFFFC8"];\n', | ||
' pipe_mod2[label="mod2 (engine)", style=filled, fillcolor="#FFFFC8"];\n', | ||
' pipe_mod1 -> pipe_mod2;\n', | ||
'}'] | ||
|
||
@pytest.mark.parametrize("simple", [True, False]) | ||
@pytest.mark.parametrize("graph_type", ['orig', 'flat', 'exec', 'hierarchical', 'colored']) | ||
def test_write_graph_dotfile(tmpdir, graph_type, simple): | ||
""" checking dot files for a workflow without iterables""" | ||
tmpdir.chdir() | ||
|
||
pipe = pe.Workflow(name='pipe') | ||
mod1 = pe.Node(interface=EngineTestInterface(), name='mod1') | ||
mod2 = pe.Node(interface=EngineTestInterface(), name='mod2') | ||
pipe.connect([(mod1, mod2, [('output1', 'input1')])]) | ||
pipe.write_graph( | ||
graph2use=graph_type, simple_form=simple, format='dot') | ||
|
||
with open("graph.dot") as f: | ||
graph_str = f.read() | ||
|
||
if simple: | ||
for str in eval("dotfile_{}".format(graph_type)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we avoid dotfiles = {
'hierarchical': dotfile_hierarchical,
...
}
...
for contents in dotfiles[graph_type]:
... Also, please do not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, will remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm against the use of
A decent StackOverflow thread: Why is using 'eval' a bad practice? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, will change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for comments |
||
assert str in graph_str | ||
else: | ||
# if simple=False graph.dot uses longer names | ||
for str in eval("dotfile_{}".format(graph_type)): | ||
if graph_type in ["hierarchical", "colored"]: | ||
assert str.replace("mod1 (engine)", "mod1.EngineTestInterface.engine").replace( | ||
"mod2 (engine)", "mod2.EngineTestInterface.engine") in graph_str | ||
else: | ||
assert str.replace( | ||
"mod1 (engine)", "pipe.mod1.EngineTestInterface.engine").replace( | ||
"mod2 (engine)", "pipe.mod2.EngineTestInterface.engine") in graph_str | ||
|
||
# graph_detailed is not created for hierachical or colored | ||
if graph_type not in ["hierarchical", "colored"]: | ||
with open("graph_detailed.dot") as f: | ||
graph_str = f.read() | ||
for str in eval("dotfile_detailed_{}".format(graph_type)): | ||
assert str in graph_str | ||
|
||
|
||
# examples of dot files used in the following test | ||
dotfile_iter_orig = dotfile_orig | ||
dotfile_detailed_iter_orig = dotfile_detailed_orig | ||
|
||
dotfile_iter_flat = dotfile_orig | ||
dotfile_detailed_iter_flat = dotfile_detailed_orig | ||
|
||
dotfile_iter_exec = dotfile_orig | ||
dotfile_detailed_iter_exec = [ | ||
'digraph structs {\n', | ||
'node [shape=record];\n', | ||
'pipemod1aIa1 [label="{IN}|{ a1 | engine | mod1.aI }|{OUT|<outoutput1> output1}"];\n', | ||
'pipemod2a1 [label="{IN|<ininput1> input1}|{ a1 | engine | mod2 }|{OUT}"];\n', | ||
'pipemod1aIa0 [label="{IN}|{ a0 | engine | mod1.aI }|{OUT|<outoutput1> output1}"];\n', | ||
'pipemod2a0 [label="{IN|<ininput1> input1}|{ a0 | engine | mod2 }|{OUT}"];\n', | ||
'pipemod1aIa0:outoutput1:e -> pipemod2a0:ininput1:w;\n', | ||
'pipemod1aIa1:outoutput1:e -> pipemod2a1:ininput1:w;\n', | ||
'}'] | ||
|
||
dotfile_iter_hierarchical = [ | ||
'digraph pipe{\n', | ||
' label="pipe";\n', | ||
' pipe_mod1[label="mod1 (engine)", shape=box3d,style=filled, color=black, colorscheme=greys7 fillcolor=2];\n', | ||
' pipe_mod2[label="mod2 (engine)"];\n', | ||
' pipe_mod1 -> pipe_mod2;\n', | ||
'}'] | ||
|
||
dotfile_iter_colored = [ | ||
'digraph pipe{\n', | ||
' label="pipe";\n', | ||
' pipe_mod1[label="mod1 (engine)", shape=box3d,style=filled, color=black, colorscheme=greys7 fillcolor=2];\n', | ||
' pipe_mod2[label="mod2 (engine)", style=filled, fillcolor="#FFFFC8"];\n', | ||
' pipe_mod1 -> pipe_mod2;\n', | ||
'}'] | ||
|
||
@pytest.mark.parametrize("simple", [True, False]) | ||
@pytest.mark.parametrize("graph_type", ['orig', 'flat', 'exec', 'hierarchical', 'colored']) | ||
def test_write_graph_dotfile_iterables(tmpdir, graph_type, simple): | ||
""" checking dot files for a workflow with iterables""" | ||
tmpdir.chdir() | ||
|
||
pipe = pe.Workflow(name='pipe') | ||
mod1 = pe.Node(interface=EngineTestInterface(), name='mod1') | ||
mod1.iterables = ('input1', [1, 2]) | ||
mod2 = pe.Node(interface=EngineTestInterface(), name='mod2') | ||
pipe.connect([(mod1, mod2, [('output1', 'input1')])]) | ||
pipe.write_graph( | ||
graph2use=graph_type, simple_form=simple, format='dot') | ||
|
||
with open("graph.dot") as f: | ||
graph_str = f.read() | ||
|
||
if simple: | ||
for str in eval("dotfile_iter_{}".format(graph_type)): | ||
assert str in graph_str | ||
else: | ||
# if simple=False graph.dot uses longer names | ||
for str in eval("dotfile_iter_{}".format(graph_type)): | ||
if graph_type in ["hierarchical", "colored"]: | ||
assert str.replace("mod1 (engine)", "mod1.EngineTestInterface.engine").replace( | ||
"mod2 (engine)", "mod2.EngineTestInterface.engine") in graph_str | ||
else: | ||
assert str.replace( | ||
"mod1 (engine)", "pipe.mod1.EngineTestInterface.engine").replace( | ||
"mod2 (engine)", "pipe.mod2.EngineTestInterface.engine") in graph_str | ||
|
||
# graph_detailed is not created for hierachical or colored | ||
if graph_type not in ["hierarchical", "colored"]: | ||
with open("graph_detailed.dot") as f: | ||
graph_str = f.read() | ||
for str in eval("dotfile_detailed_iter_{}".format(graph_type)): | ||
assert str in graph_str | ||
|
||
|
||
|
||
def test_io_subclass(): | ||
"""Ensure any io subclass allows dynamic traits""" | ||
from nipype.interfaces.io import IOBase | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -507,7 +507,7 @@ def _write_detailed_dot(graph, dotfilename): | |
# write nodes | ||
edges = [] | ||
for n in nx.topological_sort(graph): | ||
nodename = str(n) | ||
nodename = str(n.itername) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
inports = [] | ||
for u, v, d in graph.in_edges(nbunch=n, data=True): | ||
for cd in d['connect']: | ||
|
@@ -519,8 +519,8 @@ def _write_detailed_dot(graph, dotfilename): | |
ipstrip = 'in%s' % _replacefunk(inport) | ||
opstrip = 'out%s' % _replacefunk(outport) | ||
edges.append( | ||
'%s:%s:e -> %s:%s:w;' % (str(u).replace('.', ''), opstrip, | ||
str(v).replace('.', ''), ipstrip)) | ||
'%s:%s:e -> %s:%s:w;' % (u.itername.replace('.', ''), opstrip, | ||
v.itername.replace('.', ''), ipstrip)) | ||
if inport not in inports: | ||
inports.append(inport) | ||
inputstr = ['{IN'] + [ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this break the property definition below? (line 46)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? Before we had two lines: the first one
self._name = None
and 3 lines laterself.name = name
. I thought that one was left there forgotten.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change looks reasonable to me. There's no instance variable used by
name.setter
that is set between lines 39 and 43, and nothing in these lines usesself._name
in any way, so it's hard to see where this would cause a problem.I'm not sure what @oesteban means by "break[ing] the property definition". Perhaps you could clarify?