Skip to content

Commit

Permalink
ENH: Add better error detection for nipype node errors
Browse files Browse the repository at this point in the history
  • Loading branch information
mgxd committed Nov 16, 2023
1 parent 89dd4c0 commit a4922d9
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
2 changes: 1 addition & 1 deletion migas/error/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ def strip_filenames(text: str) -> str:
paths = set(re.findall(r'(?:/[^/]+)[/\w\.-]*', text))
for path in paths:
text = text.replace(path, '<redacted>')
return text
return text
34 changes: 34 additions & 0 deletions migas/error/nipype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import re
import typing as ty
from types import TracebackType

from migas.error import strip_filenames


def node_execution_error(etype: type, evalue: str, etb: TracebackType) -> dict:
strpval = evalue.replace('\n', ' ').replace('\t', ' ').strip()
node, cmd, stdout, stderr, tb = None, None, None, None, None

if m := re.search(r'(?P<node>(?<=Exception raised while executing Node )\w+)', strpval):
node = m.group('node').strip()

if m := re.search(r'(?P<cmdline>(?<=Cmdline:).*(?=Stdout:))', strpval):
cmd = strip_filenames(m.group('cmdline')).strip()

if m := re.search(r'(?P<stdout>(?<=Stdout:).*(?=Stderr:))', strpval):
stdout = strip_filenames(m.group('stdout')).strip()

if m := re.search(r'(?P<stderr>(?<=Stderr:).*(?=Traceback:))', strpval):
stderr = strip_filenames(m.group('stderr')).strip()

if m := re.search(r'(?P<tb>(?<=Traceback:).*)', strpval):
tb = strip_filenames(m.group('tb')).strip()

print(node, cmd, stdout, stderr, tb)

return {
'status': 'F',
'status_desc': f'Exception raised from node {node or "<?>"}',
'error_type': 'NodeExecutionError',
'error_desc': tb or "No traceback available",
}
65 changes: 65 additions & 0 deletions migas/error/tests/test_nipype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import sys

from migas.error.nipype import node_execution_error

ERROR_TEXT = """
nipype.pipeline.engine.nodes.NodeExecutionError: Exception raised while executing Node failingnode.
Cmdline:
mri_convert --out_type mgz --input_volume /tmp/sample/file.txt --output_volume /tmp/wf/conv1/README_out.mgz
Stdout:
mri_convert --out_type mgz --input_volume /tmp/sample/file.txt --output_volume /tmp/wf/conv1/README_out.mgz
ERROR: cannot determine file type for /tmp/sample/file.txt
Stderr:
Traceback:
Traceback (most recent call last):
File "/code/nipype/nipype/interfaces/base/core.py", line 454, in aggregate_outputs
setattr(outputs, key, val)
File "/code/nipype/nipype/interfaces/base/traits_extension.py", line 425, in validate
value = super(MultiObject, self).validate(objekt, name, newvalue)
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/trait_types.py", line 2699, in validate
return TraitListObject(self, object, name, value)
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/trait_list_object.py", line 582, in __init__
super().__init__(
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/trait_list_object.py", line 213, in __init__
super().__init__(self.item_validator(item) for item in iterable)
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/trait_list_object.py", line 213, in <genexpr>
super().__init__(self.item_validator(item) for item in iterable)
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/trait_list_object.py", line 865, in _item_validator
return trait_validator(object, self.name, value)
File "/code/nipype/nipype/interfaces/base/traits_extension.py", line 330, in validate
value = super(File, self).validate(objekt, name, value, return_pathlike=True)
File "/code/nipype/nipype/interfaces/base/traits_extension.py", line 135, in validate
self.error(objekt, name, str(value))
File "/code/.pyenv/versions/nipreps/lib/python3.10/site-packages/traits/base_trait_handler.py", line 74, in error
raise TraitError(
traits.trait_errors.TraitError: Each element of the 'out_file' trait of a MRIConvertOutputSpec instance must be a pathlike object or string representing an existing file, but a value of '/tmp/wf/conv1/README_out.mgz' <class 'str'> was specified.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/code/nipype/nipype/interfaces/base/core.py", line 401, in run
outputs = self.aggregate_outputs(runtime)
File "/code/nipype/nipype/interfaces/base/core.py", line 461, in aggregate_outputs
raise FileNotFoundError(msg)
FileNotFoundError: No such file or directory '/tmp/wf/conv1/README_out.mgz' for output 'out_file' of a MRIConvert interface
"""


class NodeExecutionError(Exception):
...


TB = None
try:
1 + 'a'
except Exception:
_, _, TB = sys.exc_info()


def test_node_execution_error():
kwargs = node_execution_error(NodeExecutionError, ERROR_TEXT, TB)
assert kwargs['status'] == 'F'
assert kwargs['error_type'] == 'NodeExecutionError'
assert 'FileNotFoundError' in kwargs['error_desc']

0 comments on commit a4922d9

Please sign in to comment.