diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 5fcf566804..e5c7fb2ca6 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -517,7 +517,9 @@ def run(self, cwd=None, ignore_exception=None, **inputs): outputs = None try: + runtime = self._pre_run_hook(runtime) runtime = self._run_interface(runtime) + runtime = self._post_run_hook(runtime) outputs = self.aggregate_outputs(runtime) except Exception as e: import traceback @@ -653,6 +655,28 @@ def save_inputs_to_json(self, json_file): with open(json_file, 'w' if PY3 else 'wb') as fhandle: json.dump(inputs, fhandle, indent=4, ensure_ascii=False) + def _pre_run_hook(self, runtime): + """ + Perform any pre-_run_interface() processing + + Subclasses may override this function to modify ``runtime`` object or + interface state + + MUST return runtime object + """ + return runtime + + def _post_run_hook(self, runtime): + """ + Perform any post-_run_interface() processing + + Subclasses may override this function to modify ``runtime`` object or + interface state + + MUST return runtime object + """ + return runtime + class SimpleInterface(BaseInterface): """ An interface pattern that allows outputs to be set in a dictionary diff --git a/nipype/interfaces/mixins/__init__.py b/nipype/interfaces/mixins/__init__.py new file mode 100644 index 0000000000..587d3a22a8 --- /dev/null +++ b/nipype/interfaces/mixins/__init__.py @@ -0,0 +1,2 @@ +from .reporting import ( + ReportCapableInterface, ReportCapableInputSpec, ReportCapableOutputSpec) diff --git a/nipype/interfaces/mixins/reporting.py b/nipype/interfaces/mixins/reporting.py new file mode 100644 index 0000000000..027928e128 --- /dev/null +++ b/nipype/interfaces/mixins/reporting.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +""" class mixin and utilities for enabling reports for nipype interfaces """ +from __future__ import (print_function, division, unicode_literals, + absolute_import) + +import os +from abc import abstractmethod + +from ... import logging +from ..base import ( + File, BaseInterface, BaseInterfaceInputSpec, TraitedSpec) + +iflogger = logging.getLogger('interface') + + +class ReportCapableInputSpec(BaseInterfaceInputSpec): + out_report = File('report', usedefault=True, hash_files=False, + desc='filename for the visual report') + + +class ReportCapableOutputSpec(TraitedSpec): + out_report = File(desc='filename for the visual report') + + +class ReportCapableInterface(BaseInterface): + """Mixin to enable reporting for Nipype interfaces""" + _out_report = None + + def __init__(self, generate_report=False, **kwargs): + super(ReportCapableInterface, self).__init__(**kwargs) + self.generate_report = generate_report + + def _post_run_hook(self, runtime): + runtime = super(ReportCapableInterface, self)._post_run_hook(runtime) + + # leave early if there's nothing to do + if not self.generate_report: + return runtime + + self._out_report = self.inputs.out_report + if not os.path.isabs(self._out_report): + self._out_report = os.path.abspath(os.path.join(runtime.cwd, + self._out_report)) + + self._generate_report() + + return runtime + + def _list_outputs(self): + try: + outputs = super(ReportCapableInterface, self)._list_outputs() + except NotImplementedError: + outputs = {} + if self._out_report is not None: + outputs['out_report'] = self._out_report + return outputs + + @abstractmethod + def _generate_report(self): + """ + Saves report to file identified by _out_report instance variable + """ + raise NotImplementedError