Skip to content

Commit fd39c1f

Browse files
authored
Merge pull request #3505 from mgxd/fix/node-execution-info
FIX: Provide more runtime information when node execution fails
2 parents af3f8df + f4a7792 commit fd39c1f

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

nipype/pipeline/engine/nodes.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,9 +750,25 @@ def _run_command(self, execute, copyfiles=True):
750750
)
751751

752752
if exc_tb:
753-
raise NodeExecutionError(
754-
f"Exception raised while executing Node {self.name}.\n\n{result.runtime.traceback}"
755-
)
753+
runtime = result.runtime
754+
755+
def _tab(text):
756+
from textwrap import indent
757+
758+
if not text:
759+
return ""
760+
return indent(text, '\t')
761+
762+
msg = f"Exception raised while executing Node {self.name}.\n\n"
763+
if hasattr(runtime, 'cmdline'):
764+
msg += (
765+
f"Cmdline:\n{_tab(runtime.cmdline)}\n"
766+
f"Stdout:\n{_tab(runtime.stdout)}\n"
767+
f"Stderr:\n{_tab(runtime.stderr)}\n"
768+
)
769+
# Always pass along the traceback
770+
msg += f"Traceback:\n{_tab(runtime.traceback)}"
771+
raise NodeExecutionError(msg)
756772

757773
return result
758774

nipype/pipeline/engine/tests/test_nodes.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .... import config
99
from ....interfaces import utility as niu
10+
from ....interfaces import base as nib
1011
from ... import engine as pe
1112
from ..utils import merge_dict
1213
from .test_base import EngineTestInterface
@@ -334,3 +335,45 @@ def _producer(num=1, deadly_num=7):
334335
wf.base_dir = os.path.abspath("./test_output")
335336
with pytest.raises(RuntimeError):
336337
wf.run(plugin="MultiProc")
338+
339+
340+
class FailCommandLine(nib.CommandLine):
341+
input_spec = nib.CommandLineInputSpec
342+
output_spec = nib.TraitedSpec
343+
_cmd = 'nipype-node-execution-fail'
344+
345+
346+
def test_NodeExecutionError(tmp_path, monkeypatch):
347+
import stat
348+
349+
monkeypatch.chdir(tmp_path)
350+
351+
# create basic executable and add to PATH
352+
exebin = tmp_path / 'bin'
353+
exebin.mkdir()
354+
exe = exebin / 'nipype-node-execution-fail'
355+
exe.write_text('#!/bin/bash\necho "Running"\necho "This should fail" >&2\nexit 1')
356+
exe.chmod(exe.stat().st_mode | stat.S_IEXEC)
357+
monkeypatch.setenv("PATH", str(exe.parent.absolute()), prepend=os.pathsep)
358+
359+
# Test with cmdline interface
360+
cmd = pe.Node(FailCommandLine(), name="cmd-fail", base_dir='cmd')
361+
with pytest.raises(pe.nodes.NodeExecutionError) as exc:
362+
cmd.run()
363+
error_msg = str(exc.value)
364+
365+
for attr in ("Cmdline:", "Stdout:", "Stderr:", "Traceback:"):
366+
assert attr in error_msg
367+
assert "This should fail" in error_msg
368+
369+
# Test with function interface
370+
def fail():
371+
raise Exception("Functions can fail too")
372+
373+
func = pe.Node(niu.Function(function=fail), name='func-fail', base_dir='func')
374+
with pytest.raises(pe.nodes.NodeExecutionError) as exc:
375+
func.run()
376+
error_msg = str(exc.value)
377+
assert "Traceback:" in error_msg
378+
assert "Cmdline:" not in error_msg
379+
assert "Functions can fail too" in error_msg

0 commit comments

Comments
 (0)