From 854afe6432a8a3db3f5bc7979ec2ed5630ee4f28 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 27 Apr 2018 10:48:58 -0400 Subject: [PATCH 1/7] ENH: Add pre- and post-run hooks to BaseInterface --- nipype/interfaces/base/core.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) 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 From 982822cf9004aee4fc2f35dafcb7bfb4c3f8921b Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 27 Apr 2018 11:22:50 -0400 Subject: [PATCH 2/7] ENH: Add ReportCapableInterface mix-in/base class --- nipype/interfaces/mixins/reporting.py | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 nipype/interfaces/mixins/reporting.py diff --git a/nipype/interfaces/mixins/reporting.py b/nipype/interfaces/mixins/reporting.py new file mode 100644 index 0000000000..64d7ce0f73 --- /dev/null +++ b/nipype/interfaces/mixins/reporting.py @@ -0,0 +1,59 @@ +# -*- 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, traits, BaseInterface, BaseInterfaceInputSpec, TraitedSpec) + +iflogger = logging.getLogger('interface') + + +class ReportCapableInputSpec(BaseInterfaceInputSpec): + generate_report = traits.Bool(False, usedefault=True, + desc="Enable report generation") + 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 _post_run_hook(self, runtime): + runtime = super(ReportCapableInterface, self)._post_run_hook(runtime) + + # leave early if there's nothing to do + if not self.inputs.generate_report: + return runtime + + self._out_report = os.path.abspath(self.inputs.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 From 2e7105205365bc404d4335e75aacaa2f717f8fd4 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Fri, 27 Apr 2018 12:28:50 -0400 Subject: [PATCH 3/7] FIX: Add mixins.__init__ --- nipype/interfaces/mixins/__init__.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 nipype/interfaces/mixins/__init__.py diff --git a/nipype/interfaces/mixins/__init__.py b/nipype/interfaces/mixins/__init__.py new file mode 100644 index 0000000000..bd77131ee0 --- /dev/null +++ b/nipype/interfaces/mixins/__init__.py @@ -0,0 +1,2 @@ +from reporting import ( + ReportCapableInterface, ReportCapableInputSpec, ReportCapableOutputSpec) From e6b258f0f92898022191b24fffe42174a1e2efae Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 27 Apr 2018 13:11:34 -0400 Subject: [PATCH 4/7] FIX: Relative import --- nipype/interfaces/mixins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/mixins/__init__.py b/nipype/interfaces/mixins/__init__.py index bd77131ee0..587d3a22a8 100644 --- a/nipype/interfaces/mixins/__init__.py +++ b/nipype/interfaces/mixins/__init__.py @@ -1,2 +1,2 @@ -from reporting import ( +from .reporting import ( ReportCapableInterface, ReportCapableInputSpec, ReportCapableOutputSpec) From 7a678c1ddbd1181b6a83525d32aec2a3e8e85632 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Thu, 3 May 2018 10:40:29 -0700 Subject: [PATCH 5/7] RF: Move generate_report from input spec to instance variable --- nipype/interfaces/mixins/reporting.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nipype/interfaces/mixins/reporting.py b/nipype/interfaces/mixins/reporting.py index 64d7ce0f73..de9f6c2853 100644 --- a/nipype/interfaces/mixins/reporting.py +++ b/nipype/interfaces/mixins/reporting.py @@ -16,8 +16,6 @@ class ReportCapableInputSpec(BaseInterfaceInputSpec): - generate_report = traits.Bool(False, usedefault=True, - desc="Enable report generation") out_report = File('report', usedefault=True, hash_files=False, desc='filename for the visual report') @@ -30,11 +28,15 @@ class ReportCapableInterface(BaseInterface): """Mixin to enable reporting for Nipype interfaces""" _out_report = None + def __init__(self, generate_report=False, **kwargs): + super(ReportCapableInterface, self).__init__(self, **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.inputs.generate_report: + if not self.generate_report: return runtime self._out_report = os.path.abspath(self.inputs.out_report) From 583d0f80f8f3a82d2bb02b174143334fd493fc8c Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Thu, 3 May 2018 11:29:34 -0700 Subject: [PATCH 6/7] FIX: Build absolute path with runtime.cwd --- nipype/interfaces/mixins/reporting.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/mixins/reporting.py b/nipype/interfaces/mixins/reporting.py index de9f6c2853..8efbe224fc 100644 --- a/nipype/interfaces/mixins/reporting.py +++ b/nipype/interfaces/mixins/reporting.py @@ -10,7 +10,7 @@ from ... import logging from ..base import ( - File, traits, BaseInterface, BaseInterfaceInputSpec, TraitedSpec) + File, BaseInterface, BaseInterfaceInputSpec, TraitedSpec) iflogger = logging.getLogger('interface') @@ -39,7 +39,11 @@ def _post_run_hook(self, runtime): if not self.generate_report: return runtime - self._out_report = os.path.abspath(self.inputs.out_report) + 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 From 25c98b754a9b127b04d608c85580d9f6be68657f Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Thu, 17 May 2018 13:02:00 -0400 Subject: [PATCH 7/7] FIX: Do not pass self to super.__init__ --- nipype/interfaces/mixins/reporting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/mixins/reporting.py b/nipype/interfaces/mixins/reporting.py index 8efbe224fc..027928e128 100644 --- a/nipype/interfaces/mixins/reporting.py +++ b/nipype/interfaces/mixins/reporting.py @@ -29,7 +29,7 @@ class ReportCapableInterface(BaseInterface): _out_report = None def __init__(self, generate_report=False, **kwargs): - super(ReportCapableInterface, self).__init__(self, **kwargs) + super(ReportCapableInterface, self).__init__(**kwargs) self.generate_report = generate_report def _post_run_hook(self, runtime):