diff --git a/CHANGES b/CHANGES index fe8abb464a..a75fe3d7bf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Next release ============ +* ENH: The name_source feature now available for all Interfaces derived from BaseInterface + (https://github.com/nipy/nipype/pull/1240) * ENH: New interfaces in dipy: RESTORE, EstimateResponseSH, CSD and StreamlineTractography (https://github.com/nipy/nipype/pull/1090) * ENH: Added interfaces of AFNI (https://github.com/nipy/nipype/pull/1360, diff --git a/nipype/algorithms/icc.py b/nipype/algorithms/icc.py index af61c260b9..5c1faff0b7 100644 --- a/nipype/algorithms/icc.py +++ b/nipype/algorithms/icc.py @@ -1,34 +1,52 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +""" +Algorithms to compute the Interclass Correlation Coefficient + + Change directory to provide relative paths for doctests + >>> import os + >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) + >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data')) + >>> os.chdir(datadir) + +""" + from __future__ import division -from builtins import range +import os +import numpy as np from numpy import ones, kron, mean, eye, hstack, dot, tile from scipy.linalg import pinv -from ..interfaces.base import BaseInterfaceInputSpec, TraitedSpec, \ - BaseInterface, traits, File import nibabel as nb -import numpy as np -import os +from builtins import range +from ..interfaces.base import (traits, File, BaseInterface, BaseInterfaceInputSpec, + TraitedSpec) class ICCInputSpec(BaseInterfaceInputSpec): subjects_sessions = traits.List(traits.List(File(exists=True)), desc="n subjects m sessions 3D stat files", mandatory=True) mask = File(exists=True, mandatory=True) + icc_map = File('icc_map.nii', desc='name of output ICC map') + session_var_map = File('session_var_map.nii', desc="variance between sessions") + session_F_map = File('session_F_map.nii', desc="F map of sessions") + subject_var_map = File('subject_var_map.nii', desc="variance between subjects") class ICCOutputSpec(TraitedSpec): icc_map = File(exists=True) session_var_map = File(exists=True, desc="variance between sessions") + session_F_map = File(exists=True, desc="variance between sessions") subject_var_map = File(exists=True, desc="variance between subjects") class ICC(BaseInterface): - ''' + """ Calculates Interclass Correlation Coefficient (3,1) as defined in P. E. Shrout & Joseph L. Fleiss (1979). "Intraclass Correlations: Uses in Assessing Rater Reliability". Psychological Bulletin 86 (2): 420-428. This particular implementation is aimed at relaibility (test-retest) studies. - ''' + """ input_spec = ICCInputSpec output_spec = ICCOutputSpec @@ -44,48 +62,46 @@ def _run_interface(self, runtime): session_var = np.zeros(session_datas[0][0].shape) subject_var = np.zeros(session_datas[0][0].shape) - for x in range(icc.shape[0]): - Y = all_data[x, :, :] - icc[x], subject_var[x], session_var[x], session_F[x], _, _ = ICC_rep_anova(Y) + for i in range(icc.shape[0]): + data = all_data[i, :, :] + icc[i], subject_var[i], session_var[i], session_F[i], _, _ = ICC_rep_anova(data) nim = nb.load(self.inputs.subjects_sessions[0][0]) new_data = np.zeros(nim.shape) new_data[maskdata] = icc.reshape(-1,) new_img = nb.Nifti1Image(new_data, nim.affine, nim.header) - nb.save(new_img, 'icc_map.nii') + nb.save(new_img, self.inputs.icc_map) new_data = np.zeros(nim.shape) new_data[maskdata] = session_var.reshape(-1,) new_img = nb.Nifti1Image(new_data, nim.affine, nim.header) - nb.save(new_img, 'session_var_map.nii') + nb.save(new_img, self.inputs.session_var_map) new_data = np.zeros(nim.shape) new_data[maskdata] = subject_var.reshape(-1,) new_img = nb.Nifti1Image(new_data, nim.affine, nim.header) - nb.save(new_img, 'subject_var_map.nii') + nb.save(new_img, self.inputs.subject_var_map.nii) + new_data = np.zeros(nim.shape) + new_data[maskdata] = session_F.reshape(-1,) + new_img = nb.Nifti1Image(new_data, nim.affine, nim.header) + nb.save(new_img, self.inputs.session_F_map) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['icc_map'] = os.path.abspath('icc_map.nii') - outputs['sessions_F_map'] = os.path.abspath('sessions_F_map.nii') - outputs['session_var_map'] = os.path.abspath('session_var_map.nii') - outputs['subject_var_map'] = os.path.abspath('subject_var_map.nii') - return outputs - -def ICC_rep_anova(Y): - ''' - the data Y are entered as a 'table' ie subjects are in rows and repeated +def ICC_rep_anova(data): + """ + the data (Y) are entered as a 'table' ie subjects are in rows and repeated measures in columns One Sample Repeated measure ANOVA - Y = XB + E with X = [FaTor / Subjects] - ''' + .. math:: + + Y = XB + E with X = [FaTor / Subjects] + """ - [nb_subjects, nb_conditions] = Y.shape + [nb_subjects, nb_conditions] = data.shape dfc = nb_conditions - 1 dfe = (nb_subjects - 1) * dfc dfr = nb_subjects - 1 @@ -94,8 +110,8 @@ def ICC_rep_anova(Y): # ------------------------------------ # Sum Square Total - mean_Y = mean(Y) - SST = ((Y - mean_Y) ** 2).sum() + mean_Y = mean(data) + SST = ((data - mean_Y) ** 2).sum() # create the design matrix for the different levels x = kron(eye(nb_conditions), ones((nb_subjects, 1))) # sessions @@ -103,16 +119,16 @@ def ICC_rep_anova(Y): X = hstack([x, x0]) # Sum Square Error - predicted_Y = dot(dot(dot(X, pinv(dot(X.T, X))), X.T), Y.flatten('F')) - residuals = Y.flatten('F') - predicted_Y + predicted_Y = dot(dot(dot(X, pinv(dot(X.T, X))), X.T), data.flatten('F')) + residuals = data.flatten('F') - predicted_Y SSE = (residuals ** 2).sum() - residuals.shape = Y.shape + residuals.shape = data.shape MSE = SSE / dfe # Sum square session effect - between colums/sessions - SSC = ((mean(Y, 0) - mean_Y) ** 2).sum() * nb_subjects + SSC = ((mean(data, 0) - mean_Y) ** 2).sum() * nb_subjects MSC = SSC / dfc / nb_subjects session_effect_F = MSC / MSE diff --git a/nipype/algorithms/mesh.py b/nipype/algorithms/mesh.py index fa63c69c9d..72fe267481 100644 --- a/nipype/algorithms/mesh.py +++ b/nipype/algorithms/mesh.py @@ -1,6 +1,6 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -''' +""" Miscellaneous algorithms for 2D contours and 3D triangularized meshes handling Change directory to provide relative paths for doctests @@ -9,21 +9,19 @@ >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data')) >>> os.chdir(datadir) -''' +""" from __future__ import division from builtins import zip import os.path as op -from warnings import warn - import numpy as np from numpy import linalg as nla from .. import logging from ..external.six import string_types -from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File, - BaseInterfaceInputSpec) -iflogger = logging.getLogger('interface') +from ..interfaces.base import traits, File, GenFile, BaseInterface, BaseInterfaceInputSpec, TraitedSpec + +IFLOGGER = logging.getLogger('interface') class TVTKBaseInterface(BaseInterface): @@ -35,7 +33,7 @@ def __init__(self, **inputs): from tvtk.tvtk_classes.vtk_version import vtk_build_version self._vtk_major = int(vtk_build_version[0]) except ImportError: - iflogger.warning('VTK version-major inspection using tvtk failed.') + IFLOGGER.warning('VTK version-major inspection using tvtk failed.') super(TVTKBaseInterface, self).__init__(**inputs) @@ -47,9 +45,8 @@ class WarpPointsInputSpec(BaseInterfaceInputSpec): desc=('dense deformation field to be applied')) interp = traits.Enum('cubic', 'nearest', 'linear', usedefault=True, mandatory=True, desc='interpolation') - out_points = File(name_source='points', name_template='%s_warped', - output_name='out_points', keep_extension=True, - desc='the warped point set') + out_points = GenFile(template='{points}_warped', keep_extension=True, + desc='the warped point set') class WarpPointsOutputSpec(TraitedSpec): @@ -77,22 +74,6 @@ class WarpPoints(TVTKBaseInterface): input_spec = WarpPointsInputSpec output_spec = WarpPointsOutputSpec - def _gen_fname(self, in_file, suffix='generated', ext=None): - import os.path as op - - fname, fext = op.splitext(op.basename(in_file)) - - if fext == '.gz': - fname, fext2 = op.splitext(fname) - fext = fext2 + fext - - if ext is None: - ext = fext - - if ext[0] == '.': - ext = ext[1:] - return op.abspath('%s_%s.%s' % (fname, suffix, ext)) - def _run_interface(self, runtime): import nibabel as nb import numpy as np @@ -137,19 +118,10 @@ def _run_interface(self, runtime): else: w.set_input_data_object(mesh) - w.file_name = self._gen_fname(self.inputs.points, - suffix='warped', - ext='.vtk') + w.file_name = self.inputs.out_points w.write() return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_points'] = self._gen_fname(self.inputs.points, - suffix='warped', - ext='.vtk') - return outputs - class ComputeMeshWarpInputSpec(BaseInterfaceInputSpec): surface1 = File(exists=True, mandatory=True, @@ -277,15 +249,12 @@ def _run_interface(self, runtime): writer.write() - self._distance = np.average(errvector, weights=weights) - return runtime + _distance = np.average(errvector, weights=weights) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - outputs['out_warp'] = op.abspath(self.inputs.out_warp) - outputs['distance'] = self._distance - return outputs + self.outputs.out_file = op.abspath(self.inputs.out_file) + self.outputs.out_warp = op.abspath(self.inputs.out_warp) + self.outputs.distance = _distance + return runtime class MeshWarpMathsInputSpec(BaseInterfaceInputSpec): @@ -302,18 +271,16 @@ class MeshWarpMathsInputSpec(BaseInterfaceInputSpec): operation = traits.Enum('sum', 'sub', 'mul', 'div', usedefault=True, desc=('operation to be performed')) - out_warp = File('warp_maths.vtk', usedefault=True, - desc='vtk file based on in_surf and warpings mapping it ' - 'to out_file') - out_file = File('warped_surf.vtk', usedefault=True, + out_warp = GenFile(template='{in_surf}_warping', keep_extension=True, + desc='vtk file based on in_surf and warpings mapping it to out_file') + out_file = File(template='{in_surf}_warped', keep_extension=True, desc='vtk with surface warped') class MeshWarpMathsOutputSpec(TraitedSpec): out_warp = File(exists=True, desc=('vtk file with the vertex-wise ' 'mapping of surface1 to surface2')) - out_file = File(exists=True, - desc='vtk with surface warped') + out_file = File(exists=True, desc='vtk with surface warped') class MeshWarpMaths(TVTKBaseInterface): @@ -416,12 +383,6 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - outputs['out_warp'] = op.abspath(self.inputs.out_warp) - return outputs - class P2PDistance(ComputeMeshWarp): @@ -437,6 +398,5 @@ class P2PDistance(ComputeMeshWarp): def __init__(self, **inputs): super(P2PDistance, self).__init__(**inputs) - warn(('This interface has been deprecated since 1.0, please use ' - 'ComputeMeshWarp'), - DeprecationWarning) + IFLOGGER.warn('This interface has been deprecated since 1.0, please use ' + 'ComputeMeshWarp') diff --git a/nipype/algorithms/metrics.py b/nipype/algorithms/metrics.py index 25ace5f012..d2365ce92b 100644 --- a/nipype/algorithms/metrics.py +++ b/nipype/algorithms/metrics.py @@ -1,6 +1,6 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -''' +""" Image assessment algorithms. Typical overlap and error computation measures to evaluate results from other processing units. @@ -10,7 +10,7 @@ >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data')) >>> os.chdir(datadir) -''' +""" from __future__ import division from builtins import zip from builtins import range @@ -27,9 +27,9 @@ from .. import logging from ..utils.misc import package_check -from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File, - InputMultiPath, - BaseInterfaceInputSpec, isdefined) +from ..interfaces.base import (traits, File, GenFile, isdefined, BaseInterfaceInputSpec, + TraitedSpec, InputMultiPath, BaseInterface) + iflogger = logging.getLogger('interface') @@ -186,31 +186,27 @@ def _run_interface(self, runtime): nii2 = nb.load(self.inputs.volume2, mmap=False) if self.inputs.method == "eucl_min": - self._distance, self._point1, self._point2 = self._eucl_min( + _distance, _point1, _point2 = self._eucl_min( nii1, nii2) elif self.inputs.method == "eucl_cog": - self._distance = self._eucl_cog(nii1, nii2) + _distance = self._eucl_cog(nii1, nii2) elif self.inputs.method == "eucl_mean": - self._distance = self._eucl_mean(nii1, nii2) + _distance = self._eucl_mean(nii1, nii2) elif self.inputs.method == "eucl_wmean": - self._distance = self._eucl_mean(nii1, nii2, weighted=True) + _distance = self._eucl_mean(nii1, nii2, weighted=True) elif self.inputs.method == "eucl_max": - self._distance = self._eucl_max(nii1, nii2) - - return runtime + _distance = self._eucl_max(nii1, nii2) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['distance'] = self._distance + self.outputs.distance = _distance if self.inputs.method == "eucl_min": - outputs['point1'] = self._point1 - outputs['point2'] = self._point2 + self.outputs.point1 = _point1 + self.outputs.point2 = _point2 elif self.inputs.method in ["eucl_mean", "eucl_wmean"]: - outputs['histogram'] = os.path.abspath(self._hist_filename) - return outputs + self.outputs.histogram = os.path.abspath(self._hist_filename) + return runtime class OverlapInputSpec(BaseInterfaceInputSpec): @@ -317,9 +313,9 @@ def _run_interface(self, runtime): volumes1.append(scale * len(data1[data1 == l])) volumes2.append(scale * len(data2[data2 == l])) - results = dict(jaccard=[], dice=[]) - results['jaccard'] = np.array(res) - results['dice'] = 2.0 * results['jaccard'] / (results['jaccard'] + 1.0) + _ove_rois = dict(jaccard=[], dice=[]) + _ove_rois['jaccard'] = np.array(res) + _ove_rois['dice'] = 2.0 * _ove_rois['jaccard'] / (_ove_rois['jaccard'] + 1.0) weights = np.ones((len(volumes1),), dtype=np.float32) if self.inputs.weighting != 'none': @@ -334,30 +330,22 @@ def _run_interface(self, runtime): nb.save(nb.Nifti1Image(both_data, nii1.affine, nii1.header), self.inputs.out_file) - self._labels = labels - self._ove_rois = results - self._vol_rois = (np.array(volumes1) - + _vol_rois = (np.array(volumes1) - np.array(volumes2)) / np.array(volumes1) - self._dice = round(np.sum(weights * results['dice']), 5) - self._jaccard = round(np.sum(weights * results['jaccard']), 5) - self._volume = np.sum(weights * self._vol_rois) - + _dice = round(np.sum(weights * _ove_rois['dice']), 5) + _jaccard = round(np.sum(weights * _ove_rois['jaccard']), 5) + _volume = np.sum(weights * _vol_rois) + self.outputs.labels = labels + self.outputs.jaccard = _jaccard + self.outputs.dice = _dice + self.outputs.volume_difference = _volume + self.outputs.roi_ji = _ove_rois['jaccard'].tolist() + self.outputs.roi_di = _ove_rois['dice'].tolist() + self.outputs.roi_voldiff = _vol_rois.tolist() + self.outputs.diff_file = os.path.abspath(self.inputs.out_file) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['labels'] = self._labels - outputs['jaccard'] = self._jaccard - outputs['dice'] = self._dice - outputs['volume_difference'] = self._volume - - outputs['roi_ji'] = self._ove_rois['jaccard'].tolist() - outputs['roi_di'] = self._ove_rois['dice'].tolist() - outputs['roi_voldiff'] = self._vol_rois.tolist() - outputs['diff_file'] = os.path.abspath(self.inputs.out_file) - return outputs - class FuzzyOverlapInputSpec(BaseInterfaceInputSpec): in_ref = InputMultiPath(File(exists=True), mandatory=True, @@ -422,7 +410,7 @@ def _run_interface(self, runtime): # img_ref[:][msk>0] = img_ref[:][msk>0] / (np.sum( img_ref, axis=0 ))[msk>0] # img_tst[tst_msk>0] = img_tst[tst_msk>0] / np.sum( img_tst, axis=0 )[tst_msk>0] - self._jaccards = [] + _jaccards = [] volumes = [] diff_im = np.zeros(img_ref.shape) @@ -431,11 +419,11 @@ def _run_interface(self, runtime): num = np.minimum(ref_comp, tst_comp) ddr = np.maximum(ref_comp, tst_comp) diff_comp[ddr > 0] += 1.0 - (num[ddr > 0] / ddr[ddr > 0]) - self._jaccards.append(np.sum(num) / np.sum(ddr)) + _jaccards.append(np.sum(num) / np.sum(ddr)) volumes.append(np.sum(ref_comp)) - self._dices = 2.0 * (np.array(self._jaccards) / - (np.array(self._jaccards) + 1.0)) + _dices = 2.0 * (np.array(_jaccards) / + (np.array(_jaccards) + 1.0)) if self.inputs.weighting != "none": weights = 1.0 / np.array(volumes) @@ -444,8 +432,8 @@ def _run_interface(self, runtime): weights = weights / np.sum(weights) - setattr(self, '_jaccard', np.sum(weights * self._jaccards)) - setattr(self, '_dice', np.sum(weights * self._dices)) + setattr(self, '_jaccard', np.sum(weights * _jaccards)) + setattr(self, '_dice', np.sum(weights * _dices)) diff = np.zeros(diff_im[0].shape) @@ -457,17 +445,13 @@ def _run_interface(self, runtime): nb.load(self.inputs.in_ref[0]).header), self.inputs.out_file) - return runtime - - def _list_outputs(self): - outputs = self._outputs().get() for method in ("dice", "jaccard"): - outputs[method] = getattr(self, '_' + method) - # outputs['volume_difference'] = self._volume - outputs['diff_file'] = os.path.abspath(self.inputs.out_file) - outputs['class_fji'] = np.array(self._jaccards).astype(float).tolist() - outputs['class_fdi'] = self._dices.astype(float).tolist() - return outputs + setattr(self.outputs, method, getattr(self, '_' + method)) + # self.outputs.volume_difference = _volume + self.outputs.diff_file = os.path.abspath(self.inputs.out_file) + self.outputs.class_fji = np.array(_jaccards).astype(float).tolist() + self.outputs.class_fdi = _dices.astype(float).tolist() + return runtime class ErrorMapInputSpec(BaseInterfaceInputSpec): @@ -479,7 +463,8 @@ class ErrorMapInputSpec(BaseInterfaceInputSpec): metric = traits.Enum("sqeuclidean", "euclidean", desc='error map metric (as implemented in scipy cdist)', usedefault=True, mandatory=True) - out_map = File(desc="Name for the output file") + out_map = GenFile(template='{in_tst}_errormap', keep_extension=True, + desc="Name for the output file") class ErrorMapOutputSpec(TraitedSpec): @@ -547,7 +532,7 @@ def _run_interface(self, runtime): errvectorexp[msk_idxs] = errvector # Get averaged error - self._distance = np.average(errvector) # Only average the masked voxels + _distance = np.average(errvector) # Only average the masked voxels errmap = errvectorexp.reshape(mapshape) @@ -555,27 +540,11 @@ def _run_interface(self, runtime): hdr.set_data_dtype(np.float32) hdr['data_type'] = 16 hdr.set_data_shape(mapshape) - - if not isdefined(self.inputs.out_map): - fname, ext = op.splitext(op.basename(self.inputs.in_tst)) - if ext == '.gz': - fname, ext2 = op.splitext(fname) - ext = ext2 + ext - self._out_file = op.abspath(fname + "_errmap" + ext) - else: - self._out_file = self.inputs.out_map - nb.Nifti1Image(errmap.astype(np.float32), nii_ref.affine, - hdr).to_filename(self._out_file) - + hdr).to_filename(self.inputs.out_map) + self.outputs.distance = _distance return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_map'] = self._out_file - outputs['distance'] = self._distance - return outputs - class SimilarityInputSpec(BaseInterfaceInputSpec): volume1 = File(exists=True, desc="3D/4D volume", mandatory=True) @@ -662,7 +631,7 @@ def _run_interface(self, runtime): else: mask2 = None - self._similarity = [] + _similarity = [] for ts1, ts2 in zip(vols1, vols2): histreg = HistogramRegistration(from_img=ts1, @@ -670,11 +639,8 @@ def _run_interface(self, runtime): similarity=self.inputs.metric, from_mask=mask1, to_mask=mask2) - self._similarity.append(histreg.eval(Affine())) + _similarity.append(histreg.eval(Affine())) + self.outputs.similarity = _similarity return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['similarity'] = self._similarity - return outputs diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 1aea8cdbae..a36ce10592 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -1,6 +1,6 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -''' +""" Miscellaneous algorithms Change directory to provide relative paths for doctests @@ -9,7 +9,7 @@ >>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data')) >>> os.chdir(datadir) -''' +""" from __future__ import print_function from __future__ import absolute_import from __future__ import division @@ -28,17 +28,15 @@ import itertools import scipy.stats as stats -from nipype import logging +from . import metrics as nam +from ..utils.filemanip import fname_presuffix, split_filename +from ..interfaces.base import (traits, File, GenFile, GenMultiFile, isdefined, Undefined, + BaseInterfaceInputSpec, TraitedSpec, InputMultiPath, + OutputMultiPath, DynamicTraitedSpec, BaseInterface) -import warnings -from . import metrics as nam -from ..interfaces.base import (BaseInterface, traits, TraitedSpec, File, - InputMultiPath, OutputMultiPath, - BaseInterfaceInputSpec, isdefined, - DynamicTraitedSpec, Undefined) -from nipype.utils.filemanip import fname_presuffix, split_filename -iflogger = logging.getLogger('interface') +from .. import logging +IFLOGGER = logging.getLogger('interface') class PickAtlasInputSpec(BaseInterfaceInputSpec): @@ -48,19 +46,18 @@ class PickAtlasInputSpec(BaseInterfaceInputSpec): traits.Int, traits.List(traits.Int), desc=("Labels of regions that will be included in the mask. Must be\ compatible with the atlas used."), - mandatory=True - ) + mandatory=True) hemi = traits.Enum( 'both', 'left', 'right', desc="Restrict the mask to only one hemisphere: left or right", - usedefault=True - ) + usedefault=True) dilation_size = traits.Int( usedefault=True, - desc="Defines how much the mask will be dilated (expanded in 3D)." - ) - output_file = File(desc="Where to store the output mask.") - + desc="Defines how much the mask will be dilated (expanded in 3D).") + output_file = File(deprecated=True, new_name='mask_file', + desc="Where to store the output mask.") + mask_file = GenFile(ns='atlas', template='%s_mask', + desc="Where to store the output mask.") class PickAtlasOutputSpec(TraitedSpec): mask_file = File(exists=True, desc="output mask file") @@ -76,8 +73,7 @@ class PickAtlas(BaseInterface): def _run_interface(self, runtime): nim = self._get_brodmann_area() - nb.save(nim, self._gen_output_filename()) - + nb.save(nim, self.inputs.out_mask) return runtime def _gen_output_filename(self): @@ -113,25 +109,18 @@ def _get_brodmann_area(self): return nb.Nifti1Image(newdata, nii.affine, nii.header) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['mask_file'] = self._gen_output_filename() - return outputs - class SimpleThresholdInputSpec(BaseInterfaceInputSpec): - volumes = InputMultiPath( - File(exists=True), desc='volumes to be thresholded', mandatory=True) - threshold = traits.Float( - desc='volumes to be thresholdedeverything below this value will be set\ - to zero', - mandatory=True - ) + volumes = InputMultiPath(File(exists=True), mandatory=True, + desc='volumes to be thresholded') + threshold = traits.Float(mandatory=True, desc='volumes to be thresholdedeverything below ' + 'this value will be set to zero') + thresholded_volumes = GenMultiFile(template='{volumes}_thresholded', keep_extension=True, + desc="thresholded volumes") class SimpleThresholdOutputSpec(TraitedSpec): - thresholded_volumes = OutputMultiPath( - File(exists=True), desc="thresholded volumes") + thresholded_volumes = OutputMultiPath(File(exists=True), desc="thresholded volumes") class SimpleThreshold(BaseInterface): @@ -141,7 +130,7 @@ class SimpleThreshold(BaseInterface): output_spec = SimpleThresholdOutputSpec def _run_interface(self, runtime): - for fname in self.inputs.volumes: + for fname, out_name in zip(self.inputs.volumes, self.inputs.thresholded_volumes): img = nb.load(fname) data = np.array(img.get_data()) @@ -151,38 +140,24 @@ def _run_interface(self, runtime): thresholded_map[active_map] = data[active_map] new_img = nb.Nifti1Image(thresholded_map, img.affine, img.header) - _, base, _ = split_filename(fname) - nb.save(new_img, base + '_thresholded.nii') + nb.save(new_img, out_name) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["thresholded_volumes"] = [] - for fname in self.inputs.volumes: - _, base, _ = split_filename(fname) - outputs["thresholded_volumes"].append( - os.path.abspath(base + '_thresholded.nii')) - return outputs - class ModifyAffineInputSpec(BaseInterfaceInputSpec): volumes = InputMultiPath( - File(exists=True), - desc='volumes which affine matrices will be modified', - mandatory=True - ) + File(exists=True), mandatory=True, + desc='volumes which affine matrices will be modified') transformation_matrix = traits.Array( - value=np.eye(4), - shape=(4, 4), - desc="transformation matrix that will be left multiplied by the\ - affine matrix", - usedefault=True - ) + value=np.eye(4), shape=(4, 4), usedefault=True, + desc='transformation matrix that will be left multiplied by the affine matrix') + transformed_volumes = GenMultiFile(template='{volumes}_transformed', keep_extension=True, + desc='output transformed files') class ModifyAffineOutputSpec(TraitedSpec): - transformed_volumes = OutputMultiPath(File(exist=True)) + transformed_volumes = OutputMultiPath(File(exist=True), desc='output transformed files') class ModifyAffine(BaseInterface): @@ -192,40 +167,26 @@ class ModifyAffine(BaseInterface): input_spec = ModifyAffineInputSpec output_spec = ModifyAffineOutputSpec - def _gen_output_filename(self, name): - _, base, _ = split_filename(name) - return os.path.abspath(base + "_transformed.nii") - def _run_interface(self, runtime): - for fname in self.inputs.volumes: + for fname, out_name in zip(self.inputs.volumes, self.inputs.transformed_volumes): img = nb.load(fname) - affine = img.affine affine = np.dot(self.inputs.transformation_matrix, affine) - - nb.save(nb.Nifti1Image(img.get_data(), affine, img.header), - self._gen_output_filename(fname)) - + nb.save(nb.Nifti1Image(img.get_data(), affine, img.header), out_name) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['transformed_volumes'] = [] - for fname in self.inputs.volumes: - outputs['transformed_volumes'].append( - self._gen_output_filename(fname)) - return outputs - class CreateNiftiInputSpec(BaseInterfaceInputSpec): data_file = File(exists=True, mandatory=True, desc="ANALYZE img file") header_file = File( exists=True, mandatory=True, desc="corresponding ANALYZE hdr file") affine = traits.Array(desc="affine transformation array") + nifti_file = GenFile(ns='data_file', template='%s_nifti.nii', + keep_extension=False, desc='output nifti file') class CreateNiftiOutputSpec(TraitedSpec): - nifti_file = File(exists=True) + nifti_file = File(exists=True, desc='output nifti file') class CreateNifti(BaseInterface): @@ -234,10 +195,6 @@ class CreateNifti(BaseInterface): input_spec = CreateNiftiInputSpec output_spec = CreateNiftiOutputSpec - def _gen_output_file_name(self): - _, base, _ = split_filename(self.inputs.data_file) - return os.path.abspath(base + ".nii") - def _run_interface(self, runtime): hdr = nb.AnalyzeHeader.from_fileobj( open(self.inputs.header_file, 'rb')) @@ -249,28 +206,23 @@ def _run_interface(self, runtime): data = hdr.data_from_fileobj(open(self.inputs.data_file, 'rb')) img = nb.Nifti1Image(data, affine, hdr) - nb.save(img, self._gen_output_file_name()) - + nb.save(img, self.inputs.nifti_file) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['nifti_file'] = self._gen_output_file_name() - return outputs - class TSNRInputSpec(BaseInterfaceInputSpec): in_file = InputMultiPath(File(exists=True), mandatory=True, desc='realigned 4D file or a list of 3D files') regress_poly = traits.Range(low=1, desc='Remove polynomials') - tsnr_file = File('tsnr.nii.gz', usedefault=True, hash_files=False, - desc='output tSNR file') - mean_file = File('mean.nii.gz', usedefault=True, hash_files=False, - desc='output mean file') - stddev_file = File('stdev.nii.gz', usedefault=True, hash_files=False, - desc='output tSNR file') - detrended_file = File('detrend.nii.gz', usedefault=True, hash_files=False, - desc='input file after detrending') + tsnr_file = GenFile(ns='in_file', template='%s_tsnr', + hash_files=False, desc='output tSNR file') + mean_file = GenFile(ns='in_file', template='%s_mean', + hash_files=False, desc='output mean file') + stddev_file = GenFile(ns='in_file', template='%s_stdev', + hash_files=False, desc='output std deviation file') + detrended_file = GenFile( + ns='in_file', template='%s_detrend', hash_files=False, + desc='input file after detrending') class TSNROutputSpec(TraitedSpec): @@ -333,20 +285,16 @@ def _run_interface(self, runtime): nb.save(img, op.abspath(self.inputs.mean_file)) img = nb.Nifti1Image(stddevimg, img.get_affine(), header) nb.save(img, op.abspath(self.inputs.stddev_file)) - return runtime - def _list_outputs(self): - outputs = self._outputs().get() - for k in ['tsnr_file', 'mean_file', 'stddev_file']: - outputs[k] = op.abspath(getattr(self.inputs, k)) - - if isdefined(self.inputs.regress_poly): - outputs['detrended_file'] = op.abspath(self.inputs.detrended_file) - return outputs + if not isdefined(self.inputs.regress_poly): + self.outputs.detrended_file = Undefined + return runtime class GunzipInputSpec(BaseInterfaceInputSpec): in_file = File(exists=True, mandatory=True) + out_file = GenFile(ns='in_file', template='%s', name_remove='.gz', + keep_extension=False, desc='output file') class GunzipOutputSpec(TraitedSpec): @@ -359,47 +307,13 @@ class Gunzip(BaseInterface): input_spec = GunzipInputSpec output_spec = GunzipOutputSpec - def _gen_output_file_name(self): - _, base, ext = split_filename(self.inputs.in_file) - if ext[-2:].lower() == ".gz": - ext = ext[:-3] - return os.path.abspath(base + ext[:-3]) - def _run_interface(self, runtime): import gzip - in_file = gzip.open(self.inputs.in_file, 'rb') - out_file = open(self._gen_output_file_name(), 'wb') - out_file.write(in_file.read()) - out_file.close() - in_file.close() + with gzip.open(self.inputs.in_file, 'rb') as in_file: + with open(self.inputs.out_file, 'wb') as out_file: + out_file.write(in_file.read()) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self._gen_output_file_name() - return outputs - - -def replaceext(in_list, ext): - out_list = list() - for filename in in_list: - path, name, _ = split_filename(op.abspath(filename)) - out_name = op.join(path, name) + ext - out_list.append(out_name) - return out_list - - -def matlab2csv(in_array, name, reshape): - output_array = np.asarray(in_array) - if reshape: - if len(np.shape(output_array)) > 1: - output_array = np.reshape(output_array, ( - np.shape(output_array)[0] * np.shape(output_array)[1], 1)) - iflogger.info(np.shape(output_array)) - output_name = op.abspath(name + '.csv') - np.savetxt(output_name, output_array, delimiter=',') - return output_name - class Matlab2CSVInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc='Input MATLAB .mat file') @@ -449,14 +363,14 @@ def _run_interface(self, runtime): if isinstance(in_dict[key][0], np.ndarray): saved_variables.append(key) else: - iflogger.info('One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) + IFLOGGER.info('One of the keys in the input file, {k}, is not a Numpy array'.format(k=key)) if len(saved_variables) > 1: - iflogger.info( + IFLOGGER.info( '{N} variables found:'.format(N=len(saved_variables))) - iflogger.info(saved_variables) + IFLOGGER.info(saved_variables) for variable in saved_variables: - iflogger.info( + IFLOGGER.info( '...Converting {var} - type {ty} - to\ CSV'.format(var=variable, ty=type(in_dict[variable])) ) @@ -465,17 +379,18 @@ def _run_interface(self, runtime): elif len(saved_variables) == 1: _, name, _ = split_filename(self.inputs.in_file) variable = saved_variables[0] - iflogger.info('Single variable found {var}, type {ty}:'.format( + IFLOGGER.info('Single variable found {var}, type {ty}:'.format( var=variable, ty=type(in_dict[variable]))) - iflogger.info('...Converting {var} to CSV from {f}'.format( + IFLOGGER.info('...Converting {var} to CSV from {f}'.format( var=variable, f=self.inputs.in_file)) matlab2csv(in_dict[variable], name, self.inputs.reshape_matrix) else: - iflogger.error('No values in the MATLAB file?!') + IFLOGGER.error('No values in the MATLAB file?!') return runtime - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + super(Matlab2CSV, self)._post_run() + in_dict = sio.loadmat(op.abspath(self.inputs.in_file)) saved_variables = list() for key in list(in_dict.keys()): @@ -483,104 +398,16 @@ def _list_outputs(self): if isinstance(in_dict[key][0], np.ndarray): saved_variables.append(key) else: - iflogger.error('One of the keys in the input file, {k}, is\ + IFLOGGER.error('One of the keys in the input file, {k}, is\ not a Numpy array'.format(k=key)) if len(saved_variables) > 1: - outputs['csv_files'] = replaceext(saved_variables, '.csv') + self.outputs.csv_files = replaceext(saved_variables, '.csv') elif len(saved_variables) == 1: _, name, ext = split_filename(self.inputs.in_file) - outputs['csv_files'] = op.abspath(name + '.csv') + self.outputs.csv_files = op.abspath(name + '.csv') else: - iflogger.error('No values in the MATLAB file?!') - return outputs - - -def merge_csvs(in_list): - for idx, in_file in enumerate(in_list): - try: - in_array = np.loadtxt(in_file, delimiter=',') - except ValueError as ex: - try: - in_array = np.loadtxt(in_file, delimiter=',', skiprows=1) - except ValueError as ex: - first = open(in_file, 'r') - header_line = first.readline() - header_list = header_line.split(',') - n_cols = len(header_list) - try: - in_array = np.loadtxt( - in_file, delimiter=',', skiprows=1, - usecols=list(range(1, n_cols)) - ) - except ValueError as ex: - in_array = np.loadtxt( - in_file, delimiter=',', skiprows=1, usecols=list(range(1, n_cols - 1))) - if idx == 0: - out_array = in_array - else: - out_array = np.dstack((out_array, in_array)) - out_array = np.squeeze(out_array) - iflogger.info('Final output array shape:') - iflogger.info(np.shape(out_array)) - return out_array - - -def remove_identical_paths(in_files): - import os.path as op - from ..utils.filemanip import split_filename - if len(in_files) > 1: - out_names = list() - commonprefix = op.commonprefix(in_files) - lastslash = commonprefix.rfind('/') - commonpath = commonprefix[0:(lastslash + 1)] - for fileidx, in_file in enumerate(in_files): - path, name, ext = split_filename(in_file) - in_file = op.join(path, name) - name = in_file.replace(commonpath, '') - name = name.replace('_subject_id_', '') - out_names.append(name) - else: - path, name, ext = split_filename(in_files[0]) - out_names = [name] - return out_names - - -def maketypelist(rowheadings, shape, extraheadingBool, extraheading): - typelist = [] - if rowheadings: - typelist.append(('heading', 'a40')) - if len(shape) > 1: - for idx in range(1, (min(shape) + 1)): - typelist.append((str(idx), float)) - else: - for idx in range(1, (shape[0] + 1)): - typelist.append((str(idx), float)) - if extraheadingBool: - typelist.append((extraheading, 'a40')) - iflogger.info(typelist) - return typelist - - -def makefmtlist(output_array, typelist, rowheadingsBool, - shape, extraheadingBool): - fmtlist = [] - if rowheadingsBool: - fmtlist.append('%s') - if len(shape) > 1: - output = np.zeros(max(shape), typelist) - for idx in range(1, min(shape) + 1): - output[str(idx)] = output_array[:, idx - 1] - fmtlist.append('%f') - else: - output = np.zeros(1, typelist) - for idx in range(1, len(output_array) + 1): - output[str(idx)] = output_array[idx - 1] - fmtlist.append('%f') - if extraheadingBool: - fmtlist.append('%s') - fmt = ','.join(fmtlist) - return fmt, output + IFLOGGER.error('No values in the MATLAB file?!') class MergeCSVFilesInputSpec(TraitedSpec): @@ -638,41 +465,41 @@ def _run_interface(self, runtime): This block defines the column headings. """ if isdefined(self.inputs.column_headings): - iflogger.info('Column headings have been provided:') + IFLOGGER.info('Column headings have been provided:') headings = self.inputs.column_headings else: - iflogger.info( + IFLOGGER.info( 'Column headings not provided! Pulled from input filenames:') headings = remove_identical_paths(self.inputs.in_files) if isdefined(self.inputs.extra_field): if isdefined(self.inputs.extra_column_heading): extraheading = self.inputs.extra_column_heading - iflogger.info('Extra column heading provided: {col}'.format( + IFLOGGER.info('Extra column heading provided: {col}'.format( col=extraheading)) else: extraheading = 'type' - iflogger.info( + IFLOGGER.info( 'Extra column heading was not defined. Using "type"') headings.append(extraheading) extraheadingBool = True if len(self.inputs.in_files) == 1: - iflogger.warn('Only one file input!') + IFLOGGER.warn('Only one file input!') if isdefined(self.inputs.row_headings): - iflogger.info('Row headings have been provided. Adding "labels"\ + IFLOGGER.info('Row headings have been provided. Adding "labels"\ column header.') prefix = '"{p}","'.format(p=self.inputs.row_heading_title) csv_headings = prefix + '","'.join(itertools.chain( headings)) + '"\n' rowheadingsBool = True else: - iflogger.info('Row headings have not been provided.') + IFLOGGER.info('Row headings have not been provided.') csv_headings = '"' + '","'.join(itertools.chain(headings)) + '"\n' - iflogger.info('Final Headings:') - iflogger.info(csv_headings) + IFLOGGER.info('Final Headings:') + IFLOGGER.info(csv_headings) """ Next we merge the arrays and define the output text file @@ -710,29 +537,20 @@ def _run_interface(self, runtime): mx = 1 for idx in range(0, mx): extrafieldlist.append(self.inputs.extra_field) - iflogger.info(len(extrafieldlist)) + IFLOGGER.info(len(extrafieldlist)) output[extraheading] = extrafieldlist - iflogger.info(output) - iflogger.info(fmt) + IFLOGGER.info(output) + IFLOGGER.info(fmt) np.savetxt(file_handle, output, fmt, delimiter=',') file_handle.close() return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - _, name, ext = split_filename(self.inputs.out_file) - if not ext == '.csv': - ext = '.csv' - out_file = op.abspath(name + ext) - outputs['csv_file'] = out_file - return outputs - class AddCSVColumnInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc='Input comma-separated value (CSV) files') - out_file = File('extra_heading.csv', usedefault=True, - desc='Output filename for merged CSV file') + out_file = GenFile(ns='in_file', template='%s_col_added', output_name='csv_file', + desc='Output filename for merged CSV file') extra_column_heading = traits.Str( desc='New heading to add for the added field.') extra_field = traits.Str( @@ -761,37 +579,21 @@ class AddCSVColumn(BaseInterface): output_spec = AddCSVColumnOutputSpec def _run_interface(self, runtime): - in_file = open(self.inputs.in_file, 'r') - _, name, ext = split_filename(self.inputs.out_file) - if not ext == '.csv': - ext = '.csv' - out_file = op.abspath(name + ext) - - out_file = open(out_file, 'w') - firstline = in_file.readline() - firstline = firstline.replace('\n', '') - new_firstline = firstline + ',"' + \ - self.inputs.extra_column_heading + '"\n' - out_file.write(new_firstline) - for line in in_file: - new_line = line.replace('\n', '') - new_line = new_line + ',' + self.inputs.extra_field + '\n' - out_file.write(new_line) + with open(self.inputs.in_file, 'r') as in_file: + firstline = in_file.readline() + firstline = firstline.replace('\n', '') + new_firstline = firstline + ',"' + self.inputs.extra_column_heading + '"\n' + with open(self.inputs.out_file, 'w') as out_file: + out_file.write(new_firstline) + for line in in_file: + new_line = line.replace('\n', '') + new_line = new_line + ',' + self.inputs.extra_field + '\n' + out_file.write(new_line) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - _, name, ext = split_filename(self.inputs.out_file) - if not ext == '.csv': - ext = '.csv' - out_file = op.abspath(name + ext) - outputs['csv_file'] = out_file - return outputs - class AddCSVRowInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): - in_file = traits.File(mandatory=True, - desc='Input comma-separated value (CSV) files') + in_file = File(mandatory=True, desc='Input comma-separated value (CSV) files') _outputs = traits.Dict(traits.Any, value={}, usedefault=True) def __setattr__(self, key, value): @@ -801,7 +603,7 @@ def __setattr__(self, key, value): self._outputs[key] = value else: if key in self._outputs: - self._outputs[key] = value + self.outputs[key] = value super(AddCSVRowInputSpec, self).__setattr__(key, value) @@ -865,9 +667,8 @@ def _run_interface(self, runtime): import lockfile as pl self._have_lock = True except ImportError: - from warnings import warn - warn(('Python module lockfile was not found: AddCSVRow will not be' - ' thread-safe in multi-processor execution')) + IFLOGGER.warn('Python module lockfile was not found: AddCSVRow will not be' + ' thread-safe in multi-processor execution') input_dict = {} for key, val in list(self.inputs._outputs.items()): @@ -906,13 +707,9 @@ def _run_interface(self, runtime): # df = pd.concat([formerdf, df], ignore_index=True) # df.to_csv(fh) + self.outputs.csv_file = op.abspath(self.inputs.in_file) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['csv_file'] = self.inputs.in_file - return outputs - def _outputs(self): return self._add_output_traits(super(AddCSVRow, self)._outputs()) @@ -922,13 +719,10 @@ def _add_output_traits(self, base): class CalculateNormalizedMomentsInputSpec(TraitedSpec): timeseries_file = File( - exists=True, mandatory=True, - desc='Text file with timeseries in columns and timepoints in rows,\ - whitespace separated') - moment = traits.Int( - mandatory=True, - desc="Define which moment should be calculated, 3 for skewness, 4 for\ - kurtosis.") + exists=True, mandatory=True, desc='Text file with timeseries in columns and timepoints' + ' in rows, whitespace separated') + moment = traits.Int(mandatory=True, desc='Define which moment should be calculated, 3 for ' + 'skewness, 4 for kurtosis.') class CalculateNormalizedMomentsOutputSpec(TraitedSpec): @@ -951,45 +745,24 @@ class CalculateNormalizedMoments(BaseInterface): output_spec = CalculateNormalizedMomentsOutputSpec def _run_interface(self, runtime): - - self._moments = calc_moments( + self.outputs.skewness = calc_moments( self.inputs.timeseries_file, self.inputs.moment) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['skewness'] = self._moments - return outputs - - -def calc_moments(timeseries_file, moment): - """Returns nth moment (3 for skewness, 4 for kurtosis) of timeseries - (list of values; one per timeseries). - - Keyword arguments: - timeseries_file -- text file with white space separated timepoints in rows - - """ - timeseries = np.genfromtxt(timeseries_file) - - m2 = stats.moment(timeseries, 2, axis=0) - m3 = stats.moment(timeseries, moment, axis=0) - zero = (m2 == 0) - return np.where(zero, 0, m3 / m2 ** (moment / 2.0)) - class AddNoiseInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc='input image that will be corrupted with noise') - in_mask = File(exists=True, desc=('input mask, voxels outside this mask ' - 'will be considered background')) + in_mask = File(exists=True, desc='input mask, voxels outside this mask ' + 'will be considered background') snr = traits.Float(10.0, desc='desired output SNR in dB', usedefault=True) dist = traits.Enum('normal', 'rician', usedefault=True, mandatory=True, - desc=('desired noise distribution')) + desc='desired noise distribution') bg_dist = traits.Enum('normal', 'rayleigh', usedefault=True, mandatory=True, - desc=('desired noise distribution, currently ' - 'only normal is implemented')) - out_file = File(desc='desired output filename') + desc='desired noise distribution, currently ' + 'only normal is implemented') + out_file = GenFile(ns=['in_file', 'snr'], template='%s_SNR%.02f', + keep_extension=True, desc='desired output filename') class AddNoiseOutputSpec(TraitedSpec): @@ -1027,23 +800,9 @@ def _run_interface(self, runtime): result = self.gen_noise(in_data, mask=in_mask, snr_db=snr, dist=self.inputs.dist, bg_dist=self.inputs.bg_dist) res_im = nb.Nifti1Image(result, in_image.affine, in_image.header) - res_im.to_filename(self._gen_output_filename()) + res_im.to_filename(self.inputs.out_file) return runtime - def _gen_output_filename(self): - if not isdefined(self.inputs.out_file): - _, base, ext = split_filename(self.inputs.in_file) - out_file = os.path.abspath('%s_SNR%03.2f%s' % (base, self.inputs.snr, ext)) - else: - out_file = self.inputs.out_file - - return out_file - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self._gen_output_filename() - return outputs - def gen_noise(self, image, mask=None, snr_db=10.0, dist='normal', bg_dist='normal'): """ Generates a copy of an image with a certain amount of @@ -1091,13 +850,13 @@ def gen_noise(self, image, mask=None, snr_db=10.0, dist='normal', bg_dist='norma class NormalizeProbabilityMapSetInputSpec(TraitedSpec): in_files = InputMultiPath(File(exists=True, mandatory=True, desc='The tpms to be normalized')) - in_mask = File(exists=True, - desc='Masked voxels must sum up 1.0, 0.0 otherwise.') + out_files = GenMultiFile(template='{in_files}_norm', keep_extension=True, + desc="normalized maps") + in_mask = File(exists=True, desc='Masked voxels must sum up 1.0, 0.0 otherwise.') class NormalizeProbabilityMapSetOutputSpec(TraitedSpec): - out_files = OutputMultiPath(File(exists=True), - desc="normalized maps") + out_files = OutputMultiPath(File(exists=True), desc="normalized maps") class NormalizeProbabilityMapSet(BaseInterface): @@ -1126,15 +885,9 @@ def _run_interface(self, runtime): if isdefined(self.inputs.in_mask): mask = self.inputs.in_mask - self._out_filenames = normalize_tpms(self.inputs.in_files, mask) + normalize_tpms(self.inputs.in_files, mask, self.inputs.out_files) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_files'] = self._out_filenames - return outputs - - class SplitROIsInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc='file to be splitted') @@ -1169,25 +922,18 @@ class SplitROIs(BaseInterface): def _run_interface(self, runtime): mask = None roisize = None - self._outnames = {} if isdefined(self.inputs.in_mask): mask = self.inputs.in_mask if isdefined(self.inputs.roi_size): roisize = self.inputs.roi_size - res = split_rois(self.inputs.in_file, - mask, roisize) - self._outnames['out_files'] = res[0] - self._outnames['out_masks'] = res[1] - self._outnames['out_index'] = res[2] - return runtime + res = split_rois(self.inputs.in_file, mask, roisize) + self.outputs.out_files = res[0] + self.outputs.out_masks = res[1] + self.outputs.out_index = res[2] - def _list_outputs(self): - outputs = self.output_spec().get() - for k, v in self._outnames.items(): - outputs[k] = v - return outputs + return runtime class MergeROIsInputSpec(TraitedSpec): @@ -1222,17 +968,134 @@ class MergeROIs(BaseInterface): output_spec = MergeROIsOutputSpec def _run_interface(self, runtime): - res = merge_rois(self.inputs.in_files, - self.inputs.in_index, - self.inputs.in_reference) - self._merged = res + self.outputs.merged_file = merge_rois( + self.inputs.in_files, self.inputs.in_index, self.inputs.in_reference) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['merged_file'] = self._merged - return outputs +# Helper functions ------------------------------------------------------ + +def replaceext(in_list, ext): + out_list = list() + for filename in in_list: + path, name, _ = split_filename(op.abspath(filename)) + out_name = op.join(path, name) + ext + out_list.append(out_name) + return out_list + + +def matlab2csv(in_array, name, reshape): + output_array = np.asarray(in_array) + if reshape: + if len(np.shape(output_array)) > 1: + output_array = np.reshape(output_array, ( + np.shape(output_array)[0] * np.shape(output_array)[1], 1)) + IFLOGGER.info(np.shape(output_array)) + output_name = op.abspath(name + '.csv') + np.savetxt(output_name, output_array, delimiter=',') + return output_name + +def merge_csvs(in_list): + for idx, in_file in enumerate(in_list): + try: + in_array = np.loadtxt(in_file, delimiter=',') + except ValueError as ex: + try: + in_array = np.loadtxt(in_file, delimiter=',', skiprows=1) + except ValueError as ex: + first = open(in_file, 'r') + header_line = first.readline() + header_list = header_line.split(',') + n_cols = len(header_list) + try: + in_array = np.loadtxt( + in_file, delimiter=',', skiprows=1, + usecols=list(range(1, n_cols)) + ) + except ValueError as ex: + in_array = np.loadtxt( + in_file, delimiter=',', skiprows=1, usecols=list(range(1, n_cols - 1))) + if idx == 0: + out_array = in_array + else: + out_array = np.dstack((out_array, in_array)) + out_array = np.squeeze(out_array) + IFLOGGER.info('Final output array shape:') + IFLOGGER.info(np.shape(out_array)) + return out_array + + +def remove_identical_paths(in_files): + import os.path as op + from ..utils.filemanip import split_filename + if len(in_files) > 1: + out_names = list() + commonprefix = op.commonprefix(in_files) + lastslash = commonprefix.rfind('/') + commonpath = commonprefix[0:(lastslash + 1)] + for fileidx, in_file in enumerate(in_files): + path, name, ext = split_filename(in_file) + in_file = op.join(path, name) + name = in_file.replace(commonpath, '') + name = name.replace('_subject_id_', '') + out_names.append(name) + else: + path, name, ext = split_filename(in_files[0]) + out_names = [name] + return out_names + + +def maketypelist(rowheadings, shape, extraheadingBool, extraheading): + typelist = [] + if rowheadings: + typelist.append(('heading', 'a40')) + if len(shape) > 1: + for idx in range(1, (min(shape) + 1)): + typelist.append((str(idx), float)) + else: + for idx in range(1, (shape[0] + 1)): + typelist.append((str(idx), float)) + if extraheadingBool: + typelist.append((extraheading, 'a40')) + IFLOGGER.info(typelist) + return typelist + + +def makefmtlist(output_array, typelist, rowheadingsBool, + shape, extraheadingBool): + fmtlist = [] + if rowheadingsBool: + fmtlist.append('%s') + if len(shape) > 1: + output = np.zeros(max(shape), typelist) + for idx in range(1, min(shape) + 1): + output[str(idx)] = output_array[:, idx - 1] + fmtlist.append('%f') + else: + output = np.zeros(1, typelist) + for idx in range(1, len(output_array) + 1): + output[str(idx)] = output_array[idx - 1] + fmtlist.append('%f') + if extraheadingBool: + fmtlist.append('%s') + fmt = ','.join(fmtlist) + return fmt, output + + +def calc_moments(timeseries_file, moment): + """Returns nth moment (3 for skewness, 4 for kurtosis) of timeseries + (list of values; one per timeseries). + + Keyword arguments: + timeseries_file -- text file with white space separated timepoints in rows + + """ + timeseries = np.genfromtxt(timeseries_file) + + m2 = stats.moment(timeseries, 2, axis=0) + m3 = stats.moment(timeseries, moment, axis=0) + zero = (m2 == 0) + return np.where(zero, 0, m3 / m2 ** (moment / 2.0)) def normalize_tpms(in_files, in_mask=None, out_files=[]): """ @@ -1369,8 +1232,7 @@ def split_rois(in_file, mask=None, roishape=None): return out_files, out_mask, out_idxs -def merge_rois(in_files, in_idxs, in_ref, - dtype=None, out_file=None): +def merge_rois(in_files, in_idxs, in_ref, dtype=None, out_file=None): """ Re-builds an image resulting from a parallelized processing """ @@ -1389,7 +1251,7 @@ def merge_rois(in_files, in_idxs, in_ref, # to avoid memory errors if op.splitext(in_ref)[1] == '.gz': try: - iflogger.info('uncompress %i' % in_ref) + IFLOGGER.info('uncompress %i' % in_ref) sp.check_call(['gunzip', in_ref], stdout=sp.PIPE, shell=True) in_ref = op.splitext(in_ref)[0] except: @@ -1468,10 +1330,9 @@ class Distance(nam.Distance): Use :py:class:`nipype.algorithms.metrics.Distance` instead. """ def __init__(self, **inputs): - super(nam.Distance, self).__init__(**inputs) - warnings.warn(("This interface has been deprecated since 0.10.0," - " please use nipype.algorithms.metrics.Distance"), - DeprecationWarning) + super(Distance, self).__init__(**inputs) + IFLOGGER.warn("This interface has been deprecated since 0.10.0," + " please use nipype.algorithms.metrics.Distance") class Overlap(nam.Overlap): @@ -1481,10 +1342,9 @@ class Overlap(nam.Overlap): Use :py:class:`nipype.algorithms.metrics.Overlap` instead. """ def __init__(self, **inputs): - super(nam.Overlap, self).__init__(**inputs) - warnings.warn(("This interface has been deprecated since 0.10.0," - " please use nipype.algorithms.metrics.Overlap"), - DeprecationWarning) + super(Overlap, self).__init__(**inputs) + IFLOGGER.warn("This interface has been deprecated since 0.10.0," + " please use nipype.algorithms.metrics.Overlap") class FuzzyOverlap(nam.FuzzyOverlap): @@ -1495,7 +1355,6 @@ class FuzzyOverlap(nam.FuzzyOverlap): Use :py:class:`nipype.algorithms.metrics.FuzzyOverlap` instead. """ def __init__(self, **inputs): - super(nam.FuzzyOverlap, self).__init__(**inputs) - warnings.warn(("This interface has been deprecated since 0.10.0," - " please use nipype.algorithms.metrics.FuzzyOverlap"), - DeprecationWarning) + super(FuzzyOverlap, self).__init__(**inputs) + IFLOGGER.warn("This interface has been deprecated since 0.10.0," + " please use nipype.algorithms.metrics.FuzzyOverlap") diff --git a/nipype/algorithms/modelgen.py b/nipype/algorithms/modelgen.py index 93aaeb042c..52daa0da1c 100644 --- a/nipype/algorithms/modelgen.py +++ b/nipype/algorithms/modelgen.py @@ -29,9 +29,10 @@ from scipy.special import gammaln from ..external.six import string_types -from ..interfaces.base import (BaseInterface, TraitedSpec, InputMultiPath, - traits, File, Bunch, BaseInterfaceInputSpec, - isdefined) + +from ..interfaces.base import (traits, File, isdefined, Undefined, BaseInterfaceInputSpec, + TraitedSpec, InputMultiPath, BaseInterface, Bunch) + from ..utils.filemanip import filename_to_list from .. import config, logging iflogger = logging.getLogger('interface') @@ -406,15 +407,10 @@ def _run_interface(self, runtime): """ self._sessioninfo = None self._generate_design() - return runtime - - def _list_outputs(self): - outputs = self._outputs().get() if not hasattr(self, '_sessinfo'): self._generate_design() - outputs['session_info'] = self._sessinfo - - return outputs + self.outputs.session_info = self._sessinfo + return runtime class SpecifySPMModelInputSpec(SpecifyModelInputSpec): @@ -571,11 +567,12 @@ class SpecifySparseModelInputSpec(SpecifyModelInputSpec): desc="Create a temporal derivative in addition to regular regressor") scale_regressors = traits.Bool(True, desc="Scale regressors by the peak", usedefault=True) - scan_onset = traits.Float(0.0, - desc="Start of scanning relative to onset of run in secs", + scan_onset = traits.Float(0.0, desc="Start of scanning relative to onset of run in secs", usedefault=True) - save_plot = traits.Bool(desc=('save plot of sparse design calculation ' - '(Requires matplotlib)')) + save_plot = traits.Bool(False, usedefault=True, desc='save plot of sparse design ' + 'calculation (Requires matplotlib)') + sparse_png_file = File('sparse.png', desc='PNG file showing sparse design') + sparse_svg_file = File('sparse.svg', desc='SVG file showing sparse design') class SpecifySparseModelOutputSpec(SpecifyModelOutputSpec): @@ -802,12 +799,9 @@ def _generate_design(self, infolist=None): sparselist = self._generate_clustered_design(infolist) super(SpecifySparseModel, self)._generate_design(infolist=sparselist) - def _list_outputs(self): - outputs = self._outputs().get() - if not hasattr(self, '_sessinfo'): - self._generate_design() - outputs['session_info'] = self._sessinfo - if isdefined(self.inputs.save_plot) and self.inputs.save_plot: - outputs['sparse_png_file'] = os.path.join(os.getcwd(), 'sparse.png') - outputs['sparse_svg_file'] = os.path.join(os.getcwd(), 'sparse.svg') - return outputs + def _post_run(self): + super(SpecifySparseModel,self)._post_run() + # Unset non-used variables + if not self.inputs.save_plot: + self.outputs.sparse_png_file = Undefined + self.outputs.sparse_svg_file = Undefined diff --git a/nipype/algorithms/rapidart.py b/nipype/algorithms/rapidart.py index 06ad009d50..fb1ee0a354 100644 --- a/nipype/algorithms/rapidart.py +++ b/nipype/algorithms/rapidart.py @@ -30,129 +30,16 @@ import scipy.io as sio from ..external.six import string_types -from ..interfaces.base import (BaseInterface, traits, InputMultiPath, - OutputMultiPath, TraitedSpec, File, - BaseInterfaceInputSpec, isdefined) + from ..utils.filemanip import filename_to_list, save_json, split_filename from ..utils.misc import find_indices +from ..interfaces.base import (traits, File, GenMultiFile, isdefined, BaseInterfaceInputSpec, + TraitedSpec, InputMultiPath, OutputMultiPath, BaseInterface) + from .. import logging, config iflogger = logging.getLogger('interface') -def _get_affine_matrix(params, source): - """Return affine matrix given a set of translation and rotation parameters - - params : np.array (upto 12 long) in native package format - source : the package that generated the parameters - supports SPM, AFNI, FSFAST, FSL, NIPY - """ - if source == 'FSL': - params = params[[3, 4, 5, 0, 1, 2]] - elif source in ('AFNI', 'FSFAST'): - params = params[np.asarray([4, 5, 3, 1, 2, 0]) + (len(params) > 6)] - params[3:] = params[3:] * np.pi / 180. - if source == 'NIPY': - # nipy does not store typical euler angles, use nipy to convert - from nipy.algorithms.registration import to_matrix44 - return to_matrix44(params) - # process for FSL, SPM, AFNI and FSFAST - rotfunc = lambda x: np.array([[np.cos(x), np.sin(x)], - [-np.sin(x), np.cos(x)]]) - q = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]) - if len(params) < 12: - params = np.hstack((params, q[len(params):])) - params.shape = (len(params),) - # Translation - T = np.eye(4) - T[0:3, -1] = params[0:3] - # Rotation - Rx = np.eye(4) - Rx[1:3, 1:3] = rotfunc(params[3]) - Ry = np.eye(4) - Ry[(0, 0, 2, 2), (0, 2, 0, 2)] = rotfunc(params[4]).ravel() - Rz = np.eye(4) - Rz[0:2, 0:2] = rotfunc(params[5]) - # Scaling - S = np.eye(4) - S[0:3, 0:3] = np.diag(params[6:9]) - # Shear - Sh = np.eye(4) - Sh[(0, 0, 1), (1, 2, 2)] = params[9:12] - if source in ('AFNI', 'FSFAST'): - return np.dot(T, np.dot(Ry, np.dot(Rx, np.dot(Rz, np.dot(S, Sh))))) - return np.dot(T, np.dot(Rx, np.dot(Ry, np.dot(Rz, np.dot(S, Sh))))) - - -def _calc_norm(mc, use_differences, source, brain_pts=None): - """Calculates the maximum overall displacement of the midpoints - of the faces of a cube due to translation and rotation. - - Parameters - ---------- - mc : motion parameter estimates - [3 translation, 3 rotation (radians)] - use_differences : boolean - brain_pts : [4 x n_points] of coordinates - - Returns - ------- - - norm : at each time point - displacement : euclidean distance (mm) of displacement at each coordinate - - """ - - if brain_pts is None: - respos = np.diag([70, 70, 75]) - resneg = np.diag([-70, -110, -45]) - all_pts = np.vstack((np.hstack((respos, resneg)), np.ones((1, 6)))) - displacement = None - else: - all_pts = brain_pts - n_pts = all_pts.size - all_pts.shape[1] - newpos = np.zeros((mc.shape[0], n_pts)) - if brain_pts is not None: - displacement = np.zeros((mc.shape[0], int(n_pts / 3))) - for i in range(mc.shape[0]): - affine = _get_affine_matrix(mc[i, :], source) - newpos[i, :] = np.dot(affine, - all_pts)[0:3, :].ravel() - if brain_pts is not None: - displacement[i, :] = \ - np.sqrt(np.sum(np.power(np.reshape(newpos[i, :], - (3, all_pts.shape[1])) - - all_pts[0:3, :], - 2), - axis=0)) - # np.savez('displacement.npz', newpos=newpos, pts=all_pts) - normdata = np.zeros(mc.shape[0]) - if use_differences: - newpos = np.concatenate((np.zeros((1, n_pts)), - np.diff(newpos, n=1, axis=0)), axis=0) - for i in range(newpos.shape[0]): - normdata[i] = \ - np.max(np.sqrt(np.sum(np.reshape(np.power(np.abs(newpos[i, :]), 2), - (3, all_pts.shape[1])), axis=0))) - else: - newpos = np.abs(signal.detrend(newpos, axis=0, type='constant')) - normdata = np.sqrt(np.mean(np.power(newpos, 2), axis=1)) - return normdata, displacement - - -def _nanmean(a, axis=None): - """Return the mean excluding items that are nan - - >>> a = [1, 2, np.nan] - >>> _nanmean(a) - 1.5 - - """ - if axis: - return np.nansum(a, axis) / np.sum(1 - np.isnan(a), axis) - else: - return np.nansum(a) / np.sum(1 - np.isnan(a)) - - class ArtifactDetectInputSpec(BaseInterfaceInputSpec): realigned_files = InputMultiPath(File(exists=True), desc="Names of realigned functional data files", @@ -304,34 +191,6 @@ def _get_output_filenames(self, motionfile, output_dir): return (artifactfile, intensityfile, statsfile, normfile, plotfile, displacementfile, maskfile) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['outlier_files'] = [] - outputs['intensity_files'] = [] - outputs['statistic_files'] = [] - outputs['mask_files'] = [] - if isdefined(self.inputs.use_norm) and self.inputs.use_norm: - outputs['norm_files'] = [] - if self.inputs.bound_by_brainmask: - outputs['displacement_files'] = [] - if isdefined(self.inputs.save_plot) and self.inputs.save_plot: - outputs['plot_files'] = [] - for i, f in enumerate(filename_to_list(self.inputs.realigned_files)): - (outlierfile, intensityfile, statsfile, normfile, plotfile, - displacementfile, maskfile) = \ - self._get_output_filenames(f, os.getcwd()) - outputs['outlier_files'].insert(i, outlierfile) - outputs['intensity_files'].insert(i, intensityfile) - outputs['statistic_files'].insert(i, statsfile) - outputs['mask_files'].insert(i, maskfile) - if isdefined(self.inputs.use_norm) and self.inputs.use_norm: - outputs['norm_files'].insert(i, normfile) - if self.inputs.bound_by_brainmask: - outputs['displacement_files'].insert(i, displacementfile) - if isdefined(self.inputs.save_plot) and self.inputs.save_plot: - outputs['plot_files'].insert(i, plotfile) - return outputs - def _plot_outliers_with_wave(self, wave, outliers, name): import matplotlib.pyplot as plt plt.plot(wave) @@ -539,24 +398,50 @@ def _run_interface(self, runtime): for i, imgf in enumerate(funcfilelist): self._detect_outliers_core(imgf, motparamlist[i], i, cwd=os.getcwd()) + + self.outputs.outlier_files = [] + self.outputs.intensity_files = [] + self.outputs.statistic_files = [] + self.outputs.mask_files = [] + if isdefined(self.inputs.use_norm) and self.inputs.use_norm: + self.outputs.norm_files = [] + if self.inputs.bound_by_brainmask: + self.outputs.displacement_files = [] + if isdefined(self.inputs.save_plot) and self.inputs.save_plot: + self.outputs.plot_files = [] + for i, f in enumerate(filename_to_list(self.inputs.realigned_files)): + (outlierfile, intensityfile, statsfile, normfile, plotfile, + displacementfile, maskfile) = \ + self._get_output_filenames(f, os.getcwd()) + self.outputs.outlier_files.insert(i, outlierfile) + self.outputs.intensity_files.insert(i, intensityfile) + self.outputs.statistic_files.insert(i, statsfile) + self.outputs.mask_files.insert(i, maskfile) + if isdefined(self.inputs.use_norm) and self.inputs.use_norm: + self.outputs.norm_files.insert(i, normfile) + if self.inputs.bound_by_brainmask: + self.outputs.displacement_files.insert(i, displacementfile) + if isdefined(self.inputs.save_plot) and self.inputs.save_plot: + self.outputs.plot_files.insert(i, plotfile) return runtime class StimCorrInputSpec(BaseInterfaceInputSpec): - realignment_parameters = InputMultiPath(File(exists=True), mandatory=True, - desc=('Names of realignment parameters corresponding to the functional ' - 'data files')) + realignment_parameters = InputMultiPath( + File(exists=True), mandatory=True, + desc='Names of realignment parameters corresponding to the functional data files') intensity_values = InputMultiPath(File(exists=True), mandatory=True, desc='Name of file containing intensity values') spm_mat_file = File(exists=True, mandatory=True, desc='SPM mat file (use pre-estimate SPM.mat file)') - concatenated_design = traits.Bool(mandatory=True, - desc='state if the design matrix contains concatenated sessions') - + concatenated_design = traits.Bool( + mandatory=True, desc='state if the design matrix contains concatenated sessions') + stimcorr_files = GenMultiFile(template='qa.{realignment_parameters}_stimcorr.txt', + keep_extension=False, desc='List of files containing correlation values') class StimCorrOutputSpec(TraitedSpec): - stimcorr_files = OutputMultiPath(File(exists=True), - desc='List of files containing correlation values') + stimcorr_files = OutputMultiPath( + File(exists=True), desc='List of files containing correlation values') class StimulusCorrelation(BaseInterface): @@ -584,29 +469,11 @@ class StimulusCorrelation(BaseInterface): input_spec = StimCorrInputSpec output_spec = StimCorrOutputSpec - def _get_output_filenames(self, motionfile, output_dir): - """Generate output files based on motion filenames - - Parameters - ---------- - motionfile: file/string - Filename for motion parameter file - output_dir: string - output directory in which the files will be generated - """ - (_, filename) = os.path.split(motionfile) - (filename, _) = os.path.splitext(filename) - corrfile = os.path.join(output_dir, ''.join(('qa.', filename, - '_stimcorr.txt'))) - return corrfile - - def _stimcorr_core(self, motionfile, intensityfile, designmatrix, cwd=None): + def _stimcorr_core(self, motionfile, intensityfile, corrfile, designmatrix): """ Core routine for determining stimulus correlation """ - if not cwd: - cwd = os.getcwd() # read in motion parameters mc_in = np.loadtxt(motionfile) g_in = np.loadtxt(intensityfile) @@ -615,7 +482,6 @@ def _stimcorr_core(self, motionfile, intensityfile, designmatrix, cwd=None): mccol = mc_in.shape[1] concat_matrix = np.hstack((np.hstack((designmatrix, mc_in)), g_in)) cm = np.corrcoef(concat_matrix, rowvar=0) - corrfile = self._get_output_filenames(motionfile, cwd) # write output to outputfile file = open(corrfile, 'w') file.write("Stats for:\n") @@ -665,14 +531,122 @@ def _run_interface(self, runtime): nrows.append(mc_in.shape[0]) matrix = self._get_spm_submatrix(spmmat, sessidx, rows) self._stimcorr_core(motparamlist[i], intensityfiles[i], + self.inputs.stimcorr_files[i], matrix, os.getcwd()) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - files = [] - for i, f in enumerate(self.inputs.realignment_parameters): - files.insert(i, self._get_output_filenames(f, os.getcwd())) - if files: - outputs['stimcorr_files'] = files - return outputs + +# Helper functions ----------------------------------------------------------------- + +def _get_affine_matrix(params, source): + """Return affine matrix given a set of translation and rotation parameters + + params : np.array (upto 12 long) in native package format + source : the package that generated the parameters + supports SPM, AFNI, FSFAST, FSL, NIPY + """ + if source == 'FSL': + params = params[[3, 4, 5, 0, 1, 2]] + elif source in ('AFNI', 'FSFAST'): + params = params[np.asarray([4, 5, 3, 1, 2, 0]) + (len(params) > 6)] + params[3:] = params[3:] * np.pi / 180. + if source == 'NIPY': + # nipy does not store typical euler angles, use nipy to convert + from nipy.algorithms.registration import to_matrix44 + return to_matrix44(params) + # process for FSL, SPM, AFNI and FSFAST + rotfunc = lambda x: np.array([[np.cos(x), np.sin(x)], + [-np.sin(x), np.cos(x)]]) + q = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0]) + if len(params) < 12: + params = np.hstack((params, q[len(params):])) + params.shape = (len(params),) + # Translation + T = np.eye(4) + T[0:3, -1] = params[0:3] + # Rotation + Rx = np.eye(4) + Rx[1:3, 1:3] = rotfunc(params[3]) + Ry = np.eye(4) + Ry[(0, 0, 2, 2), (0, 2, 0, 2)] = rotfunc(params[4]).ravel() + Rz = np.eye(4) + Rz[0:2, 0:2] = rotfunc(params[5]) + # Scaling + S = np.eye(4) + S[0:3, 0:3] = np.diag(params[6:9]) + # Shear + Sh = np.eye(4) + Sh[(0, 0, 1), (1, 2, 2)] = params[9:12] + if source in ('AFNI', 'FSFAST'): + return np.dot(T, np.dot(Ry, np.dot(Rx, np.dot(Rz, np.dot(S, Sh))))) + return np.dot(T, np.dot(Rx, np.dot(Ry, np.dot(Rz, np.dot(S, Sh))))) + + +def _calc_norm(mc, use_differences, source, brain_pts=None): + """Calculates the maximum overall displacement of the midpoints + of the faces of a cube due to translation and rotation. + + Parameters + ---------- + mc : motion parameter estimates + [3 translation, 3 rotation (radians)] + use_differences : boolean + brain_pts : [4 x n_points] of coordinates + + Returns + ------- + + norm : at each time point + displacement : euclidean distance (mm) of displacement at each coordinate + + """ + + if brain_pts is None: + respos = np.diag([70, 70, 75]) + resneg = np.diag([-70, -110, -45]) + all_pts = np.vstack((np.hstack((respos, resneg)), np.ones((1, 6)))) + displacement = None + else: + all_pts = brain_pts + n_pts = all_pts.size - all_pts.shape[1] + newpos = np.zeros((mc.shape[0], n_pts)) + if brain_pts is not None: + displacement = np.zeros((mc.shape[0], int(n_pts / 3))) + for i in range(mc.shape[0]): + affine = _get_affine_matrix(mc[i, :], source) + newpos[i, :] = np.dot(affine, + all_pts)[0:3, :].ravel() + if brain_pts is not None: + displacement[i, :] = \ + np.sqrt(np.sum(np.power(np.reshape(newpos[i, :], + (3, all_pts.shape[1])) - + all_pts[0:3, :], + 2), + axis=0)) + # np.savez('displacement.npz', newpos=newpos, pts=all_pts) + normdata = np.zeros(mc.shape[0]) + if use_differences: + newpos = np.concatenate((np.zeros((1, n_pts)), + np.diff(newpos, n=1, axis=0)), axis=0) + for i in range(newpos.shape[0]): + normdata[i] = \ + np.max(np.sqrt(np.sum(np.reshape(np.power(np.abs(newpos[i, :]), 2), + (3, all_pts.shape[1])), axis=0))) + else: + newpos = np.abs(signal.detrend(newpos, axis=0, type='constant')) + normdata = np.sqrt(np.mean(np.power(newpos, 2), axis=1)) + return normdata, displacement + + +def _nanmean(a, axis=None): + """Return the mean excluding items that are nan + + >>> a = [1, 2, np.nan] + >>> _nanmean(a) + 1.5 + + """ + if axis: + return np.nansum(a, axis) / np.sum(1 - np.isnan(a), axis) + else: + return np.nansum(a) / np.sum(1 - np.isnan(a)) diff --git a/nipype/algorithms/tests/test_auto_AddCSVColumn.py b/nipype/algorithms/tests/test_auto_AddCSVColumn.py index 89a52b8abe..1cf26d7c2b 100644 --- a/nipype/algorithms/tests/test_auto_AddCSVColumn.py +++ b/nipype/algorithms/tests/test_auto_AddCSVColumn.py @@ -8,7 +8,8 @@ def test_AddCSVColumn_inputs(): extra_field=dict(), in_file=dict(mandatory=True, ), - out_file=dict(usedefault=True, + out_file=dict(ns='in_file', + output_name='csv_file', ), ) inputs = AddCSVColumn.input_spec() diff --git a/nipype/algorithms/tests/test_auto_AddNoise.py b/nipype/algorithms/tests/test_auto_AddNoise.py index 50aa563ce0..0dc3fcabcc 100644 --- a/nipype/algorithms/tests/test_auto_AddNoise.py +++ b/nipype/algorithms/tests/test_auto_AddNoise.py @@ -13,7 +13,8 @@ def test_AddNoise_inputs(): in_file=dict(mandatory=True, ), in_mask=dict(), - out_file=dict(), + out_file=dict(ns=['in_file', 'snr'], + ), snr=dict(usedefault=True, ), ) diff --git a/nipype/algorithms/tests/test_auto_CreateNifti.py b/nipype/algorithms/tests/test_auto_CreateNifti.py index 0e12142783..ce76d6e26d 100644 --- a/nipype/algorithms/tests/test_auto_CreateNifti.py +++ b/nipype/algorithms/tests/test_auto_CreateNifti.py @@ -12,6 +12,8 @@ def test_CreateNifti_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), + nifti_file=dict(ns='data_file', + ), ) inputs = CreateNifti.input_spec() diff --git a/nipype/algorithms/tests/test_auto_Gunzip.py b/nipype/algorithms/tests/test_auto_Gunzip.py index b77e6dfbd5..35e4572913 100644 --- a/nipype/algorithms/tests/test_auto_Gunzip.py +++ b/nipype/algorithms/tests/test_auto_Gunzip.py @@ -9,6 +9,9 @@ def test_Gunzip_inputs(): ), in_file=dict(mandatory=True, ), + out_file=dict(name_remove='.gz', + ns='in_file', + ), ) inputs = Gunzip.input_spec() diff --git a/nipype/algorithms/tests/test_auto_ICC.py b/nipype/algorithms/tests/test_auto_ICC.py index 76b70b3369..4c32fe9a29 100644 --- a/nipype/algorithms/tests/test_auto_ICC.py +++ b/nipype/algorithms/tests/test_auto_ICC.py @@ -4,11 +4,15 @@ def test_ICC_inputs(): - input_map = dict(ignore_exception=dict(nohash=True, + input_map = dict(icc_map=dict(), + ignore_exception=dict(nohash=True, usedefault=True, ), mask=dict(mandatory=True, ), + session_F_map=dict(), + session_var_map=dict(), + subject_var_map=dict(), subjects_sessions=dict(mandatory=True, ), ) @@ -21,6 +25,7 @@ def test_ICC_inputs(): def test_ICC_outputs(): output_map = dict(icc_map=dict(), + session_F_map=dict(), session_var_map=dict(), subject_var_map=dict(), ) diff --git a/nipype/algorithms/tests/test_auto_MeshWarpMaths.py b/nipype/algorithms/tests/test_auto_MeshWarpMaths.py index dfd4c5bd63..a1ae3d5525 100644 --- a/nipype/algorithms/tests/test_auto_MeshWarpMaths.py +++ b/nipype/algorithms/tests/test_auto_MeshWarpMaths.py @@ -14,10 +14,10 @@ def test_MeshWarpMaths_inputs(): ), operator=dict(mandatory=True, ), - out_file=dict(usedefault=True, - ), - out_warp=dict(usedefault=True, + out_file=dict(keep_extension=True, + template='{in_surf}_warped', ), + out_warp=dict(), ) inputs = MeshWarpMaths.input_spec() diff --git a/nipype/algorithms/tests/test_auto_ModifyAffine.py b/nipype/algorithms/tests/test_auto_ModifyAffine.py index fb8c5ca876..1d16433b83 100644 --- a/nipype/algorithms/tests/test_auto_ModifyAffine.py +++ b/nipype/algorithms/tests/test_auto_ModifyAffine.py @@ -9,6 +9,7 @@ def test_ModifyAffine_inputs(): ), transformation_matrix=dict(usedefault=True, ), + transformed_volumes=dict(), volumes=dict(mandatory=True, ), ) diff --git a/nipype/algorithms/tests/test_auto_NormalizeProbabilityMapSet.py b/nipype/algorithms/tests/test_auto_NormalizeProbabilityMapSet.py index c2595baa72..66382d723c 100644 --- a/nipype/algorithms/tests/test_auto_NormalizeProbabilityMapSet.py +++ b/nipype/algorithms/tests/test_auto_NormalizeProbabilityMapSet.py @@ -6,6 +6,7 @@ def test_NormalizeProbabilityMapSet_inputs(): input_map = dict(in_files=dict(), in_mask=dict(), + out_files=dict(), ) inputs = NormalizeProbabilityMapSet.input_spec() diff --git a/nipype/algorithms/tests/test_auto_PickAtlas.py b/nipype/algorithms/tests/test_auto_PickAtlas.py index 27aaac7d41..c4db574d2a 100644 --- a/nipype/algorithms/tests/test_auto_PickAtlas.py +++ b/nipype/algorithms/tests/test_auto_PickAtlas.py @@ -15,7 +15,11 @@ def test_PickAtlas_inputs(): ), labels=dict(mandatory=True, ), - output_file=dict(), + mask_file=dict(ns='atlas', + ), + output_file=dict(deprecated=True, + new_name='mask_file', + ), ) inputs = PickAtlas.input_spec() diff --git a/nipype/algorithms/tests/test_auto_SimpleThreshold.py b/nipype/algorithms/tests/test_auto_SimpleThreshold.py index ff46592c11..f2ab4fa0c6 100644 --- a/nipype/algorithms/tests/test_auto_SimpleThreshold.py +++ b/nipype/algorithms/tests/test_auto_SimpleThreshold.py @@ -9,6 +9,7 @@ def test_SimpleThreshold_inputs(): ), threshold=dict(mandatory=True, ), + thresholded_volumes=dict(), volumes=dict(mandatory=True, ), ) diff --git a/nipype/algorithms/tests/test_auto_SpecifySparseModel.py b/nipype/algorithms/tests/test_auto_SpecifySparseModel.py index aa641facf7..873c2c5e41 100644 --- a/nipype/algorithms/tests/test_auto_SpecifySparseModel.py +++ b/nipype/algorithms/tests/test_auto_SpecifySparseModel.py @@ -22,11 +22,14 @@ def test_SpecifySparseModel_inputs(): ), realignment_parameters=dict(copyfile=False, ), - save_plot=dict(), + save_plot=dict(usedefault=True, + ), scale_regressors=dict(usedefault=True, ), scan_onset=dict(usedefault=True, ), + sparse_png_file=dict(), + sparse_svg_file=dict(), stimuli_as_impulses=dict(usedefault=True, ), subject_info=dict(mandatory=True, diff --git a/nipype/algorithms/tests/test_auto_StimulusCorrelation.py b/nipype/algorithms/tests/test_auto_StimulusCorrelation.py index f1b786aa8e..e806c3ee88 100644 --- a/nipype/algorithms/tests/test_auto_StimulusCorrelation.py +++ b/nipype/algorithms/tests/test_auto_StimulusCorrelation.py @@ -15,6 +15,7 @@ def test_StimulusCorrelation_inputs(): ), spm_mat_file=dict(mandatory=True, ), + stimcorr_files=dict(), ) inputs = StimulusCorrelation.input_spec() diff --git a/nipype/algorithms/tests/test_auto_TSNR.py b/nipype/algorithms/tests/test_auto_TSNR.py index 4bc6693b20..9cec72a0ca 100644 --- a/nipype/algorithms/tests/test_auto_TSNR.py +++ b/nipype/algorithms/tests/test_auto_TSNR.py @@ -5,7 +5,7 @@ def test_TSNR_inputs(): input_map = dict(detrended_file=dict(hash_files=False, - usedefault=True, + ns='in_file', ), ignore_exception=dict(nohash=True, usedefault=True, @@ -13,14 +13,14 @@ def test_TSNR_inputs(): in_file=dict(mandatory=True, ), mean_file=dict(hash_files=False, - usedefault=True, + ns='in_file', ), regress_poly=dict(), stddev_file=dict(hash_files=False, - usedefault=True, + ns='in_file', ), tsnr_file=dict(hash_files=False, - usedefault=True, + ns='in_file', ), ) inputs = TSNR.input_spec() diff --git a/nipype/algorithms/tests/test_auto_TVTKBaseInterface.py b/nipype/algorithms/tests/test_auto_TVTKBaseInterface.py index 3dd8ac6d2a..02fedd0e30 100644 --- a/nipype/algorithms/tests/test_auto_TVTKBaseInterface.py +++ b/nipype/algorithms/tests/test_auto_TVTKBaseInterface.py @@ -14,3 +14,11 @@ def test_TVTKBaseInterface_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_TVTKBaseInterface_outputs(): + output_map = dict() + outputs = TVTKBaseInterface.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/algorithms/tests/test_auto_WarpPoints.py b/nipype/algorithms/tests/test_auto_WarpPoints.py index 741b9f0c60..554bc8cdfb 100644 --- a/nipype/algorithms/tests/test_auto_WarpPoints.py +++ b/nipype/algorithms/tests/test_auto_WarpPoints.py @@ -10,11 +10,7 @@ def test_WarpPoints_inputs(): interp=dict(mandatory=True, usedefault=True, ), - out_points=dict(keep_extension=True, - name_source='points', - name_template='%s_warped', - output_name='out_points', - ), + out_points=dict(), points=dict(mandatory=True, ), warp=dict(mandatory=True, diff --git a/nipype/algorithms/tests/test_errormap.py b/nipype/algorithms/tests/test_errormap.py index 361646add0..7c58fac2e0 100644 --- a/nipype/algorithms/tests/test_errormap.py +++ b/nipype/algorithms/tests/test_errormap.py @@ -12,6 +12,7 @@ def test_errormap(): tempdir = mkdtemp() + os.chdir(tempdir) # Single-Spectual # Make two fake 2*2*2 voxel volumes volume1 = np.array([[[2.0, 8.0], [1.0, 2.0]], [[1.0, 9.0], [0.0, 3.0]]]) # John von Neumann's birthday diff --git a/nipype/algorithms/tests/test_rapidart.py b/nipype/algorithms/tests/test_rapidart.py index 1ba6414e29..4edd4e8b10 100644 --- a/nipype/algorithms/tests/test_rapidart.py +++ b/nipype/algorithms/tests/test_rapidart.py @@ -16,19 +16,20 @@ def test_ad_init(): yield assert_false, ad.inputs.use_differences[1] -def test_ad_output_filenames(): - ad = ra.ArtifactDetect() - outputdir = '/tmp' - f = 'motion.nii' - (outlierfile, intensityfile, statsfile, normfile, plotfile, - displacementfile, maskfile) = ad._get_output_filenames(f, outputdir) - yield assert_equal, outlierfile, '/tmp/art.motion_outliers.txt' - yield assert_equal, intensityfile, '/tmp/global_intensity.motion.txt' - yield assert_equal, statsfile, '/tmp/stats.motion.txt' - yield assert_equal, normfile, '/tmp/norm.motion.txt' - yield assert_equal, plotfile, '/tmp/plot.motion.png' - yield assert_equal, displacementfile, '/tmp/disp.motion.nii' - yield assert_equal, maskfile, '/tmp/mask.motion.nii' +# def test_ad_output_filenames(): + # TODO: rewrite this test + # ad = ra.ArtifactDetect() + # outputdir = '/tmp' + # f = 'motion.nii' + # (outlierfile, intensityfile, statsfile, normfile, plotfile, + # displacementfile, maskfile) = ad.inputs. + # yield assert_equal, outlierfile, '/tmp/art.motion_outliers.txt' + # yield assert_equal, intensityfile, '/tmp/global_intensity.motion.txt' + # yield assert_equal, statsfile, '/tmp/stats.motion.txt' + # yield assert_equal, normfile, '/tmp/norm.motion.txt' + # yield assert_equal, plotfile, '/tmp/plot.motion.png' + # yield assert_equal, displacementfile, '/tmp/disp.motion.nii' + # yield assert_equal, maskfile, '/tmp/mask.motion.nii' def test_ad_get_affine_matrix(): @@ -79,11 +80,3 @@ def test_sc_populate_inputs(): spm_mat_file=None, concatenated_design=None) yield assert_equal, set(sc.inputs.__dict__.keys()), set(inputs.__dict__.keys()) - - -def test_sc_output_filenames(): - sc = ra.StimulusCorrelation() - outputdir = '/tmp' - f = 'motion.nii' - corrfile = sc._get_output_filenames(f, outputdir) - yield assert_equal, corrfile, '/tmp/qa.motion_stimcorr.txt' diff --git a/nipype/external/portalocker.py b/nipype/external/portalocker.py index 40b12b3cf3..2b7e60e2a5 100644 --- a/nipype/external/portalocker.py +++ b/nipype/external/portalocker.py @@ -1,6 +1,6 @@ # portalocker.py - Cross-platform (posix/nt) API for flock-style file locking. # Requires python 1.5.2 or better. -'''Cross-platform (posix/nt) API for flock-style file locking. +"""Cross-platform (posix/nt) API for flock-style file locking. Synopsis: @@ -47,7 +47,7 @@ Lowell Alleman Version: $Id: portalocker.py 5474 2008-05-16 20:53:50Z lowell $ -''' +""" from __future__ import print_function from __future__ import absolute_import diff --git a/nipype/fixes/numpy/testing/noseclasses.py b/nipype/fixes/numpy/testing/noseclasses.py index 9f69dc33db..aaa7350eda 100644 --- a/nipype/fixes/numpy/testing/noseclasses.py +++ b/nipype/fixes/numpy/testing/noseclasses.py @@ -304,16 +304,16 @@ def configure(self, options, config): class KnownFailureTest(Exception): - '''Raise this exception to mark a test as a known failing test.''' + """Raise this exception to mark a test as a known failing test.""" pass class KnownFailure(ErrorClassPlugin): - '''Plugin that installs a KNOWNFAIL error class for the + """Plugin that installs a KNOWNFAIL error class for the KnownFailureClass exception. When KnownFailureTest is raised, the exception will be logged in the knownfail attribute of the result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the - exception will not be counted as an error or failure.''' + exception will not be counted as an error or failure.""" enabled = True knownfail = ErrorClass(KnownFailureTest, label='KNOWNFAIL', diff --git a/nipype/fixes/numpy/testing/nosetester.py b/nipype/fixes/numpy/testing/nosetester.py index 22c8d1a5ef..2a5c92333b 100644 --- a/nipype/fixes/numpy/testing/nosetester.py +++ b/nipype/fixes/numpy/testing/nosetester.py @@ -126,7 +126,7 @@ class NoseTester(object): 'swig_ext'] def __init__(self, package=None): - ''' Test class init + """ Test class init Parameters ---------- @@ -134,7 +134,7 @@ def __init__(self, package=None): If string, gives full path to package If None, extract calling module path Default is None - ''' + """ package_name = None if package is None: f = sys._getframe(1) @@ -158,7 +158,7 @@ def __init__(self, package=None): self.package_name = package_name def _test_argv(self, label, verbose, extra_argv): - ''' Generate argv for nosetest command + """ Generate argv for nosetest command Parameters ---------- @@ -173,7 +173,7 @@ def _test_argv(self, label, verbose, extra_argv): ------- argv : list command line arguments that will be passed to nose - ''' + """ argv = [__file__, self.package_path, '-s'] if label and label != 'full': if not isinstance(label, string_types): diff --git a/nipype/interfaces/afni/base.py b/nipype/interfaces/afni/base.py index ffe9f230b5..1f1fb48474 100644 --- a/nipype/interfaces/afni/base.py +++ b/nipype/interfaces/afni/base.py @@ -3,25 +3,18 @@ """Provide interface to AFNI commands.""" import os -from sys import platform from builtins import object from ... import logging -from ...utils.filemanip import split_filename -from ..base import ( - CommandLine, traits, CommandLineInputSpec, isdefined, File, TraitedSpec) +from ..base import traits, File, CommandLine, CommandLineInputSpec, TraitedSpec # Use nipype's logging system IFLOGGER = logging.getLogger('interface') +AFNI_FTYPES = {'NIFTI': '.nii', 'AFNI': '', 'NIFTI_GZ': '.nii.gz'} class Info(object): - """Handle afni output type and version information. - """ - __outputtype = 'AFNI' - ftypes = {'NIFTI': '.nii', - 'AFNI': '', - 'NIFTI_GZ': '.nii.gz'} + """Handle afni output type and version information. """ @staticmethod def version(): @@ -47,67 +40,30 @@ def version(): # If afni_vcheck is not present, return None IFLOGGER.warn('afni_vcheck executable not found.') return None - except RuntimeError as e: + except RuntimeError as err: # If AFNI is outdated, afni_vcheck throws error. # Show new version, but parse current anyways. - currv = str(e).split('\n')[4].split('=', 1)[1].strip() - nextv = str(e).split('\n')[6].split('=', 1)[1].strip() + currv = str(err).split('\n')[4].split('=', 1)[1].strip() + nextv = str(err).split('\n')[6].split('=', 1)[1].strip() IFLOGGER.warn( - 'AFNI is outdated, detected version %s and %s is available.' % (currv, nextv)) + 'AFNI is outdated, detected version %s and %s is available.', currv, nextv) if currv.startswith('AFNI_'): currv = currv[5:] - v = currv.split('.') + version = currv.split('.') try: - v = [int(n) for n in v] + version = [int(n) for n in version] except ValueError: return currv - return tuple(v) - - @classmethod - def outputtype_to_ext(cls, outputtype): - """Get the file extension for the given output type. - - Parameters - ---------- - outputtype : {'NIFTI', 'NIFTI_GZ', 'AFNI'} - String specifying the output type. - - Returns - ------- - extension : str - The file extension for the output type. - """ - - try: - return cls.ftypes[outputtype] - except KeyError: - msg = 'Invalid AFNIOUTPUTTYPE: ', outputtype - raise KeyError(msg) - - @classmethod - def outputtype(cls): - """AFNI has no environment variables, - Output filetypes get set in command line calls - Nipype uses AFNI as default - - - Returns - ------- - None - """ - # warn(('AFNI has no environment variable that sets filetype ' - # 'Nipype uses NIFTI_GZ as default')) - return 'AFNI' + return tuple(version) @staticmethod def standard_image(img_name): - '''Grab an image from the standard location. + """Grab an image from the standard location. - Could be made more fancy to allow for more relocatability''' - clout = CommandLine('which afni', - terminal_output='allatonce').run() + Could be made more fancy to allow for more relocatability""" + clout = CommandLine('which afni', terminal_output='allatonce').run() if clout.runtime.returncode is not 0: return None @@ -122,79 +78,22 @@ class AFNICommandBase(CommandLine): See http://afni.nimh.nih.gov/afni/community/board/read.php?1,145346,145347#msg-145347 """ def _run_interface(self, runtime): - if platform == 'darwin': + if runtime.platform == 'darwin': runtime.environ['DYLD_FALLBACK_LIBRARY_PATH'] = '/usr/local/afni/' return super(AFNICommandBase, self)._run_interface(runtime) class AFNICommandInputSpec(CommandLineInputSpec): - outputtype = traits.Enum('AFNI', list(Info.ftypes.keys()), - desc='AFNI output filetype') - out_file = File(name_template="%s_afni", desc='output image file name', - argstr='-prefix %s', - name_source=["in_file"]) - + outputtype = traits.Trait('AFNI', AFNI_FTYPES, usedefault=True, + desc='AFNI output filetype') class AFNICommandOutputSpec(TraitedSpec): - out_file = File(desc='output file', - exists=True) + out_file = File(exists=True, desc='output file') class AFNICommand(AFNICommandBase): """Shared options for several AFNI commands """ input_spec = AFNICommandInputSpec - _outputtype = None - - def __init__(self, **inputs): - super(AFNICommand, self).__init__(**inputs) - self.inputs.on_trait_change(self._output_update, 'outputtype') - - if self._outputtype is None: - self._outputtype = Info.outputtype() - - if not isdefined(self.inputs.outputtype): - self.inputs.outputtype = self._outputtype - else: - self._output_update() - - def _output_update(self): - """ i think? updates class private attribute based on instance input - in fsl also updates ENVIRON variable....not valid in afni - as it uses no environment variables - """ - self._outputtype = self.inputs.outputtype - - @classmethod - def set_default_output_type(cls, outputtype): - """Set the default output type for AFNI classes. - - This method is used to set the default output type for all afni - subclasses. However, setting this will not update the output - type for any existing instances. For these, assign the - .inputs.outputtype. - """ - - if outputtype in Info.ftypes: - cls._outputtype = outputtype - else: - raise AttributeError('Invalid AFNI outputtype: %s' % outputtype) - - def _overload_extension(self, value, name=None): - path, base, _ = split_filename(value) - return os.path.join(path, base + Info.outputtype_to_ext(self.inputs.outputtype)) - - def _list_outputs(self): - outputs = super(AFNICommand, self)._list_outputs() - metadata = dict(name_source=lambda t: t is not None) - out_names = list(self.inputs.traits(**metadata).keys()) - if out_names: - for name in out_names: - if outputs[name]: - _, _, ext = split_filename(outputs[name]) - if ext == "": - outputs[name] = outputs[name] + "+orig.BRIK" - return outputs - def no_afni(): """ Checks if AFNI is available """ diff --git a/nipype/interfaces/afni/preprocess.py b/nipype/interfaces/afni/preprocess.py index c88fa02506..051a38c150 100644 --- a/nipype/interfaces/afni/preprocess.py +++ b/nipype/interfaces/afni/preprocess.py @@ -14,18 +14,19 @@ import re import numpy as np -from .base import (AFNICommandBase, AFNICommand, AFNICommandInputSpec, AFNICommandOutputSpec, - Info, no_afni) -from ..base import CommandLineInputSpec -from ..base import (Directory, TraitedSpec, - traits, isdefined, File, InputMultiPath, Undefined) +from ..base import (Directory, traits, isdefined, File, GenFile, Undefined, + CommandLineInputSpec, TraitedSpec, InputMultiPath) +from .base import (AFNICommandBase, AFNICommand, AFNICommandInputSpec, + AFNICommandOutputSpec, Info, no_afni) from ...external.six import string_types from ...utils.filemanip import (load_json, save_json, split_filename) class To3DInputSpec(AFNICommandInputSpec): - out_file = File(name_template="%s", desc='output image file name', - argstr='-prefix %s', name_source=["in_folder"]) + prefix = traits.Str('afni_to3d', usedefault='true', argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template='{prefix}{outputtype_}', keep_extension=False, + desc='output image file name') in_folder = Directory(desc='folder with DICOM images to convert', argstr='%s/*.dcm', position=-1, @@ -53,6 +54,7 @@ class To3DInputSpec(AFNICommandInputSpec): class To3D(AFNICommand): + """Create a 3D dataset from 2D image files using AFNI to3d command For complete details, see the `to3d Documentation @@ -68,7 +70,7 @@ class To3D(AFNICommand): >>> To3D.inputs.out_file = 'dicomdir.nii' >>> To3D.inputs.filetype = "anat" >>> To3D.cmdline #doctest: +ELLIPSIS - 'to3d -datum float -anat -prefix dicomdir.nii ./*.dcm' + 'to3d -datum float -anat -prefix afni_to3d ./*.dcm' >>> res = To3D.run() #doctest: +SKIP """ @@ -79,48 +81,29 @@ class To3D(AFNICommand): class TShiftInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dTShift', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_tshift", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - - tr = traits.Str(desc='manually set the TR' + - 'You can attach suffix "s" for seconds or "ms" for milliseconds.', - argstr='-TR %s') - - tzero = traits.Float(desc='align each slice to given time offset', - argstr='-tzero %s', - xor=['tslice']) - - tslice = traits.Int(desc='align each slice to time offset of given slice', - argstr='-slice %s', - xor=['tzero']) - + in_file = File(desc='input file to 3dTShift', argstr='%s', position=-1, mandatory=True, + exists=True, copyfile=False) + prefix = GenFile(template='{in_file}_tshift', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template='{prefix}{outputtype_}', desc='output image file name') + tr = traits.Str(argstr='-TR %s', desc='manually set the TR You can attach suffix "s" for ' + 'seconds or "ms" for milliseconds.') + tzero = traits.Float(argstr='-tzero %s', xor=['tslice'], + desc='align each slice to given time offset') + tslice = traits.Int(argstr='-slice %s', xor=['tzero'], + desc='align each slice to time offset of given slice') ignore = traits.Int(desc='ignore the first set of points specified', argstr='-ignore %s') - - interp = traits.Enum(('Fourier', 'linear', 'cubic', 'quintic', 'heptic'), - desc='different interpolation methods (see 3dTShift for details)' + - ' default = Fourier', argstr='-%s') - - tpattern = traits.Str(desc='use specified slice time pattern rather than one in header', - argstr='-tpattern %s') - - rlt = traits.Bool(desc='Before shifting, remove the mean and linear trend', - argstr="-rlt") - - rltplus = traits.Bool(desc='Before shifting,' + - ' remove the mean and linear trend and ' + - 'later put back the mean', - argstr="-rlt+") - + interp = traits.Enum('Fourier', 'linear', 'cubic', 'quintic', 'heptic', argstr='-%s', + desc='different interpolation methods (see 3dTShift for details)') + tpattern = traits.Str(argstr='-tpattern %s', + desc='use specified slice time pattern rather than one in header') + rlt = traits.Bool(argstr="-rlt", desc='Before shifting, remove the mean and linear trend') + rltplus = traits.Bool(argstr="-rlt+", desc='Before shifting, remove the mean and linear trend' + ' and later put back the mean') class TShift(AFNICommand): + """Shifts voxel time series from input so that seperate slices are aligned to the same temporal origin @@ -147,39 +130,25 @@ class TShift(AFNICommand): output_spec = AFNICommandOutputSpec -class RefitInputSpec(CommandLineInputSpec): - in_file = File(desc='input file to 3drefit', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=True) - - deoblique = traits.Bool(desc='replace current transformation' + - ' matrix with cardinal matrix', - argstr='-deoblique') - - xorigin = traits.Str(desc='x distance for edge voxel offset', - argstr='-xorigin %s') - - yorigin = traits.Str(desc='y distance for edge voxel offset', - argstr='-yorigin %s') - zorigin = traits.Str(desc='z distance for edge voxel offset', - argstr='-zorigin %s') - - xdel = traits.Float(desc='new x voxel dimension in mm', - argstr='-xdel %f') - - ydel = traits.Float(desc='new y voxel dimension in mm', - argstr='-ydel %f') - - zdel = traits.Float(desc='new z voxel dimension in mm', - argstr='-zdel %f') - - space = traits.Enum('TLRC', 'MNI', 'ORIG', - argstr='-space %s', - desc='Associates the dataset with a specific' + - ' template type, e.g. TLRC, MNI, ORIG') +class RefitInputSpec(AFNICommandInputSpec): + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, + copyfile=True, desc='input file to 3drefit') + out_file = GenFile(template='{in_file}{outputtype_}', desc='output file') + deoblique = traits.Bool( + False, usedefault=True, argstr='-deoblique', + desc='replace current transformation matrix with cardinal matrix') + xorigin = traits.Str(argstr='-xorigin %s', desc='x distance for edge voxel offset') + yorigin = traits.Str(argstr='-yorigin %s', desc='y distance for edge voxel offset') + zorigin = traits.Str(argstr='-zorigin %s', desc='z distance for edge voxel offset') + xdel = traits.Float(argstr='-xdel %f', desc='new x voxel dimension in mm') + ydel = traits.Float(argstr='-ydel %f', desc='new y voxel dimension in mm') + zdel = traits.Float(argstr='-zdel %f', desc='new z voxel dimension in mm') + space = traits.Enum( + 'TLRC', 'MNI', 'ORIG', argstr='-space %s', + desc='Associates the dataset with a specific template type, e.g. TLRC, MNI, ORIG') + +class RefitOutputSpec(TraitedSpec): + out_file = File(exists=True, desc='output file') class Refit(AFNICommandBase): @@ -203,56 +172,33 @@ class Refit(AFNICommandBase): _cmd = '3drefit' input_spec = RefitInputSpec - output_spec = AFNICommandOutputSpec - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["out_file"] = os.path.abspath(self.inputs.in_file) - return outputs + output_spec = RefitOutputSpec class WarpInputSpec(AFNICommandInputSpec): - - in_file = File(desc='input file to 3dWarp', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_warp", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dWarp') + prefix = GenFile(template='{in_file}_warp', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template="{prefix}{outputtype_}", desc='output image file name') tta2mni = traits.Bool(desc='transform dataset from Talairach to MNI152', argstr='-tta2mni') - mni2tta = traits.Bool(desc='transform dataset from MNI152 to Talaraich', argstr='-mni2tta') - matparent = File(desc="apply transformation from 3dWarpDrive", - argstr="-matparent %s", - exists=True) - + argstr="-matparent %s", exists=True) deoblique = traits.Bool(desc='transform dataset from oblique to cardinal', argstr='-deoblique') - - interp = traits.Enum(('linear', 'cubic', 'NN', 'quintic'), - desc='spatial interpolation methods [default = linear]', - argstr='-%s') - - gridset = File(desc="copy grid of specified dataset", - argstr="-gridset %s", - exists=True) - - newgrid = traits.Float(desc="specify grid of this size (mm)", - argstr="-newgrid %f") - - zpad = traits.Int(desc="pad input dataset with N planes" + - " of zero on all sides.", + interp = traits.Enum('linear', 'cubic', 'NN', 'quintic', argstr='-%s', + desc='spatial interpolation methods [default = linear]') + gridset = File(desc="copy grid of specified dataset", argstr="-gridset %s", exists=True) + newgrid = traits.Float(desc="specify grid of this size (mm)", argstr="-newgrid %f") + zpad = traits.Int(desc="pad input dataset with N planes of zero on all sides.", argstr="-zpad %d") class Warp(AFNICommand): + """Use 3dWarp for spatially transforming a dataset For complete details, see the `3dWarp Documentation. @@ -265,16 +211,14 @@ class Warp(AFNICommand): >>> warp = afni.Warp() >>> warp.inputs.in_file = 'structural.nii' >>> warp.inputs.deoblique = True - >>> warp.inputs.out_file = "trans.nii.gz" >>> warp.cmdline - '3dWarp -deoblique -prefix trans.nii.gz structural.nii' + '3dWarp -deoblique -prefix structural_warp structural.nii' >>> warp_2 = afni.Warp() >>> warp_2.inputs.in_file = 'structural.nii' >>> warp_2.inputs.newgrid = 1.0 - >>> warp_2.inputs.out_file = "trans.nii.gz" >>> warp_2.cmdline - '3dWarp -newgrid 1.000000 -prefix trans.nii.gz structural.nii' + '3dWarp -newgrid 1.000000 -prefix structural_warp structural.nii' """ @@ -284,33 +228,25 @@ class Warp(AFNICommand): class ResampleInputSpec(AFNICommandInputSpec): - - in_file = File(desc='input file to 3dresample', - argstr='-inset %s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_resample", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - - orientation = traits.Str(desc='new orientation code', - argstr='-orient %s') - - resample_mode = traits.Enum('NN', 'Li', 'Cu', 'Bk', - argstr='-rmode %s', - desc="resampling method from set {'NN', 'Li', 'Cu', 'Bk'}. These are for 'Nearest Neighbor', 'Linear', 'Cubic' and 'Blocky' interpolation, respectively. Default is NN.") - - voxel_size = traits.Tuple(*[traits.Float()] * 3, - argstr='-dxyz %f %f %f', + in_file = File(argstr='-inset %s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dresample') + prefix = GenFile(template='{in_file}_resample', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template="{prefix}{outputtype_}", keep_extension=False, + desc='output image file name') + orientation = traits.Str(desc='new orientation code', argstr='-orient %s') + resample_mode = traits.Enum( + 'NN', 'Li', 'Cu', 'Bk', argstr='-rmode %s', + desc="resampling method from set {'NN', 'Li', 'Cu', 'Bk'}. These are for 'Nearest " + "Neighbor', 'Linear', 'Cubic' and 'Blocky' interpolation, respectively.") + voxel_size = traits.Tuple(*[traits.Float()] * 3, argstr='-dxyz %f %f %f', desc="resample to new dx, dy and dz") - master = traits.File(argstr='-master %s', desc='align dataset grid to a reference file') class Resample(AFNICommand): + """Resample or reorient an image using AFNI 3dresample command For complete details, see the `3dresample Documentation. @@ -325,7 +261,7 @@ class Resample(AFNICommand): >>> resample.inputs.orientation= 'RPI' >>> resample.inputs.outputtype = "NIFTI" >>> resample.cmdline - '3dresample -orient RPI -prefix functional_resample.nii -inset functional.nii' + '3dresample -orient RPI -prefix functional_resample -inset functional.nii' >>> res = resample.run() # doctest: +SKIP """ @@ -336,33 +272,23 @@ class Resample(AFNICommand): class AutoTcorrelateInputSpec(AFNICommandInputSpec): - in_file = File(desc='timeseries x space (volume or surface) file', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - polort = traits.Int( - desc='Remove polynomical trend of order m or -1 for no detrending', - argstr="-polort %d") - eta2 = traits.Bool(desc='eta^2 similarity', - argstr="-eta2") - mask = File(exists=True, desc="mask of voxels", - argstr="-mask %s") - mask_only_targets = traits.Bool(desc="use mask only on targets voxels", - argstr="-mask_only_targets", - xor=['mask_source']) - mask_source = File(exists=True, - desc="mask for source voxels", - argstr="-mask_source %s", - xor=['mask_only_targets']) - - out_file = File(name_template="%s_similarity_matrix.1D", desc='output image file name', - argstr='-prefix %s', name_source="in_file") + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='timeseries x space (volume or surface) file') + polort = traits.Int(argstr="-polort %d", + desc='Remove polynomical trend of order m or -1 for no detrending') + eta2 = traits.Bool(desc='eta^2 similarity', argstr="-eta2") + mask = File(exists=True, desc="mask of voxels", argstr="-mask %s") + mask_only_targets = traits.Bool( + False, usedefault=True, argstr="-mask_only_targets", xor=['mask_source'], + desc="use mask only on targets voxels") + mask_source = File(exists=True, argstr="-mask_source %s", xor=['mask_only_targets'], + desc="mask for source voxels") + out_file = GenFile(template="{in_file}_similarity_matrix.1D", argstr='-prefix %s', + keep_extension=False, desc='output image file name') class AutoTcorrelate(AFNICommand): + """Computes the correlation coefficient between the time series of each pair of voxels in the input dataset, and stores the output into a new anatomical bucket dataset [scaled to shorts to save memory space]. @@ -385,32 +311,20 @@ class AutoTcorrelate(AFNICommand): output_spec = AFNICommandOutputSpec _cmd = '3dAutoTcorrelate' - def _overload_extension(self, value, name=None): - path, base, ext = split_filename(value) - if ext.lower() not in [".1d", ".nii.gz", ".nii"]: - ext = ext + ".1D" - return os.path.join(path, base + ext) - class TStatInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dTstat', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_tstat", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - - mask = File(desc='mask file', - argstr='-mask %s', - exists=True) - options = traits.Str(desc='selected statistical output', - argstr='%s') + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dTstat') + prefix = GenFile(template='{in_file}_tstat', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template="{in_file}{outputtype_}", keep_extension=False, + desc='output image file name') + mask = File(desc='mask file', argstr='-mask %s', exists=True) + options = traits.Str(desc='selected statistical output', argstr='%s') class TStat(AFNICommand): + """Compute voxel-wise statistics using AFNI 3dTstat command For complete details, see the `3dTstat Documentation. @@ -425,7 +339,7 @@ class TStat(AFNICommand): >>> tstat.inputs.args= '-mean' >>> tstat.inputs.out_file = "stats" >>> tstat.cmdline - '3dTstat -mean -prefix stats functional.nii' + '3dTstat -mean -prefix functional_tstat functional.nii' >>> res = tstat.run() # doctest: +SKIP """ @@ -436,18 +350,16 @@ class TStat(AFNICommand): class DetrendInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dDetrend', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_detrend", desc='output image file name', - argstr='-prefix %s', name_source="in_file") + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dDetrend') + prefix = GenFile(template='{in_file}_detrend', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template="{prefix}{outputtype_}", keep_extension=False, + desc='output image file name') class Detrend(AFNICommand): + """This program removes components from voxel time series using linear least squares @@ -474,18 +386,16 @@ class Detrend(AFNICommand): class DespikeInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dDespike', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_despike", desc='output image file name', - argstr='-prefix %s', name_source="in_file") + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dDespike') + prefix = GenFile(template='{in_file}_despike', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template="{prefix}{outputtype_}", keep_extension=False, + desc='output image file name') class Despike(AFNICommand): + """Removes 'spikes' from the 3D+time input dataset For complete details, see the `3dDespike Documentation. @@ -509,41 +419,26 @@ class Despike(AFNICommand): class AutomaskInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dAutomask', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - - out_file = File(name_template="%s_mask", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - - brain_file = File(name_template="%s_masked", - desc="output file from 3dAutomask", - argstr='-apply_prefix %s', - name_source="in_file") - - clfrac = traits.Float(desc='sets the clip level fraction' + - ' (must be 0.1-0.9). ' + - 'A small value will tend to make the mask larger [default = 0.5].', - argstr="-clfrac %s") - - dilate = traits.Int(desc='dilate the mask outwards', - argstr="-dilate %s") - - erode = traits.Int(desc='erode the mask inwards', - argstr="-erode %s") + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dAutomask',) + out_file = GenFile(template="{in_file}_mask", argstr='-prefix %s', + desc='output image file name') + brain_file = GenFile(template="{in_file}_masked", argstr='-apply_prefix %s', + desc="output file from 3dAutomask") + clfrac = traits.Range(low=0.1, high=0.9, argstr='-clfrac %.2f', + desc='sets the clip level fraction. A small value will tend ' + 'to make the mask larger [default = 0.5].') + dilate = traits.Int(argstr="-dilate %s", desc='dilate the mask outwards') + erode = traits.Int(argstr="-erode %s", desc='erode the mask inwards') class AutomaskOutputSpec(TraitedSpec): - out_file = File(desc='mask file', - exists=True) - + out_file = File(exists=True, desc='mask file') brain_file = File(desc='brain file (skull stripped)', exists=True) class Automask(AFNICommand): + """Create a brain-only mask of the image using AFNI 3dAutomask command For complete details, see the `3dAutomask Documentation. @@ -569,51 +464,38 @@ class Automask(AFNICommand): class VolregInputSpec(AFNICommandInputSpec): - - in_file = File(desc='input file to 3dvolreg', - argstr='%s', - position=-1, - mandatory=True, - exists=True, - copyfile=False) - out_file = File(name_template="%s_volreg", desc='output image file name', - argstr='-prefix %s', name_source="in_file") - basefile = File(desc='base file for registration', - argstr='-base %s', - position=-6, - exists=True) - zpad = traits.Int(desc='Zeropad around the edges' + - ' by \'n\' voxels during rotations', - argstr='-zpad %d', - position=-5) - md1d_file = File(name_template='%s_md.1D', desc='max displacement output file', - argstr='-maxdisp1D %s', name_source="in_file", - keep_extension=True, position=-4) - oned_file = File(name_template='%s.1D', desc='1D movement parameters output file', - argstr='-1Dfile %s', - name_source="in_file", - keep_extension=True) + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, copyfile=False, + desc='input file to 3dvolreg') + out_file = GenFile(template="{in_file}_volreg", argstr='-prefix %s', + desc='output image file name') + basefile = File(argstr='-base %s', position=-6, exists=True, + desc='base file for registration') + zpad = traits.Int(argstr='-zpad %d', position=-5, + desc='Zeropad around the edges by \'n\' voxels during rotations') + md1d_file = GenFile(template='{in_file}_md.1D', argstr='-maxdisp1D %s', keep_extension=False, + position=-4, desc='max displacement output file') + oned_file = GenFile(template='{in_file}.1D', argstr='-1Dfile %s', keep_extension=False, + desc='1D movement parameters output file') verbose = traits.Bool(desc='more detailed description of the process', argstr='-verbose') timeshift = traits.Bool(desc='time shift to mean slice time offset', argstr='-tshift 0') copyorigin = traits.Bool(desc='copy base file origin coords to output', argstr='-twodup') - oned_matrix_save = File(name_template='%s.aff12.1D', - desc='Save the matrix transformation', - argstr='-1Dmatrix_save %s', - keep_extension=True, - name_source="in_file") + oned_matrix_save = GenFile(template='{in_file}.aff12.1D', argstr='-1Dmatrix_save %s', + keep_extension=False, desc='Save the matrix transformation') class VolregOutputSpec(TraitedSpec): out_file = File(desc='registered file', exists=True) md1d_file = File(desc='max displacement info file', exists=True) oned_file = File(desc='movement parameters info file', exists=True) - oned_matrix_save = File(desc='matrix transformation from base to input', exists=True) + oned_matrix_save = File( + desc='matrix transformation from base to input', exists=True) class Volreg(AFNICommand): + """Register input volumes to a base volume using AFNI 3dvolreg command For complete details, see the `3dvolreg Documentation. @@ -646,8 +528,8 @@ class MergeInputSpec(AFNICommandInputSpec): position=-1, mandatory=True, copyfile=False) - out_file = File(name_template="%s_merge", desc='output image file name', - argstr='-prefix %s', name_source="in_file") + out_file = GenFile(template="{in_file}_merge", argstr='-prefix %s', + desc='output image file name') doall = traits.Bool(desc='apply options to all sub-bricks in dataset', argstr='-doall') blurfwhm = traits.Int(desc='FWHM blur value (mm)', @@ -656,6 +538,7 @@ class MergeInputSpec(AFNICommandInputSpec): class Merge(AFNICommand): + """Merge or edit volumes using AFNI 3dmerge command For complete details, see the `3dmerge Documentation. @@ -680,17 +563,15 @@ class Merge(AFNICommand): class CopyInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dcopy', - argstr='%s', - position=-2, - mandatory=True, - exists=True, - copyfile=False) - out_file = File(name_template="%s_copy", desc='output image file name', - argstr='%s', position=-1, name_source="in_file") + in_file = File(desc='input file to 3dcopy', argstr='%s', position=-2, mandatory=True, + exists=True, copyfile=False) + out_file = GenFile( + template='{in_file}_copy{outputtype_}', position=-1, + argstr='%s', desc='output image file name') class Copy(AFNICommand): + """Copies an image of one type to an image of the same or different type using 3dcopy command @@ -707,17 +588,20 @@ class Copy(AFNICommand): '3dcopy functional.nii functional_copy' >>> from copy import deepcopy - >>> copy3d_2 = deepcopy(copy3d) + >>> copy3d_2 = afni.Copy() + >>> copy3d_2.inputs.in_file = 'functional.nii' >>> copy3d_2.inputs.outputtype = 'NIFTI' >>> copy3d_2.cmdline '3dcopy functional.nii functional_copy.nii' - >>> copy3d_3 = deepcopy(copy3d) + >>> copy3d_3 = afni.Copy() + >>> copy3d_3.inputs.in_file = 'functional.nii' >>> copy3d_3.inputs.outputtype = 'NIFTI_GZ' >>> copy3d_3.cmdline '3dcopy functional.nii functional_copy.nii.gz' - >>> copy3d_4 = deepcopy(copy3d) + >>> copy3d_4 = afni.Copy() + >>> copy3d_4.inputs.in_file = 'functional.nii' >>> copy3d_4.inputs.out_file = 'new_func.nii' >>> copy3d_4.cmdline '3dcopy functional.nii new_func.nii' @@ -748,6 +632,7 @@ class FourierInputSpec(AFNICommandInputSpec): class Fourier(AFNICommand): + """Program to lowpass and/or highpass each voxel time series in a dataset, via the FFT @@ -858,6 +743,7 @@ class BandpassInputSpec(AFNICommandInputSpec): class Bandpass(AFNICommand): + """Program to lowpass and/or highpass each voxel time series in a dataset, offering more/different options than Fourier @@ -896,6 +782,7 @@ class ZCutUpInputSpec(AFNICommandInputSpec): class ZCutUp(AFNICommand): + """Cut z-slices from a volume using AFNI 3dZcutup command For complete details, see the `3dZcutup Documentation. @@ -934,7 +821,8 @@ class AllineateInputSpec(AFNICommandInputSpec): desc='output file from 3dAllineate', argstr='-prefix %s', position=-2, - name_source='%s_allineate', + name_source='in_file', + name_template='%s_allineate', genfile=True) out_param_file = File( @@ -1110,12 +998,20 @@ class AllineateInputSpec(AFNICommandInputSpec): desc='To fix non-linear warp dependency along directions.') + def _format_arg(self, name, trait_spec, value): + if name == 'nwarp_fixmot' or name == 'nwarp_fixdep': + arg = ' '.join([trait_spec.argstr % v for v in value]) + return arg + return super(AllineateInputSpec, self)._format_arg(name, trait_spec, value) + + class AllineateOutputSpec(TraitedSpec): out_file = File(desc='output image file name') matrix = File(desc='matrix to align input file') class Allineate(AFNICommand): + """Program to align one dataset (the 'source') to a base dataset For complete details, see the `3dAllineate Documentation. @@ -1137,36 +1033,13 @@ class Allineate(AFNICommand): input_spec = AllineateInputSpec output_spec = AllineateOutputSpec - def _format_arg(self, name, trait_spec, value): - if name == 'nwarp_fixmot' or name == 'nwarp_fixdep': - arg = ' '.join([trait_spec.argstr % v for v in value]) - return arg - return super(Allineate, self)._format_arg(name, trait_spec, value) - - def _list_outputs(self): - outputs = self.output_spec().get() - if not isdefined(self.inputs.out_file): - outputs['out_file'] = self._gen_filename(self.inputs.in_file, - suffix=self.inputs.suffix) - else: - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()[name] - class MaskaveInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dmaskave', - argstr='%s', - position=-2, - mandatory=True, - exists=True, - copyfile=False) - out_file = File(name_template="%s_maskave.1D", desc='output image file name', - keep_extension=True, - argstr="> %s", name_source="in_file", position=-1) + in_file = File(argstr='%s', position=-2, mandatory=True, exists=True, + copyfile=False, desc='input file to 3dmaskave') + out_file = GenFile( + template="{in_file}_maskave.1D", keep_extension=False, argstr="> %s", position=-1, + desc='output image file name',) mask = File(desc='matrix to align input file', argstr='-mask %s', position=1, @@ -1177,6 +1050,7 @@ class MaskaveInputSpec(AFNICommandInputSpec): class Maskave(AFNICommand): + """Computes average of all voxels in the input dataset which satisfy the criterion in the options list @@ -1214,6 +1088,7 @@ class SkullStripInputSpec(AFNICommandInputSpec): class SkullStrip(AFNICommand): + """A program to extract the brain from surrounding tissue from MRI T1-weighted images @@ -1259,6 +1134,7 @@ class TCatInputSpec(AFNICommandInputSpec): class TCat(AFNICommand): + """Concatenate sub-bricks from input datasets into one big 3D+time dataset @@ -1303,6 +1179,7 @@ class FimInputSpec(AFNICommandInputSpec): class Fim(AFNICommand): + """Program to calculate the cross-correlation of an ideal reference waveform with the measured FMRI time series for each voxel @@ -1353,6 +1230,7 @@ class TCorrelateInputSpec(AFNICommandInputSpec): class TCorrelate(AFNICommand): + """Computes the correlation coefficient between corresponding voxel time series in two input 3D+time datasets 'xset' and 'yset' @@ -1366,7 +1244,6 @@ class TCorrelate(AFNICommand): >>> tcorrelate = afni.TCorrelate() >>> tcorrelate.inputs.xset= 'u_rc1s1_Template.nii' >>> tcorrelate.inputs.yset = 'u_rc1s2_Template.nii' - >>> tcorrelate.inputs.out_file = 'functional_tcorrelate.nii.gz' >>> tcorrelate.inputs.polort = -1 >>> tcorrelate.inputs.pearson = True >>> res = tcarrelate.run() # doctest: +SKIP @@ -1379,22 +1256,14 @@ class TCorrelate(AFNICommand): class TCorr1DInputSpec(AFNICommandInputSpec): - xset = File(desc='3d+time dataset input', - argstr=' %s', - position=-2, - mandatory=True, - exists=True, - copyfile=False) - y_1d = File(desc='1D time series file input', - argstr=' %s', - position=-1, - mandatory=True, - exists=True) - out_file = File(desc='output filename prefix', - name_template='%s_correlation.nii.gz', - argstr='-prefix %s', - name_source='xset', - keep_extension=True) + xset = File(argstr='%s', position=-2, mandatory=True, exists=True, + copyfile=False, desc='3d+time dataset input') + prefix = GenFile(template='{xset}_correlation', keep_extension=False, + argstr='-prefix %s', desc='output files prefix') + y_1d = File(argstr='%s', position=-1, mandatory=True, exists=True, + desc='1D time series file input') + out_file = File(name_template='{prefix}{outputtype_}', keep_extension=False, + desc='output filename prefix') pearson = traits.Bool(desc='Correlation is the normal' + ' Pearson correlation coefficient', argstr=' -pearson', @@ -1423,6 +1292,7 @@ class TCorr1DOutputSpec(TraitedSpec): class TCorr1D(AFNICommand): + """Computes the correlation coefficient between each voxel time series in the input 3D+time dataset. For complete details, see the `3dTcorr1D Documentation. @@ -1433,7 +1303,7 @@ class TCorr1D(AFNICommand): >>> tcorr1D.inputs.xset= 'u_rc1s1_Template.nii' >>> tcorr1D.inputs.y_1d = 'seed.1D' >>> tcorr1D.cmdline - '3dTcorr1D -prefix u_rc1s1_Template_correlation.nii.gz u_rc1s1_Template.nii seed.1D' + '3dTcorr1D -prefix u_rc1s1_Template_correlation u_rc1s1_Template.nii seed.1D' >>> res = tcorr1D.run() # doctest: +SKIP """ @@ -1443,20 +1313,12 @@ class TCorr1D(AFNICommand): class BrickStatInputSpec(AFNICommandInputSpec): - in_file = File(desc='input file to 3dmaskave', - argstr='%s', - position=-1, - mandatory=True, - exists=True) - - mask = File(desc='-mask dset = use dset as mask to include/exclude voxels', - argstr='-mask %s', - position=2, - exists=True) - - min = traits.Bool(desc='print the minimum value in dataset', - argstr='-min', - position=1) + in_file = File(argstr='%s', position=-1, mandatory=True, exists=True, + desc='input file to 3dmaskave') + mask = File(argstr='-mask %s', position=2, exists=True, + desc='-mask dset = use dset as mask to include/exclude voxels') + min = traits.Bool(argstr='-min', position=1, + desc='print the minimum value in dataset') class BrickStatOutputSpec(TraitedSpec): @@ -1464,6 +1326,7 @@ class BrickStatOutputSpec(TraitedSpec): class BrickStat(AFNICommand): + """Compute maximum and/or minimum voxel values of an input dataset For complete details, see the `3dBrickStat Documentation. @@ -1510,7 +1373,6 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): save_json(outfile, dict(stat=min_val)) outputs.min_val = min_val - return outputs class ROIStatsInputSpec(CommandLineInputSpec): @@ -1575,7 +1437,6 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): f.close() outputs.stats = os.path.abspath(output_filename) - return outputs class CalcInputSpec(AFNICommandInputSpec): @@ -1596,8 +1457,20 @@ class CalcInputSpec(AFNICommandInputSpec): single_idx = traits.Int(desc='volume index for in_file_a') other = File(desc='other options', argstr='') + def _format_arg(self, name, trait_spec, value): + if name == 'in_file_a': + arg = trait_spec.argstr % value + if isdefined(self.start_idx): + arg += '[%d..%d]' % (self.start_idx, + self.stop_idx) + if isdefined(self.single_idx): + arg += '[%d]' % (self.single_idx) + return arg + return super(CalcInputSpec, self)._format_arg(name, trait_spec, value) + class Calc(AFNICommand): + """This program does voxel-by-voxel arithmetic on 3D datasets For complete details, see the `3dcalc Documentation. @@ -1622,23 +1495,6 @@ class Calc(AFNICommand): input_spec = CalcInputSpec output_spec = AFNICommandOutputSpec - def _format_arg(self, name, trait_spec, value): - if name == 'in_file_a': - arg = trait_spec.argstr % value - if isdefined(self.inputs.start_idx): - arg += '[%d..%d]' % (self.inputs.start_idx, - self.inputs.stop_idx) - if isdefined(self.inputs.single_idx): - arg += '[%d]' % (self.inputs.single_idx) - return arg - return super(Calc, self)._format_arg(name, trait_spec, value) - - def _parse_inputs(self, skip=None): - """Skip the arguments without argstr metadata - """ - return super(Calc, self)._parse_inputs( - skip=('start_idx', 'stop_idx', 'other')) - class BlurInMaskInputSpec(AFNICommandInputSpec): in_file = File( @@ -1648,8 +1504,10 @@ class BlurInMaskInputSpec(AFNICommandInputSpec): mandatory=True, exists=True, copyfile=False) - out_file = File(name_template='%s_blur', desc='output to the file', argstr='-prefix %s', - name_source='in_file', position=-1) + prefix = GenFile(template='{in_file}_blur', keep_extension=False, argstr='-prefix %s', + desc='output files prefix') + out_file = GenFile(template='{prefix}{outputtype_}', keep_extension=False, + desc='output to the file') mask = File( desc='Mask dataset, if desired. Blurring will occur only within the mask. Voxels NOT in the mask will be set to zero in the output.', argstr='-mask %s') @@ -1673,6 +1531,7 @@ class BlurInMaskInputSpec(AFNICommandInputSpec): class BlurInMask(AFNICommand): + """ Blurs a dataset spatially inside a mask. That's all. Experimental. For complete details, see the `3dBlurInMask Documentation. @@ -1698,7 +1557,8 @@ class BlurInMask(AFNICommand): class TCorrMapInputSpec(AFNICommandInputSpec): - in_file = File(exists=True, argstr='-input %s', mandatory=True, copyfile=False) + in_file = File( + exists=True, argstr='-input %s', mandatory=True, copyfile=False) seeds = File(exists=True, argstr='-seed %s', xor=('seeds_width')) mask = File(exists=True, argstr='-mask %s') automask = traits.Bool(argstr='-automask') @@ -1749,9 +1609,19 @@ class TCorrMapInputSpec(AFNICommandInputSpec): histogram = File( name_source='in_file', argstr='-Hist %d %s', suffix='_hist') + def _format_arg(self, name, trait_spec, value): + if name in self._thresh_opts: + return trait_spec.argstr % self.thresholds + [value] + elif name in self._expr_opts: + return trait_spec.argstr % (self.expr, value) + elif name == 'histogram': + return trait_spec.argstr % (self.histogram_bin_numbers, + value) + else: + return super(TCorrMapInputSpec, self)._format_arg(name, trait_spec, value) -class TCorrMapOutputSpec(TraitedSpec): +class TCorrMapOutputSpec(TraitedSpec): mean_file = File() zmean = File() qmean = File() @@ -1768,6 +1638,7 @@ class TCorrMapOutputSpec(TraitedSpec): class TCorrMap(AFNICommand): + """ For each voxel time series, computes the correlation between it and all other voxels, and combines this set of values into the output dataset(s) in some way. @@ -1792,16 +1663,6 @@ class TCorrMap(AFNICommand): output_spec = TCorrMapOutputSpec _additional_metadata = ['suffix'] - def _format_arg(self, name, trait_spec, value): - if name in self.inputs._thresh_opts: - return trait_spec.argstr % self.inputs.thresholds + [value] - elif name in self.inputs._expr_opts: - return trait_spec.argstr % (self.inputs.expr, value) - elif name == 'histogram': - return trait_spec.argstr % (self.inputs.histogram_bin_numbers, - value) - else: - return super(TCorrMap, self)._format_arg(name, trait_spec, value) class AutoboxInputSpec(AFNICommandInputSpec): @@ -1831,6 +1692,7 @@ class AutoboxOuputSpec(TraitedSpec): # out_file not mandatory class Autobox(AFNICommand): + """ Computes size of a box that fits around the volume. Also can be used to crop the volume to that box. @@ -1863,7 +1725,6 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): d[k] = int(d[k]) outputs.set(**d) outputs.set(out_file=self._gen_filename('out_file')) - return outputs def _gen_filename(self, name): if name == 'out_file' and (not isdefined(self.inputs.out_file)): @@ -1878,7 +1739,8 @@ class RetroicorInputSpec(AFNICommandInputSpec): mandatory=True, exists=True, copyfile=False) - out_file = File(desc='output image file name', argstr='-prefix %s', mandatory=True, position=1) + out_file = File( + desc='output image file name', argstr='-prefix %s', mandatory=True, position=1) card = File(desc='1D cardiac data file for cardiac correction', argstr='-card %s', position=-2, @@ -1905,6 +1767,7 @@ class RetroicorInputSpec(AFNICommandInputSpec): class Retroicor(AFNICommand): + """Performs Retrospective Image Correction for physiological motion effects, using a slightly modified version of the RETROICOR algorithm @@ -1948,11 +1811,12 @@ class AFNItoNIFTIInputSpec(AFNICommandInputSpec): exists=True, copyfile=False) out_file = File(name_template="%s.nii", desc='output image file name', - argstr='-prefix %s', name_source="in_file") + argstr='-prefix %s', name_source="in_file", keep_extension=False) hash_files = False class AFNItoNIFTI(AFNICommand): + """Changes AFNI format files to NIFTI format using 3dAFNItoNIFTI see AFNI Documentation: @@ -1974,15 +1838,6 @@ class AFNItoNIFTI(AFNICommand): input_spec = AFNItoNIFTIInputSpec output_spec = AFNICommandOutputSpec - def _overload_extension(self, value): - path, base, ext = split_filename(value) - if ext.lower() not in [".1d", ".nii.gz", ".1D"]: - ext = ext + ".nii" - return os.path.join(path, base + ext) - - def _gen_filename(self, name): - return os.path.abspath(super(AFNItoNIFTI, self)._gen_filename(name)) - class EvalInputSpec(AFNICommandInputSpec): in_file_a = File(desc='input file to 1deval', @@ -2004,8 +1859,26 @@ class EvalInputSpec(AFNICommandInputSpec): single_idx = traits.Int(desc='volume index for in_file_a') other = File(desc='other options', argstr='') + def _format_arg(self, name, trait_spec, value): + if name == 'in_file_a': + arg = trait_spec.argstr % value + if isdefined(self.start_idx): + arg += '[%d..%d]' % (self.start_idx, + self.stop_idx) + if isdefined(self.single_idx): + arg += '[%d]' % (self.single_idx) + return arg + return super(EvalInputSpec, self)._format_arg(name, trait_spec, value) + + def parse_args(self, skip=None): + """Skip the arguments without argstr metadata + """ + return super(EvalInputSpec, self).parse_args( + skip=('start_idx', 'stop_idx', 'out1D', 'other')) + class Eval(AFNICommand): + """Evaluates an expression that may include columns of data from one or more text files see AFNI Documentation: @@ -2029,23 +1902,6 @@ class Eval(AFNICommand): input_spec = EvalInputSpec output_spec = AFNICommandOutputSpec - def _format_arg(self, name, trait_spec, value): - if name == 'in_file_a': - arg = trait_spec.argstr % value - if isdefined(self.inputs.start_idx): - arg += '[%d..%d]' % (self.inputs.start_idx, - self.inputs.stop_idx) - if isdefined(self.inputs.single_idx): - arg += '[%d]' % (self.inputs.single_idx) - return arg - return super(Eval, self)._format_arg(name, trait_spec, value) - - def _parse_inputs(self, skip=None): - """Skip the arguments without argstr metadata - """ - return super(Eval, self)._parse_inputs( - skip=('start_idx', 'stop_idx', 'out1D', 'other')) - class MeansInputSpec(AFNICommandInputSpec): in_file_a = File(desc='input file to 3dMean', @@ -2064,12 +1920,15 @@ class MeansInputSpec(AFNICommandInputSpec): std_dev = traits.Bool(desc='calculate std dev', argstr='-stdev') sqr = traits.Bool(desc='mean square instead of value', argstr='-sqr') summ = traits.Bool(desc='take sum, (not average)', argstr='-sum') - count = traits.Bool(desc='compute count of non-zero voxels', argstr='-count') - mask_inter = traits.Bool(desc='create intersection mask', argstr='-mask_inter') + count = traits.Bool( + desc='compute count of non-zero voxels', argstr='-count') + mask_inter = traits.Bool( + desc='create intersection mask', argstr='-mask_inter') mask_union = traits.Bool(desc='create union mask', argstr='-mask_union') class Means(AFNICommand): + """Takes the voxel-by-voxel mean of all input datasets using 3dMean see AFNI Documentation: @@ -2096,9 +1955,12 @@ class HistInputSpec(CommandLineInputSpec): in_file = File( desc='input file to 3dHist', argstr='-input %s', position=1, mandatory=True, exists=True, copyfile=False) - out_file = File( - desc='Write histogram to niml file with this prefix', name_template='%s_hist', - keep_extension=False, argstr='-prefix %s', name_source=['in_file']) + prefix = GenFile( + template='{in_file}_hist', keep_extension=False, argstr='-prefix %s', + desc='Write histogram to niml file with this prefix') + out_file = GenFile( + template='{prefix}.niml.hist', keep_extension=False, + desc='Write histogram to niml file with this prefix') showhist = traits.Bool(False, usedefault=True, desc='write a text visual histogram', argstr='-showhist') out_show = File( @@ -2110,8 +1972,15 @@ class HistInputSpec(CommandLineInputSpec): min_value = traits.Float(argstr='-min %f', desc='minimum intensity value') bin_width = traits.Float(argstr='-binwidth %f', desc='bin width') + def parse_args(self, skip=None): + if not self.showhist: + if skip is None: + skip = [] + skip += ['out_show'] + return super(HistInputSpec, self).parse_args(skip=skip) + class HistOutputSpec(TraitedSpec): - out_file = File(desc='output file', exists=True) + out_file = File(desc='output file') out_show = File(desc='output visual histogram') @@ -2148,28 +2017,19 @@ def __init__(self, **inputs): if isinstance(version[0], int) and version[0] > 15: self._redirect_x = False - def _parse_inputs(self, skip=None): - if not self.inputs.showhist: - if skip is None: - skip = [] - skip += ['out_show'] - return super(Hist, self)._parse_inputs(skip=skip) - - def _list_outputs(self): - outputs = super(Hist, self)._list_outputs() - outputs['out_file'] += '.niml.hist' + def _post_run(self): if not self.inputs.showhist: - outputs['out_show'] = Undefined - return outputs + self.outputs.out_show = Undefined class FWHMxInputSpec(CommandLineInputSpec): in_file = File(desc='input dataset', argstr='-input %s', mandatory=True, exists=True) - out_file = File(argstr='> %s', name_source='in_file', name_template='%s_fwhmx.out', - position=-1, keep_extension=False, desc='output file') - out_subbricks = File(argstr='-out %s', name_source='in_file', name_template='%s_subbricks.out', - keep_extension=False, desc='output file listing the subbricks FWHM') + out_file = GenFile(argstr='> %s', template='{in_file}_fwhmx.out', position=-1, + keep_extension=False, desc='output file') + out_subbricks = GenFile( + argstr='-out %s', template='{in_file}_subbricks.out', + keep_extension=False, desc='output file listing the subbricks FWHM') mask = File(desc='use only voxels that are nonzero in mask', argstr='-mask %s', exists=True) automask = traits.Bool(False, usedefault=True, argstr='-automask', desc='compute a mask from THIS dataset, a la 3dAutomask') @@ -2197,8 +2057,48 @@ class FWHMxInputSpec(CommandLineInputSpec): combine = traits.Bool(argstr='-combine', desc='combine the final measurements along each axis') compat = traits.Bool(argstr='-compat', desc='be compatible with the older 3dFWHM') acf = traits.Either( - traits.Bool(), File(), traits.Tuple(File(exists=True), traits.Float()), - default=False, usedefault=True, argstr='-acf', desc='computes the spatial autocorrelation') + traits.Bool(), File(), traits.Tuple(File(exists=True), traits.Float()), default=False, + usedefault=True, argstr='-acf', desc='computes the spatial autocorrelation') + + def parse_args(self, skip=None): + if not self.detrend: + if skip is None: + skip = [] + skip += ['out_detrend'] + return super(FWHMxInputSpec, self).parse_args(skip=skip) + + def arg_used(self, name): + return self._format_arg(name) is None + + def _format_arg(self, name, trait_spec=None, value=None): + if trait_spec is None: + trait_spec = self.traits()[name] + + if value is None: + value = getattr(self, name) + + if name == 'detrend': + if isinstance(value, bool): + if value: + return trait_spec.argstr + else: + return None + elif isinstance(value, int): + return trait_spec.argstr + ' %d' % value + + if name == 'acf': + if value is None: + return None + if isinstance(value, bool): + if value: + return trait_spec.argstr + else: + return None + elif isinstance(value, tuple): + return trait_spec.argstr + ' %s %f' % value + elif isinstance(value, string_types): + return trait_spec.argstr + ' ' + value + return super(FWHMxInputSpec, self)._format_arg(name, trait_spec, value) class FWHMxOutputSpec(TraitedSpec): @@ -2317,58 +2217,27 @@ class FWHMx(AFNICommandBase): _cmd = '3dFWHMx' input_spec = FWHMxInputSpec output_spec = FWHMxOutputSpec - _acf = True - def _parse_inputs(self, skip=None): - if not self.inputs.detrend: - if skip is None: - skip = [] - skip += ['out_detrend'] - return super(FWHMx, self)._parse_inputs(skip=skip) - - def _format_arg(self, name, trait_spec, value): - if name == 'detrend': - if isinstance(value, bool): - if value: - return trait_spec.argstr - else: - return None - elif isinstance(value, int): - return trait_spec.argstr + ' %d' % value - - if name == 'acf': - if isinstance(value, bool): - if value: - return trait_spec.argstr - else: - self._acf = False - return None - elif isinstance(value, tuple): - return trait_spec.argstr + ' %s %f' % value - elif isinstance(value, string_types): - return trait_spec.argstr + ' ' + value - return super(FWHMx, self)._format_arg(name, trait_spec, value) - - def _list_outputs(self): - outputs = super(FWHMx, self)._list_outputs() + def _post_run(self): + super(FWHMx, self)._post_run() if self.inputs.detrend: fname, ext = op.splitext(self.inputs.in_file) if '.gz' in ext: _, ext2 = op.splitext(fname) ext = ext2 + ext - outputs['out_detrend'] += ext + self.outputs.out_detrend += ext else: - outputs['out_detrend'] = Undefined + self.outputs.out_detrend = Undefined - sout = np.loadtxt(outputs['out_file']) #pylint: disable=E1101 - if self._acf: - outputs['acf_param'] = tuple(sout[1]) + sout = np.loadtxt(self.outputs.out_file) #pylint: disable=E1101 + if self.inputs.arg_used('acf'): + self.outputs.acf_param = tuple(sout[1]) sout = tuple(sout[0]) - outputs['out_acf'] = op.abspath('3dFWHMx.1D') + self.outputs.out_acf = op.abspath('3dFWHMx.1D') if isinstance(self.inputs.acf, string_types): - outputs['out_acf'] = op.abspath(self.inputs.acf) + self.outputs.out_acf = op.abspath(self.inputs.acf) + + self.outputs.fwhm = tuple(sout) - outputs['fwhm'] = tuple(sout) - return outputs diff --git a/nipype/interfaces/afni/svm.py b/nipype/interfaces/afni/svm.py index c2bb335d32..a7ef1b5b44 100644 --- a/nipype/interfaces/afni/svm.py +++ b/nipype/interfaces/afni/svm.py @@ -8,21 +8,14 @@ >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data')) >>> os.chdir(datadir) """ -import warnings -import os -import re +from ..base import traits, File, TraitedSpec +from .base import AFNICommand, AFNICommandInputSpec, AFNICommandOutputSpec -from ..base import (Directory, TraitedSpec, - traits, isdefined, File, InputMultiPath, Undefined) -from ...utils.filemanip import (load_json, save_json, split_filename) -from nipype.utils.filemanip import fname_presuffix -from .base import AFNICommand, AFNICommandInputSpec,\ - AFNICommandOutputSpec -from nipype.interfaces.base import CommandLineInputSpec, CommandLine,\ - OutputMultiPath +from ... import logging -warn = warnings.warn +IFLOGGER = logging.getLogger('interface') +warn = IFLOGGER.warn class SVMTrainInputSpec(AFNICommandInputSpec): diff --git a/nipype/interfaces/afni/tests/test_auto_AFNICommand.py b/nipype/interfaces/afni/tests/test_auto_AFNICommand.py index f822168eb8..cc1d14c481 100644 --- a/nipype/interfaces/afni/tests/test_auto_AFNICommand.py +++ b/nipype/interfaces/afni/tests/test_auto_AFNICommand.py @@ -12,11 +12,8 @@ def test_AFNICommand_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - out_file=dict(argstr='-prefix %s', - name_source=['in_file'], - name_template='%s_afni', + outputtype=dict(usedefault=True, ), - outputtype=dict(), terminal_output=dict(nohash=True, ), ) @@ -26,3 +23,11 @@ def test_AFNICommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_AFNICommand_outputs(): + output_map = dict() + outputs = AFNICommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/afni/tests/test_auto_AFNICommandBase.py b/nipype/interfaces/afni/tests/test_auto_AFNICommandBase.py index 9052c5345a..c5d8841a19 100644 --- a/nipype/interfaces/afni/tests/test_auto_AFNICommandBase.py +++ b/nipype/interfaces/afni/tests/test_auto_AFNICommandBase.py @@ -21,3 +21,11 @@ def test_AFNICommandBase_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_AFNICommandBase_outputs(): + output_map = dict() + outputs = AFNICommandBase.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/afni/tests/test_auto_AFNItoNIFTI.py b/nipype/interfaces/afni/tests/test_auto_AFNItoNIFTI.py index 1e46cccd56..945af37be8 100644 --- a/nipype/interfaces/afni/tests/test_auto_AFNItoNIFTI.py +++ b/nipype/interfaces/afni/tests/test_auto_AFNItoNIFTI.py @@ -18,10 +18,12 @@ def test_AFNItoNIFTI_inputs(): position=-1, ), out_file=dict(argstr='-prefix %s', + keep_extension=False, name_source='in_file', name_template='%s.nii', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Allineate.py b/nipype/interfaces/afni/tests/test_auto_Allineate.py index 27a1cc5dae..66d44c24e7 100644 --- a/nipype/interfaces/afni/tests/test_auto_Allineate.py +++ b/nipype/interfaces/afni/tests/test_auto_Allineate.py @@ -64,7 +64,8 @@ def test_Allineate_inputs(): ), out_file=dict(argstr='-prefix %s', genfile=True, - name_source='%s_allineate', + name_source='in_file', + name_template='%s_allineate', position=-2, ), out_matrix=dict(argstr='-1Dmatrix_save %s', @@ -73,7 +74,8 @@ def test_Allineate_inputs(): ), out_weight_file=dict(argstr='-wtprefix %s', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), reference=dict(argstr='-base %s', ), replacebase=dict(argstr='-replacebase', diff --git a/nipype/interfaces/afni/tests/test_auto_AutoTcorrelate.py b/nipype/interfaces/afni/tests/test_auto_AutoTcorrelate.py index 0591a7eb7f..e1f50719dc 100644 --- a/nipype/interfaces/afni/tests/test_auto_AutoTcorrelate.py +++ b/nipype/interfaces/afni/tests/test_auto_AutoTcorrelate.py @@ -22,16 +22,16 @@ def test_AutoTcorrelate_inputs(): mask=dict(argstr='-mask %s', ), mask_only_targets=dict(argstr='-mask_only_targets', + usedefault=True, xor=['mask_source'], ), mask_source=dict(argstr='-mask_source %s', xor=['mask_only_targets'], ), out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_similarity_matrix.1D', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), polort=dict(argstr='-polort %d', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_Autobox.py b/nipype/interfaces/afni/tests/test_auto_Autobox.py index 3a23e751a3..8bb5ccc343 100644 --- a/nipype/interfaces/afni/tests/test_auto_Autobox.py +++ b/nipype/interfaces/afni/tests/test_auto_Autobox.py @@ -21,7 +21,8 @@ def test_Autobox_inputs(): out_file=dict(argstr='-prefix %s', name_source='in_file', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), padding=dict(argstr='-npad %d', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_Automask.py b/nipype/interfaces/afni/tests/test_auto_Automask.py index 5ee4b08162..06289c30f5 100644 --- a/nipype/interfaces/afni/tests/test_auto_Automask.py +++ b/nipype/interfaces/afni/tests/test_auto_Automask.py @@ -7,10 +7,8 @@ def test_Automask_inputs(): input_map = dict(args=dict(argstr='%s', ), brain_file=dict(argstr='-apply_prefix %s', - name_source='in_file', - name_template='%s_masked', ), - clfrac=dict(argstr='-clfrac %s', + clfrac=dict(argstr='-clfrac %.2f', ), dilate=dict(argstr='-dilate %s', ), @@ -28,10 +26,9 @@ def test_Automask_inputs(): position=-1, ), out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_mask', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Bandpass.py b/nipype/interfaces/afni/tests/test_auto_Bandpass.py index 519d8fd501..670f03cfdf 100644 --- a/nipype/interfaces/afni/tests/test_auto_Bandpass.py +++ b/nipype/interfaces/afni/tests/test_auto_Bandpass.py @@ -54,7 +54,8 @@ def test_Bandpass_inputs(): name_template='%s_bp', position=1, ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), tr=dict(argstr='-dt %f', diff --git a/nipype/interfaces/afni/tests/test_auto_BlurInMask.py b/nipype/interfaces/afni/tests/test_auto_BlurInMask.py index 276cf8a81f..45636f2ea2 100644 --- a/nipype/interfaces/afni/tests/test_auto_BlurInMask.py +++ b/nipype/interfaces/afni/tests/test_auto_BlurInMask.py @@ -31,12 +31,11 @@ def test_BlurInMask_inputs(): options=dict(argstr='%s', position=2, ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_blur', - position=-1, + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), preserve=dict(argstr='-preserve', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_BrickStat.py b/nipype/interfaces/afni/tests/test_auto_BrickStat.py index 0c47101656..9bc240fe6a 100644 --- a/nipype/interfaces/afni/tests/test_auto_BrickStat.py +++ b/nipype/interfaces/afni/tests/test_auto_BrickStat.py @@ -22,11 +22,8 @@ def test_BrickStat_inputs(): min=dict(argstr='-min', position=1, ), - out_file=dict(argstr='-prefix %s', - name_source=['in_file'], - name_template='%s_afni', + outputtype=dict(usedefault=True, ), - outputtype=dict(), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Calc.py b/nipype/interfaces/afni/tests/test_auto_Calc.py index c15431a5a8..b6d937b41a 100644 --- a/nipype/interfaces/afni/tests/test_auto_Calc.py +++ b/nipype/interfaces/afni/tests/test_auto_Calc.py @@ -32,7 +32,8 @@ def test_Calc_inputs(): name_source='in_file_a', name_template='%s_calc', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), single_idx=dict(), start_idx=dict(requires=['stop_idx'], ), diff --git a/nipype/interfaces/afni/tests/test_auto_Copy.py b/nipype/interfaces/afni/tests/test_auto_Copy.py index fffc9267d1..10fea3394b 100644 --- a/nipype/interfaces/afni/tests/test_auto_Copy.py +++ b/nipype/interfaces/afni/tests/test_auto_Copy.py @@ -18,11 +18,10 @@ def test_Copy_inputs(): position=-2, ), out_file=dict(argstr='%s', - name_source='in_file', - name_template='%s_copy', position=-1, ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Despike.py b/nipype/interfaces/afni/tests/test_auto_Despike.py index 0e8c5876f9..52cb9de3ac 100644 --- a/nipype/interfaces/afni/tests/test_auto_Despike.py +++ b/nipype/interfaces/afni/tests/test_auto_Despike.py @@ -17,11 +17,11 @@ def test_Despike_inputs(): mandatory=True, position=-1, ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_despike', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Detrend.py b/nipype/interfaces/afni/tests/test_auto_Detrend.py index 2fd8bf3d6f..277392d541 100644 --- a/nipype/interfaces/afni/tests/test_auto_Detrend.py +++ b/nipype/interfaces/afni/tests/test_auto_Detrend.py @@ -17,11 +17,11 @@ def test_Detrend_inputs(): mandatory=True, position=-1, ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_detrend', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Eval.py b/nipype/interfaces/afni/tests/test_auto_Eval.py index 0ca8e85bc0..50951737be 100644 --- a/nipype/interfaces/afni/tests/test_auto_Eval.py +++ b/nipype/interfaces/afni/tests/test_auto_Eval.py @@ -34,7 +34,8 @@ def test_Eval_inputs(): name_source='in_file_a', name_template='%s_calc', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), single_idx=dict(), start_idx=dict(requires=['stop_idx'], ), diff --git a/nipype/interfaces/afni/tests/test_auto_FWHMx.py b/nipype/interfaces/afni/tests/test_auto_FWHMx.py index f35aa66b62..dc2d67879f 100644 --- a/nipype/interfaces/afni/tests/test_auto_FWHMx.py +++ b/nipype/interfaces/afni/tests/test_auto_FWHMx.py @@ -46,15 +46,9 @@ def test_FWHMx_inputs(): name_template='%s_detrend', ), out_file=dict(argstr='> %s', - keep_extension=False, - name_source='in_file', - name_template='%s_fwhmx.out', position=-1, ), out_subbricks=dict(argstr='-out %s', - keep_extension=False, - name_source='in_file', - name_template='%s_subbricks.out', ), terminal_output=dict(nohash=True, ), diff --git a/nipype/interfaces/afni/tests/test_auto_Fim.py b/nipype/interfaces/afni/tests/test_auto_Fim.py index 60aa963b28..9cf495629a 100644 --- a/nipype/interfaces/afni/tests/test_auto_Fim.py +++ b/nipype/interfaces/afni/tests/test_auto_Fim.py @@ -31,7 +31,8 @@ def test_Fim_inputs(): name_source='in_file', name_template='%s_fim', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Fourier.py b/nipype/interfaces/afni/tests/test_auto_Fourier.py index 0bc9e03b6c..0013f320e4 100644 --- a/nipype/interfaces/afni/tests/test_auto_Fourier.py +++ b/nipype/interfaces/afni/tests/test_auto_Fourier.py @@ -29,7 +29,8 @@ def test_Fourier_inputs(): name_source='in_file', name_template='%s_fourier', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Hist.py b/nipype/interfaces/afni/tests/test_auto_Hist.py index 0024e5f186..60b38fdcab 100644 --- a/nipype/interfaces/afni/tests/test_auto_Hist.py +++ b/nipype/interfaces/afni/tests/test_auto_Hist.py @@ -27,17 +27,15 @@ def test_Hist_inputs(): ), nbin=dict(argstr='-nbin %d', ), - out_file=dict(argstr='-prefix %s', - keep_extension=False, - name_source=['in_file'], - name_template='%s_hist', - ), + out_file=dict(), out_show=dict(argstr='> %s', keep_extension=False, name_source='in_file', name_template='%s_hist.out', position=-1, ), + prefix=dict(argstr='-prefix %s', + ), showhist=dict(argstr='-showhist', usedefault=True, ), diff --git a/nipype/interfaces/afni/tests/test_auto_Maskave.py b/nipype/interfaces/afni/tests/test_auto_Maskave.py index dbff513cc8..6dec4e3d4e 100644 --- a/nipype/interfaces/afni/tests/test_auto_Maskave.py +++ b/nipype/interfaces/afni/tests/test_auto_Maskave.py @@ -21,12 +21,10 @@ def test_Maskave_inputs(): position=1, ), out_file=dict(argstr='> %s', - keep_extension=True, - name_source='in_file', - name_template='%s_maskave.1D', position=-1, ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), quiet=dict(argstr='-quiet', position=2, ), diff --git a/nipype/interfaces/afni/tests/test_auto_Means.py b/nipype/interfaces/afni/tests/test_auto_Means.py index de764464b5..1114573eb1 100644 --- a/nipype/interfaces/afni/tests/test_auto_Means.py +++ b/nipype/interfaces/afni/tests/test_auto_Means.py @@ -31,7 +31,8 @@ def test_Means_inputs(): name_source='in_file_a', name_template='%s_mean', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), scale=dict(argstr='-%sscale', ), sqr=dict(argstr='-sqr', diff --git a/nipype/interfaces/afni/tests/test_auto_Merge.py b/nipype/interfaces/afni/tests/test_auto_Merge.py index 9851b90b9c..ec0eaa3b03 100644 --- a/nipype/interfaces/afni/tests/test_auto_Merge.py +++ b/nipype/interfaces/afni/tests/test_auto_Merge.py @@ -23,10 +23,9 @@ def test_Merge_inputs(): position=-1, ), out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_merge', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_Refit.py b/nipype/interfaces/afni/tests/test_auto_Refit.py index 124655276a..ec345d4b58 100644 --- a/nipype/interfaces/afni/tests/test_auto_Refit.py +++ b/nipype/interfaces/afni/tests/test_auto_Refit.py @@ -7,6 +7,7 @@ def test_Refit_inputs(): input_map = dict(args=dict(argstr='%s', ), deoblique=dict(argstr='-deoblique', + usedefault=True, ), environ=dict(nohash=True, usedefault=True, @@ -19,6 +20,9 @@ def test_Refit_inputs(): mandatory=True, position=-1, ), + out_file=dict(), + outputtype=dict(usedefault=True, + ), space=dict(argstr='-space %s', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_Resample.py b/nipype/interfaces/afni/tests/test_auto_Resample.py index 8aa40f92ee..d133cebc3c 100644 --- a/nipype/interfaces/afni/tests/test_auto_Resample.py +++ b/nipype/interfaces/afni/tests/test_auto_Resample.py @@ -21,11 +21,11 @@ def test_Resample_inputs(): ), orientation=dict(argstr='-orient %s', ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_resample', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), resample_mode=dict(argstr='-rmode %s', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_Retroicor.py b/nipype/interfaces/afni/tests/test_auto_Retroicor.py index 2d5fb74175..48c3b78ccb 100644 --- a/nipype/interfaces/afni/tests/test_auto_Retroicor.py +++ b/nipype/interfaces/afni/tests/test_auto_Retroicor.py @@ -31,7 +31,8 @@ def test_Retroicor_inputs(): mandatory=True, position=1, ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), resp=dict(argstr='-resp %s', position=-3, ), diff --git a/nipype/interfaces/afni/tests/test_auto_SVMTest.py b/nipype/interfaces/afni/tests/test_auto_SVMTest.py index a1566c59f7..8a1a071763 100644 --- a/nipype/interfaces/afni/tests/test_auto_SVMTest.py +++ b/nipype/interfaces/afni/tests/test_auto_SVMTest.py @@ -31,7 +31,8 @@ def test_SVMTest_inputs(): out_file=dict(argstr='-predictions %s', name_template='%s_predictions', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), testlabels=dict(argstr='-testlabels %s', diff --git a/nipype/interfaces/afni/tests/test_auto_SVMTrain.py b/nipype/interfaces/afni/tests/test_auto_SVMTrain.py index eb13dcb531..2604a841e0 100644 --- a/nipype/interfaces/afni/tests/test_auto_SVMTrain.py +++ b/nipype/interfaces/afni/tests/test_auto_SVMTrain.py @@ -45,7 +45,8 @@ def test_SVMTrain_inputs(): name_template='%s_vectors', suffix='_bucket', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), trainlabels=dict(argstr='-trainlabels %s', diff --git a/nipype/interfaces/afni/tests/test_auto_SkullStrip.py b/nipype/interfaces/afni/tests/test_auto_SkullStrip.py index 12449c331f..5b2334fed0 100644 --- a/nipype/interfaces/afni/tests/test_auto_SkullStrip.py +++ b/nipype/interfaces/afni/tests/test_auto_SkullStrip.py @@ -21,7 +21,8 @@ def test_SkullStrip_inputs(): name_source='in_file', name_template='%s_skullstrip', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_TCat.py b/nipype/interfaces/afni/tests/test_auto_TCat.py index ce1e8fbaac..bd02538439 100644 --- a/nipype/interfaces/afni/tests/test_auto_TCat.py +++ b/nipype/interfaces/afni/tests/test_auto_TCat.py @@ -21,7 +21,8 @@ def test_TCat_inputs(): name_source='in_files', name_template='%s_tcat', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), rlt=dict(argstr='-rlt%s', position=1, ), diff --git a/nipype/interfaces/afni/tests/test_auto_TCorr1D.py b/nipype/interfaces/afni/tests/test_auto_TCorr1D.py index 96ebdbe3a6..a60372482e 100644 --- a/nipype/interfaces/afni/tests/test_auto_TCorr1D.py +++ b/nipype/interfaces/afni/tests/test_auto_TCorr1D.py @@ -16,16 +16,17 @@ def test_TCorr1D_inputs(): position=1, xor=['pearson', 'spearman', 'quadrant'], ), - out_file=dict(argstr='-prefix %s', - keep_extension=True, - name_source='xset', - name_template='%s_correlation.nii.gz', + out_file=dict(keep_extension=False, + name_template='{prefix}{outputtype_}', + ), + outputtype=dict(usedefault=True, ), - outputtype=dict(), pearson=dict(argstr=' -pearson', position=1, xor=['spearman', 'quadrant', 'ktaub'], ), + prefix=dict(argstr='-prefix %s', + ), quadrant=dict(argstr=' -quadrant', position=1, xor=['pearson', 'spearman', 'ktaub'], @@ -36,12 +37,12 @@ def test_TCorr1D_inputs(): ), terminal_output=dict(nohash=True, ), - xset=dict(argstr=' %s', + xset=dict(argstr='%s', copyfile=False, mandatory=True, position=-2, ), - y_1d=dict(argstr=' %s', + y_1d=dict(argstr='%s', mandatory=True, position=-1, ), diff --git a/nipype/interfaces/afni/tests/test_auto_TCorrMap.py b/nipype/interfaces/afni/tests/test_auto_TCorrMap.py index 15c98d2aac..6a126d2daa 100644 --- a/nipype/interfaces/afni/tests/test_auto_TCorrMap.py +++ b/nipype/interfaces/afni/tests/test_auto_TCorrMap.py @@ -55,11 +55,8 @@ def test_TCorrMap_inputs(): name_source='in_file', suffix='_mean', ), - out_file=dict(argstr='-prefix %s', - name_source=['in_file'], - name_template='%s_afni', + outputtype=dict(usedefault=True, ), - outputtype=dict(), pmean=dict(argstr='-Pmean %s', name_source='in_file', suffix='_pmean', diff --git a/nipype/interfaces/afni/tests/test_auto_TCorrelate.py b/nipype/interfaces/afni/tests/test_auto_TCorrelate.py index 4a5d68f9e9..3fbd091430 100644 --- a/nipype/interfaces/afni/tests/test_auto_TCorrelate.py +++ b/nipype/interfaces/afni/tests/test_auto_TCorrelate.py @@ -16,7 +16,8 @@ def test_TCorrelate_inputs(): name_source='xset', name_template='%s_tcorr', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), pearson=dict(argstr='-pearson', position=1, ), diff --git a/nipype/interfaces/afni/tests/test_auto_TShift.py b/nipype/interfaces/afni/tests/test_auto_TShift.py index a67893c811..eefd89e4d7 100644 --- a/nipype/interfaces/afni/tests/test_auto_TShift.py +++ b/nipype/interfaces/afni/tests/test_auto_TShift.py @@ -21,11 +21,11 @@ def test_TShift_inputs(): ), interp=dict(argstr='-%s', ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_tshift', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), rlt=dict(argstr='-rlt', ), rltplus=dict(argstr='-rlt+', diff --git a/nipype/interfaces/afni/tests/test_auto_TStat.py b/nipype/interfaces/afni/tests/test_auto_TStat.py index f7a60ca78c..ce224b221c 100644 --- a/nipype/interfaces/afni/tests/test_auto_TStat.py +++ b/nipype/interfaces/afni/tests/test_auto_TStat.py @@ -21,11 +21,11 @@ def test_TStat_inputs(): ), options=dict(argstr='%s', ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_tstat', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/afni/tests/test_auto_To3D.py b/nipype/interfaces/afni/tests/test_auto_To3D.py index 4357ee96da..565a235505 100644 --- a/nipype/interfaces/afni/tests/test_auto_To3D.py +++ b/nipype/interfaces/afni/tests/test_auto_To3D.py @@ -24,11 +24,12 @@ def test_To3D_inputs(): mandatory=True, position=-1, ), - out_file=dict(argstr='-prefix %s', - name_source=['in_folder'], - name_template='%s', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', + usedefault='true', ), - outputtype=dict(), skipoutliers=dict(argstr='-skip_outliers', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/afni/tests/test_auto_Volreg.py b/nipype/interfaces/afni/tests/test_auto_Volreg.py index f97afe6366..c59b13efe3 100644 --- a/nipype/interfaces/afni/tests/test_auto_Volreg.py +++ b/nipype/interfaces/afni/tests/test_auto_Volreg.py @@ -23,26 +23,16 @@ def test_Volreg_inputs(): position=-1, ), md1d_file=dict(argstr='-maxdisp1D %s', - keep_extension=True, - name_source='in_file', - name_template='%s_md.1D', position=-4, ), oned_file=dict(argstr='-1Dfile %s', - keep_extension=True, - name_source='in_file', - name_template='%s.1D', ), oned_matrix_save=dict(argstr='-1Dmatrix_save %s', - keep_extension=True, - name_source='in_file', - name_template='%s.aff12.1D', ), out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_volreg', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), timeshift=dict(argstr='-tshift 0', diff --git a/nipype/interfaces/afni/tests/test_auto_Warp.py b/nipype/interfaces/afni/tests/test_auto_Warp.py index c749d7fade..ab570f9287 100644 --- a/nipype/interfaces/afni/tests/test_auto_Warp.py +++ b/nipype/interfaces/afni/tests/test_auto_Warp.py @@ -29,11 +29,11 @@ def test_Warp_inputs(): ), newgrid=dict(argstr='-newgrid %f', ), - out_file=dict(argstr='-prefix %s', - name_source='in_file', - name_template='%s_warp', + out_file=dict(), + outputtype=dict(usedefault=True, + ), + prefix=dict(argstr='-prefix %s', ), - outputtype=dict(), terminal_output=dict(nohash=True, ), tta2mni=dict(argstr='-tta2mni', diff --git a/nipype/interfaces/afni/tests/test_auto_ZCutUp.py b/nipype/interfaces/afni/tests/test_auto_ZCutUp.py index f3ede54b3e..bd59594d21 100644 --- a/nipype/interfaces/afni/tests/test_auto_ZCutUp.py +++ b/nipype/interfaces/afni/tests/test_auto_ZCutUp.py @@ -23,7 +23,8 @@ def test_ZCutUp_inputs(): name_source='in_file', name_template='%s_zcupup', ), - outputtype=dict(), + outputtype=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/ants/base.py b/nipype/interfaces/ants/base.py index 20fab05881..70902faa1d 100644 --- a/nipype/interfaces/ants/base.py +++ b/nipype/interfaces/ants/base.py @@ -33,6 +33,12 @@ class ANTSCommandInputSpec(CommandLineInputSpec): nohash=True, desc="Number of ITK threads to use") + @staticmethod + def _format_xarray(val): + """ Convenience method for converting input arrays [1,2,3] to commandline format '1x2x3' """ + return 'x'.join([str(x) for x in val]) + + class ANTSCommand(CommandLine): """Base class for ANTS interfaces """ @@ -69,11 +75,6 @@ def _num_threads_update(self): self.inputs.environ.update({PREFERED_ITKv4_THREAD_LIMIT_VARIABLE: '%s' % self.inputs.num_threads}) - @staticmethod - def _format_xarray(val): - """ Convenience method for converting input arrays [1,2,3] to commandline format '1x2x3' """ - return 'x'.join([str(x) for x in val]) - @classmethod def set_default_num_threads(cls, num_threads): """Set the default number of threads for ITK calls diff --git a/nipype/interfaces/ants/legacy.py b/nipype/interfaces/ants/legacy.py index b68e0f7ed8..8e8e1f00b5 100644 --- a/nipype/interfaces/ants/legacy.py +++ b/nipype/interfaces/ants/legacy.py @@ -95,32 +95,30 @@ class antsIntroduction(ANTSCommand): input_spec = antsIntroductionInputSpec output_spec = antsIntroductionOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): transmodel = self.inputs.transformation_model # When transform is set as 'RI'/'RA', wrap fields should not be expected # The default transformation is GR, which outputs the wrap fields if not isdefined(transmodel) or (isdefined(transmodel) and transmodel not in ['RI', 'RA']): - outputs['warp_field'] = os.path.join(os.getcwd(), + self.outputs.warp_field = os.path.join(os.getcwd(), self.inputs.out_prefix + 'Warp.nii.gz') - outputs['inverse_warp_field'] = os.path.join(os.getcwd(), + self.outputs.inverse_warp_field = os.path.join(os.getcwd(), self.inputs.out_prefix + 'InverseWarp.nii.gz') - outputs['affine_transformation'] = os.path.join(os.getcwd(), + self.outputs.affine_transformation = os.path.join(os.getcwd(), self.inputs.out_prefix + 'Affine.txt') - outputs['input_file'] = os.path.join(os.getcwd(), + self.outputs.input_file = os.path.join(os.getcwd(), self.inputs.out_prefix + 'repaired.nii.gz') - outputs['output_file'] = os.path.join(os.getcwd(), + self.outputs.output_file = os.path.join(os.getcwd(), self.inputs.out_prefix + 'deformed.nii.gz') - return outputs - + # How do we make a pass through so that GenWarpFields is just an alias for antsIntroduction ? @@ -227,9 +225,8 @@ def _format_arg(self, opt, spec, val): return start + ' '.join(name for name in val) return super(buildtemplateparallel, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['template_files'] = [] + def _post_run(self): + self.outputs.template_files = [] for i in range(len(glob(os.path.realpath('*iteration*')))): temp = os.path.realpath('%s_iteration_%d/%stemplate.nii.gz' % (self.inputs.transformation_model, @@ -247,15 +244,15 @@ def _list_outputs(self): self.inputs.out_prefix, i)) - outputs['template_files'].append(os.path.realpath(file_)) - outputs['final_template_file'] = \ + self.outputs.template_files.append(os.path.realpath(file_)) + self.outputs.final_template_file = \ os.path.realpath('%stemplate.nii.gz' % self.inputs.out_prefix) - outputs['subject_outfiles'] = [] + self.outputs.subject_outfiles = [] for filename in self.inputs.in_files: _, base, _ = split_filename(filename) temp = glob(os.path.realpath('%s%s*' % (self.inputs.out_prefix, base))) for file_ in temp: - outputs['subject_outfiles'].append(file_) - return outputs + self.outputs.subject_outfiles.append(file_) + \ No newline at end of file diff --git a/nipype/interfaces/ants/registration.py b/nipype/interfaces/ants/registration.py index 89af092a33..ccb5b47efd 100644 --- a/nipype/interfaces/ants/registration.py +++ b/nipype/interfaces/ants/registration.py @@ -85,77 +85,49 @@ class ANTSInputSpec(ANTSCommandInputSpec): traits.Int(), argstr='--number-of-affine-iterations %s', sep='x') -class ANTSOutputSpec(TraitedSpec): - affine_transform = File(exists=True, desc='Affine transform file') - warp_transform = File(exists=True, desc='Warping deformation field') - inverse_warp_transform = File( - exists=True, desc='Inverse warping deformation field') - metaheader = File(exists=True, desc='VTK metaheader .mhd file') - metaheader_raw = File(exists=True, desc='VTK metaheader .raw file') - - -class ANTS(ANTSCommand): - - """ - - - Examples - -------- - - >>> from nipype.interfaces.ants import ANTS - >>> ants = ANTS() - >>> ants.inputs.dimension = 3 - >>> ants.inputs.output_transform_prefix = 'MY' - >>> ants.inputs.metric = ['CC'] - >>> ants.inputs.fixed_image = ['T1.nii'] - >>> ants.inputs.moving_image = ['resting.nii'] - >>> ants.inputs.metric_weight = [1.0] - >>> ants.inputs.radius = [5] - >>> ants.inputs.transformation_model = 'SyN' - >>> ants.inputs.gradient_step_length = 0.25 - >>> ants.inputs.number_of_iterations = [50, 35, 15] - >>> ants.inputs.use_histogram_matching = True - >>> ants.inputs.mi_option = [32, 16000] - >>> ants.inputs.regularization = 'Gauss' - >>> ants.inputs.regularization_gradient_field_sigma = 3 - >>> ants.inputs.regularization_deformation_field_sigma = 0 - >>> ants.inputs.number_of_affine_iterations = [10000,10000,10000,10000,10000] - >>> ants.cmdline - 'ANTS 3 --MI-option 32x16000 --image-metric CC[ T1.nii, resting.nii, 1, 5 ] --number-of-affine-iterations \ -10000x10000x10000x10000x10000 --number-of-iterations 50x35x15 --output-naming MY --regularization Gauss[3.0,0.0] \ ---transformation-model SyN[0.25] --use-Histogram-Matching 1' - """ - _cmd = 'ANTS' - input_spec = ANTSInputSpec - output_spec = ANTSOutputSpec + def _format_arg(self, opt, spec, val): + if opt == 'moving_image': + return self._image_metric_constructor() + elif opt == 'transformation_model': + return self._transformation_constructor() + elif opt == 'regularization': + return self._regularization_constructor() + elif opt == 'affine_gradient_descent_option': + return self._affine_gradient_descent_option_constructor() + elif opt == 'use_histogram_matching': + if self.use_histogram_matching: + return '--use-Histogram-Matching 1' + else: + return '--use-Histogram-Matching 0' + return super(ANTSInputSpec, self)._format_arg(opt, spec, val) def _image_metric_constructor(self): retval = [] intensity_based = ['CC', 'MI', 'SMI', 'PR', 'SSD', 'MSQ'] point_set_based = ['PSE', 'JTB'] - for ii in range(len(self.inputs.moving_image)): - if self.inputs.metric[ii] in intensity_based: + for ii in range(len(self.moving_image)): + if self.metric[ii] in intensity_based: retval.append( - '--image-metric %s[ %s, %s, %g, %d ]' % (self.inputs.metric[ii], - self.inputs.fixed_image[ + '--image-metric %s[ %s, %s, %g, %d ]' % (self.metric[ii], + self.fixed_image[ ii], - self.inputs.moving_image[ + self.moving_image[ ii], - self.inputs.metric_weight[ + self.metric_weight[ ii], - self.inputs.radius[ii])) - elif self.inputs.metric[ii] == point_set_based: + self.radius[ii])) + elif self.metric[ii] == point_set_based: pass - # retval.append('--image-metric %s[%s, %s, ...'.format(self.inputs.metric[ii], - # self.inputs.fixed_image[ii], self.inputs.moving_image[ii], ...)) + # retval.append('--image-metric %s[%s, %s, ...'.format(self.metric[ii], + # self.fixed_image[ii], self.moving_image[ii], ...)) return ' '.join(retval) def _transformation_constructor(self): - model = self.inputs.transformation_model - step_length = self.inputs.gradient_step_length - time_step = self.inputs.number_of_time_steps - delta_time = self.inputs.delta_time - symmetry_type = self.inputs.symmetry_type + model = self.transformation_model + step_length = self.gradient_step_length + time_step = self.number_of_time_steps + delta_time = self.delta_time + symmetry_type = self.symmetry_type retval = ['--transformation-model %s' % model] parameters = [] for elem in (step_length, time_step, delta_time, symmetry_type): @@ -170,12 +142,12 @@ def _transformation_constructor(self): return ''.join(retval) def _regularization_constructor(self): - return '--regularization {0}[{1},{2}]'.format(self.inputs.regularization, - self.inputs.regularization_gradient_field_sigma, - self.inputs.regularization_deformation_field_sigma) + return '--regularization {0}[{1},{2}]'.format(self.regularization, + self.regularization_gradient_field_sigma, + self.regularization_deformation_field_sigma) def _affine_gradient_descent_option_constructor(self): - values = self.inputs.affine_gradient_descent_option + values = self.affine_gradient_descent_option defaults = [0.1, 0.5, 1.e-4, 1.e-4] for ii in range(len(defaults)): try: @@ -186,33 +158,55 @@ def _affine_gradient_descent_option_constructor(self): retval = ['--affine-gradient-descent-option', parameters] return ' '.join(retval) - def _format_arg(self, opt, spec, val): - if opt == 'moving_image': - return self._image_metric_constructor() - elif opt == 'transformation_model': - return self._transformation_constructor() - elif opt == 'regularization': - return self._regularization_constructor() - elif opt == 'affine_gradient_descent_option': - return self._affine_gradient_descent_option_constructor() - elif opt == 'use_histogram_matching': - if self.inputs.use_histogram_matching: - return '--use-Histogram-Matching 1' - else: - return '--use-Histogram-Matching 0' - return super(ANTS, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['affine_transform'] = os.path.abspath( - self.inputs.output_transform_prefix + 'Affine.txt') - outputs['warp_transform'] = os.path.abspath( - self.inputs.output_transform_prefix + 'Warp.nii.gz') - outputs['inverse_warp_transform'] = os.path.abspath( - self.inputs.output_transform_prefix + 'InverseWarp.nii.gz') - # outputs['metaheader'] = os.path.abspath(self.inputs.output_transform_prefix + 'velocity.mhd') - # outputs['metaheader_raw'] = os.path.abspath(self.inputs.output_transform_prefix + 'velocity.raw') - return outputs +class ANTSOutputSpec(TraitedSpec): + affine_transform = File( + name_source='output_transform_prefix', name_template='%sAffine.txt', + keep_extension=False, desc='Affine transform file') + warp_transform = File( + name_source='output_transform_prefix', name_template='%sWarp.nii.gz', + keep_extension=False, desc='Warping deformation field') + inverse_warp_transform = File( + name_source='output_transform_prefix', name_template='%sInverseWarp.nii.gz', + keep_extension=False, desc='Inverse warping deformation field') + metaheader = File(exists=True, desc='VTK metaheader .mhd file') + metaheader_raw = File(exists=True, desc='VTK metaheader .raw file') + + +class ANTS(ANTSCommand): + + """ + + + Examples + -------- + + >>> from nipype.interfaces.ants import ANTS + >>> ants = ANTS() + >>> ants.inputs.dimension = 3 + >>> ants.inputs.output_transform_prefix = 'MY' + >>> ants.inputs.metric = ['CC'] + >>> ants.inputs.fixed_image = ['T1.nii'] + >>> ants.inputs.moving_image = ['resting.nii'] + >>> ants.inputs.metric_weight = [1.0] + >>> ants.inputs.radius = [5] + >>> ants.inputs.transformation_model = 'SyN' + >>> ants.inputs.gradient_step_length = 0.25 + >>> ants.inputs.number_of_iterations = [50, 35, 15] + >>> ants.inputs.use_histogram_matching = True + >>> ants.inputs.mi_option = [32, 16000] + >>> ants.inputs.regularization = 'Gauss' + >>> ants.inputs.regularization_gradient_field_sigma = 3 + >>> ants.inputs.regularization_deformation_field_sigma = 0 + >>> ants.inputs.number_of_affine_iterations = [10000,10000,10000,10000,10000] + >>> ants.cmdline + 'ANTS 3 --MI-option 32x16000 --image-metric CC[ T1.nii, resting.nii, 1, 5 ] --number-of-affine-iterations \ +10000x10000x10000x10000x10000 --number-of-iterations 50x35x15 --output-naming MY --regularization Gauss[3.0,0.0] \ +--transformation-model SyN[0.25] --use-Histogram-Matching 1' + """ + _cmd = 'ANTS' + input_spec = ANTSInputSpec + output_spec = ANTSOutputSpec class RegistrationInputSpec(ANTSCommandInputSpec): @@ -380,6 +374,254 @@ class RegistrationInputSpec(ANTSCommandInputSpec): low=0.0, high=1.0, value=0.0, argstr='%s', usedefault=True, desc="The Lower quantile to clip image ranges") + def parse_args(self, skip=None): + if skip is None: + skip = [] + + if (isdefined(self.winsorize_upper_quantile) and + isdefined(self.winsorize_lower_quantile)): + skip += ['winsorize_upper_quantile'] + return super(RegistrationInputSpec, self).parse_args(skip) + + def _format_arg(self, opt, spec, val): + if opt == 'fixed_image_mask': + if isdefined(self.moving_image_mask): + return '--masks [ %s, %s ]' % (self.fixed_image_mask, + self.moving_image_mask) + else: + return '--masks %s' % self.fixed_image_mask + elif opt == 'transforms': + return self._format_registration() + elif opt == 'initial_moving_transform': + try: + do_invert_transform = int(self.invert_initial_moving_transform) + except ValueError: + do_invert_transform = 0 # Just do the default behavior + return '--initial-moving-transform [ %s, %d ]' % (self.initial_moving_transform, + do_invert_transform) + elif opt == 'initial_moving_transform_com': + try: + do_center_of_mass_init = int(self.initial_moving_transform_com) + except ValueError: + do_center_of_mass_init = 0 # Just do the default behavior + return '--initial-moving-transform [ %s, %s, %d ]' % (self.fixed_image[0], + self.moving_image[0], + do_center_of_mass_init) + elif opt == 'interpolation': + if self.interpolation in ['BSpline', 'MultiLabel', 'Gaussian'] and \ + isdefined(self.interpolation_parameters): + return '--interpolation %s[ %s ]' % (self.interpolation, + ', '.join([str(param) + for param in self.interpolation_parameters])) + else: + return '--interpolation %s' % self.interpolation + elif opt == 'output_transform_prefix': + out_filename = self._get_outputfilenames(inverse=False) + inv_out_filename = self._get_outputfilenames(inverse=True) + if out_filename and inv_out_filename: + return '--output [ %s, %s, %s ]' % (self.output_transform_prefix, + out_filename, + inv_out_filename) + elif out_filename: + return '--output [ %s, %s ]' % (self.output_transform_prefix, + out_filename) + else: + return '--output %s' % self.output_transform_prefix + elif opt == 'winsorize_upper_quantile' or opt == 'winsorize_lower_quantile': + return self._format_winsorize_image_intensities() + + # This feature was removed from recent versions of antsRegistration due to corrupt outputs. + # elif opt == 'collapse_linear_transforms_to_fixed_image_header': + # return self._formatCollapseLinearTransformsToFixedImageHeader() + return super(RegistrationInputSpec, self)._format_arg(opt, spec, val) + + + def _format_metric(self, index): + """ + Format the antsRegistration -m metric argument(s). + + Parameters + ---------- + index: the stage index + """ + # The metric name input for the current stage. + name_input = self.metric[index] + # The stage-specific input dictionary. + stage_inputs = dict( + fixed_image=self.fixed_image[0], + moving_image=self.moving_image[0], + metric=name_input, + weight=self.metric_weight[index], + radius_or_bins=self.radius_or_number_of_bins[index], + optional=self.radius_or_number_of_bins[index] + ) + # The optional sampling strategy and percentage. + if isdefined(self.sampling_strategy) and self.sampling_strategy: + sampling_strategy = self.sampling_strategy[index] + if sampling_strategy: + stage_inputs['sampling_strategy'] = sampling_strategy + if isdefined(self.sampling_percentage) and self.sampling_percentage: + sampling_percentage = self.sampling_percentage[index] + if sampling_percentage: + stage_inputs['sampling_percentage'] = sampling_percentage + + # Make a list of metric specifications, one per -m command line + # argument for the current stage. + # If there are multiple inputs for this stage, then convert the + # dictionary of list inputs into a list of metric specifications. + # Otherwise, make a singleton list of the metric specification + # from the non-list inputs. + if isinstance(name_input, list): + items = list(stage_inputs.items()) + indexes = list(range(0, len(name_input))) + specs = list() + for i in indexes: + temp = dict([(k, v[i]) for k, v in items]) + if len(self.fixed_image) == 1: + temp["fixed_image"] = self.fixed_image[0] + else: + temp["fixed_image"] = self.fixed_image[i] + + if len(self.moving_image) == 1: + temp["moving_image"] = self.moving_image[0] + else: + temp["moving_image"] = self.moving_image[i] + + specs.append(temp) + else: + specs = [stage_inputs] + + # Format the --metric command line metric arguments, one per + # specification. + return [self._format_metric_argument(**spec) for spec in specs] + + @staticmethod + def _format_metric_argument(**kwargs): + retval = '%s[ %s, %s, %g, %d' % (kwargs['metric'], + kwargs['fixed_image'], + kwargs['moving_image'], + kwargs['weight'], + kwargs['radius_or_bins']) + + # The optional sampling strategy. + if 'sampling_strategy' in kwargs: + sampling_strategy = kwargs['sampling_strategy'] + elif 'sampling_percentage' in kwargs: + # The sampling percentage is specified but not the + # sampling strategy. Use the default strategy. + sampling_strategy = Registration.DEF_SAMPLING_STRATEGY + else: + sampling_strategy = None + # Format the optional sampling arguments. + if sampling_strategy: + retval += ', %s' % sampling_strategy + if 'sampling_percentage' in kwargs: + retval += ', %g' % kwargs['sampling_percentage'] + + retval += ' ]' + + return retval + + def _format_transform(self, index): + retval = [] + retval.append('%s[ ' % self.transforms[index]) + parameters = ', '.join([str( + element) for element in self.transform_parameters[index]]) + retval.append('%s' % parameters) + retval.append(' ]') + return "".join(retval) + + def _format_registration(self): + retval = [] + for ii in range(len(self.transforms)): + retval.append('--transform %s' % (self._format_transform(ii))) + for metric in self._format_metric(ii): + retval.append('--metric %s' % metric) + retval.append('--convergence %s' % self._format_convergence(ii)) + if isdefined(self.sigma_units): + retval.append('--smoothing-sigmas %s%s' % + (self._format_xarray(self.smoothing_sigmas[ii]), + self.sigma_units[ii])) + else: + retval.append('--smoothing-sigmas %s' % + self._format_xarray(self.smoothing_sigmas[ii])) + retval.append('--shrink-factors %s' % + self._format_xarray(self.shrink_factors[ii])) + if isdefined(self.use_estimate_learning_rate_once): + retval.append('--use-estimate-learning-rate-once %d' % + self.use_estimate_learning_rate_once[ii]) + if isdefined(self.use_histogram_matching): + # use_histogram_matching is either a common flag for all transforms + # or a list of transform-specific flags + if isinstance(self.use_histogram_matching, bool): + histval = self.use_histogram_matching + else: + histval = self.use_histogram_matching[ii] + retval.append('--use-histogram-matching %d' % histval) + return " ".join(retval) + + def _get_outputfilenames(self, inverse=False): + output_filename = None + if not inverse: + if isdefined(self.output_warped_image) and \ + self.output_warped_image: + output_filename = self.output_warped_image + if isinstance(output_filename, bool): + output_filename = '%s_Warped.nii.gz' % self.output_transform_prefix + else: + output_filename = output_filename + return output_filename + inv_output_filename = None + if isdefined(self.output_inverse_warped_image) and \ + self.output_inverse_warped_image: + inv_output_filename = self.output_inverse_warped_image + if isinstance(inv_output_filename, bool): + inv_output_filename = '%s_InverseWarped.nii.gz' % self.output_transform_prefix + else: + inv_output_filename = inv_output_filename + return inv_output_filename + + def _format_convergence(self, ii): + convergence_iter = self._format_xarray(self.number_of_iterations[ii]) + if len(self.convergence_threshold) > ii: + convergence_value = self.convergence_threshold[ii] + else: + convergence_value = self.convergence_threshold[0] + if len(self.convergence_window_size) > ii: + convergence_ws = self.convergence_window_size[ii] + else: + convergence_ws = self.convergence_window_size[0] + return '[ %s, %g, %d ]' % (convergence_iter, convergence_value, convergence_ws) + + def _format_winsorize_image_intensities(self): + if not self.winsorize_upper_quantile > self.winsorize_lower_quantile: + raise RuntimeError("Upper bound MUST be more than lower bound: %g > %g" + % (self.winsorize_upper_quantile, self.winsorize_lower_quantile)) + return '--winsorize-image-intensities [ %s, %s ]' % (self.winsorize_lower_quantile, + self.winsorize_upper_quantile) + + + def _output_filenames(self, prefix, count, transform, inverse=False): + self.low_dimensional_transform_map = {'Rigid': 'Rigid.mat', + 'Affine': 'Affine.mat', + 'GenericAffine': 'GenericAffine.mat', + 'CompositeAffine': 'Affine.mat', + 'Similarity': 'Similarity.mat', + 'Translation': 'Translation.mat', + 'BSpline': 'BSpline.txt', + 'Initial': 'DerivedInitialMovingTranslation.mat'} + if transform in list(self.low_dimensional_transform_map.keys()): + suffix = self.low_dimensional_transform_map[transform] + inverse_mode = inverse + else: + inverse_mode = False # These are not analytically invertable + if inverse: + suffix = 'InverseWarp.nii.gz' + else: + suffix = 'Warp.nii.gz' + return '%s%d%s' % (prefix, count, suffix), inverse_mode + + class RegistrationOutputSpec(TraitedSpec): forward_transforms = traits.List( File(exists=True), desc='List of output transforms for forward registration') @@ -440,7 +682,7 @@ class Registration(ANTSCommand): --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] \ --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.025, 1.0 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.025, 1.0 ] --write-composite-transform 1' >>> reg1.run() # doctest: +SKIP >>> reg2 = copy.deepcopy(reg) @@ -453,7 +695,7 @@ class Registration(ANTSCommand): --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] \ --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 0.975 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 0.975 ] --write-composite-transform 1' >>> reg3 = copy.deepcopy(reg) >>> reg3.inputs.winsorize_lower_quantile = 0.025 @@ -466,7 +708,7 @@ class Registration(ANTSCommand): --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] \ --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.025, 0.975 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.025, 0.975 ] --write-composite-transform 1' >>> reg3a = copy.deepcopy(reg) >>> reg3a.inputs.float = True @@ -478,7 +720,7 @@ class Registration(ANTSCommand): --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 \ --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \ --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \ ---use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ +--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ --write-composite-transform 1' >>> reg3b = copy.deepcopy(reg) @@ -491,7 +733,7 @@ class Registration(ANTSCommand): --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 \ --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \ --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \ ---use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ +--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ --write-composite-transform 1' >>> # Test collapse transforms flag @@ -500,8 +742,7 @@ class Registration(ANTSCommand): >>> reg4.inputs.restore_state = 'trans.mat' >>> reg4.inputs.initialize_transforms_per_stage = True >>> reg4.inputs.collapse_output_transforms = True - >>> outputs = reg4._list_outputs() - >>> pprint.pprint(outputs) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + >>> pprint.pprint(reg4.outputs) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE {'composite_transform': '.../nipype/testing/data/output_Composite.h5', 'forward_invert_flags': [], 'forward_transforms': [], @@ -519,14 +760,14 @@ class Registration(ANTSCommand): --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 \ --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \ --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \ ---use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ +--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ --write-composite-transform 1' >>> # Test collapse transforms flag >>> reg4b = copy.deepcopy(reg4) >>> reg4b.inputs.write_composite_transform = False >>> outputs = reg4b._list_outputs() - >>> pprint.pprint(outputs) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + >>> pprint.pprint(reg4b.outputs) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE {'composite_transform': , 'forward_invert_flags': [False, False], 'forward_transforms': ['.../nipype/testing/data/output_0GenericAffine.mat', @@ -547,7 +788,7 @@ class Registration(ANTSCommand): --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 \ --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \ --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \ ---use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ +--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ --write-composite-transform 0' >>> # Test multiple metrics per stage @@ -568,7 +809,7 @@ class Registration(ANTSCommand): --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] \ --metric CC[ fixed1.nii, moving1.nii, 0.5, 4, None, 0.1 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' >>> # Test multiple inputs >>> reg6 = copy.deepcopy(reg5) @@ -583,7 +824,7 @@ class Registration(ANTSCommand): --metric Mattes[ fixed1.nii, moving1.nii, 0.5, 32, None, 0.05 ] \ --metric CC[ fixed2.nii, moving2.nii, 0.5, 4, None, 0.1 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' >>> # Test Interpolation Parameters (BSpline) >>> reg7a = copy.deepcopy(reg) @@ -597,7 +838,7 @@ class Registration(ANTSCommand): --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform SyN[ 0.25, 3.0, 0.0 ] \ --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' >>> # Test Interpolation Parameters (MultiLabel/Gaussian) >>> reg7b = copy.deepcopy(reg) @@ -611,7 +852,7 @@ class Registration(ANTSCommand): --smoothing-sigmas 1.0x0.0vox --shrink-factors 2x1 --use-estimate-learning-rate-once 1 --use-histogram-matching 1 \ --transform SyN[ 0.25, 3.0, 0.0 ] --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] \ --convergence [ 100x50x30, 1e-09, 20 ] --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 \ ---use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ +--use-estimate-learning-rate-once 1 --use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] \ --write-composite-transform 1' >>> # Test Extended Transform Parameters @@ -626,7 +867,7 @@ class Registration(ANTSCommand): --use-estimate-learning-rate-once 1 --use-histogram-matching 1 --transform BSplineSyN[ 0.25, 26, 0, 3 ] \ --metric Mattes[ fixed1.nii, moving1.nii, 1, 32 ] --convergence [ 100x50x30, 1e-09, 20 ] \ --smoothing-sigmas 2.0x1.0x0.0vox --shrink-factors 3x2x1 --use-estimate-learning-rate-once 1 \ ---use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' +--use-histogram-matching 1 --winsorize-image-intensities [ 0.0, 1.0 ] --write-composite-transform 1' """ DEF_SAMPLING_STRATEGY = 'None' """The default sampling strategy argument.""" @@ -634,255 +875,13 @@ class Registration(ANTSCommand): _cmd = 'antsRegistration' input_spec = RegistrationInputSpec output_spec = RegistrationOutputSpec - _quantilesDone = False _linear_transform_names = ['Rigid', 'Affine', 'Translation', 'CompositeAffine', 'Similarity'] - def _format_metric(self, index): - """ - Format the antsRegistration -m metric argument(s). - - Parameters - ---------- - index: the stage index - """ - # The metric name input for the current stage. - name_input = self.inputs.metric[index] - # The stage-specific input dictionary. - stage_inputs = dict( - fixed_image=self.inputs.fixed_image[0], - moving_image=self.inputs.moving_image[0], - metric=name_input, - weight=self.inputs.metric_weight[index], - radius_or_bins=self.inputs.radius_or_number_of_bins[index], - optional=self.inputs.radius_or_number_of_bins[index] - ) - # The optional sampling strategy and percentage. - if isdefined(self.inputs.sampling_strategy) and self.inputs.sampling_strategy: - sampling_strategy = self.inputs.sampling_strategy[index] - if sampling_strategy: - stage_inputs['sampling_strategy'] = sampling_strategy - if isdefined(self.inputs.sampling_percentage) and self.inputs.sampling_percentage: - sampling_percentage = self.inputs.sampling_percentage[index] - if sampling_percentage: - stage_inputs['sampling_percentage'] = sampling_percentage - - # Make a list of metric specifications, one per -m command line - # argument for the current stage. - # If there are multiple inputs for this stage, then convert the - # dictionary of list inputs into a list of metric specifications. - # Otherwise, make a singleton list of the metric specification - # from the non-list inputs. - if isinstance(name_input, list): - items = list(stage_inputs.items()) - indexes = list(range(0, len(name_input))) - specs = list() - for i in indexes: - temp = dict([(k, v[i]) for k, v in items]) - if len(self.inputs.fixed_image) == 1: - temp["fixed_image"] = self.inputs.fixed_image[0] - else: - temp["fixed_image"] = self.inputs.fixed_image[i] - - if len(self.inputs.moving_image) == 1: - temp["moving_image"] = self.inputs.moving_image[0] - else: - temp["moving_image"] = self.inputs.moving_image[i] - - specs.append(temp) - else: - specs = [stage_inputs] - - # Format the --metric command line metric arguments, one per - # specification. - return [self._format_metric_argument(**spec) for spec in specs] - - @staticmethod - def _format_metric_argument(**kwargs): - retval = '%s[ %s, %s, %g, %d' % (kwargs['metric'], - kwargs['fixed_image'], - kwargs['moving_image'], - kwargs['weight'], - kwargs['radius_or_bins']) - - # The optional sampling strategy. - if 'sampling_strategy' in kwargs: - sampling_strategy = kwargs['sampling_strategy'] - elif 'sampling_percentage' in kwargs: - # The sampling percentage is specified but not the - # sampling strategy. Use the default strategy. - sampling_strategy = Registration.DEF_SAMPLING_STRATEGY - else: - sampling_strategy = None - # Format the optional sampling arguments. - if sampling_strategy: - retval += ', %s' % sampling_strategy - if 'sampling_percentage' in kwargs: - retval += ', %g' % kwargs['sampling_percentage'] - - retval += ' ]' - - return retval - - def _format_transform(self, index): - retval = [] - retval.append('%s[ ' % self.inputs.transforms[index]) - parameters = ', '.join([str( - element) for element in self.inputs.transform_parameters[index]]) - retval.append('%s' % parameters) - retval.append(' ]') - return "".join(retval) - - def _format_registration(self): - retval = [] - for ii in range(len(self.inputs.transforms)): - retval.append('--transform %s' % (self._format_transform(ii))) - for metric in self._format_metric(ii): - retval.append('--metric %s' % metric) - retval.append('--convergence %s' % self._format_convergence(ii)) - if isdefined(self.inputs.sigma_units): - retval.append('--smoothing-sigmas %s%s' % - (self._format_xarray(self.inputs.smoothing_sigmas[ii]), - self.inputs.sigma_units[ii])) - else: - retval.append('--smoothing-sigmas %s' % - self._format_xarray(self.inputs.smoothing_sigmas[ii])) - retval.append('--shrink-factors %s' % - self._format_xarray(self.inputs.shrink_factors[ii])) - if isdefined(self.inputs.use_estimate_learning_rate_once): - retval.append('--use-estimate-learning-rate-once %d' % - self.inputs.use_estimate_learning_rate_once[ii]) - if isdefined(self.inputs.use_histogram_matching): - # use_histogram_matching is either a common flag for all transforms - # or a list of transform-specific flags - if isinstance(self.inputs.use_histogram_matching, bool): - histval = self.inputs.use_histogram_matching - else: - histval = self.inputs.use_histogram_matching[ii] - retval.append('--use-histogram-matching %d' % histval) - return " ".join(retval) - - def _get_outputfilenames(self, inverse=False): - output_filename = None - if not inverse: - if isdefined(self.inputs.output_warped_image) and \ - self.inputs.output_warped_image: - output_filename = self.inputs.output_warped_image - if isinstance(output_filename, bool): - output_filename = '%s_Warped.nii.gz' % self.inputs.output_transform_prefix - else: - output_filename = output_filename - return output_filename - inv_output_filename = None - if isdefined(self.inputs.output_inverse_warped_image) and \ - self.inputs.output_inverse_warped_image: - inv_output_filename = self.inputs.output_inverse_warped_image - if isinstance(inv_output_filename, bool): - inv_output_filename = '%s_InverseWarped.nii.gz' % self.inputs.output_transform_prefix - else: - inv_output_filename = inv_output_filename - return inv_output_filename - - def _format_convergence(self, ii): - convergence_iter = self._format_xarray(self.inputs.number_of_iterations[ii]) - if len(self.inputs.convergence_threshold) > ii: - convergence_value = self.inputs.convergence_threshold[ii] - else: - convergence_value = self.inputs.convergence_threshold[0] - if len(self.inputs.convergence_window_size) > ii: - convergence_ws = self.inputs.convergence_window_size[ii] - else: - convergence_ws = self.inputs.convergence_window_size[0] - return '[ %s, %g, %d ]' % (convergence_iter, convergence_value, convergence_ws) - - def _format_winsorize_image_intensities(self): - if not self.inputs.winsorize_upper_quantile > self.inputs.winsorize_lower_quantile: - raise RuntimeError("Upper bound MUST be more than lower bound: %g > %g" - % (self.inputs.winsorize_upper_quantile, self.inputs.winsorize_lower_quantile)) - self._quantilesDone = True - return '--winsorize-image-intensities [ %s, %s ]' % (self.inputs.winsorize_lower_quantile, - self.inputs.winsorize_upper_quantile) - - def _format_arg(self, opt, spec, val): - if opt == 'fixed_image_mask': - if isdefined(self.inputs.moving_image_mask): - return '--masks [ %s, %s ]' % (self.inputs.fixed_image_mask, - self.inputs.moving_image_mask) - else: - return '--masks %s' % self.inputs.fixed_image_mask - elif opt == 'transforms': - return self._format_registration() - elif opt == 'initial_moving_transform': - try: - do_invert_transform = int(self.inputs.invert_initial_moving_transform) - except ValueError: - do_invert_transform = 0 # Just do the default behavior - return '--initial-moving-transform [ %s, %d ]' % (self.inputs.initial_moving_transform, - do_invert_transform) - elif opt == 'initial_moving_transform_com': - try: - do_center_of_mass_init = int(self.inputs.initial_moving_transform_com) - except ValueError: - do_center_of_mass_init = 0 # Just do the default behavior - return '--initial-moving-transform [ %s, %s, %d ]' % (self.inputs.fixed_image[0], - self.inputs.moving_image[0], - do_center_of_mass_init) - elif opt == 'interpolation': - if self.inputs.interpolation in ['BSpline', 'MultiLabel', 'Gaussian'] and \ - isdefined(self.inputs.interpolation_parameters): - return '--interpolation %s[ %s ]' % (self.inputs.interpolation, - ', '.join([str(param) - for param in self.inputs.interpolation_parameters])) - else: - return '--interpolation %s' % self.inputs.interpolation - elif opt == 'output_transform_prefix': - out_filename = self._get_outputfilenames(inverse=False) - inv_out_filename = self._get_outputfilenames(inverse=True) - if out_filename and inv_out_filename: - return '--output [ %s, %s, %s ]' % (self.inputs.output_transform_prefix, - out_filename, - inv_out_filename) - elif out_filename: - return '--output [ %s, %s ]' % (self.inputs.output_transform_prefix, - out_filename) - else: - return '--output %s' % self.inputs.output_transform_prefix - elif opt == 'winsorize_upper_quantile' or opt == 'winsorize_lower_quantile': - if not self._quantilesDone: - return self._format_winsorize_image_intensities() - else: - self._quantilesDone = False - return '' # Must return something for argstr! - # This feature was removed from recent versions of antsRegistration due to corrupt outputs. - # elif opt == 'collapse_linear_transforms_to_fixed_image_header': - # return self._formatCollapseLinearTransformsToFixedImageHeader() - return super(Registration, self)._format_arg(opt, spec, val) - - def _output_filenames(self, prefix, count, transform, inverse=False): - self.low_dimensional_transform_map = {'Rigid': 'Rigid.mat', - 'Affine': 'Affine.mat', - 'GenericAffine': 'GenericAffine.mat', - 'CompositeAffine': 'Affine.mat', - 'Similarity': 'Similarity.mat', - 'Translation': 'Translation.mat', - 'BSpline': 'BSpline.txt', - 'Initial': 'DerivedInitialMovingTranslation.mat'} - if transform in list(self.low_dimensional_transform_map.keys()): - suffix = self.low_dimensional_transform_map[transform] - inverse_mode = inverse - else: - inverse_mode = False # These are not analytically invertable - if inverse: - suffix = 'InverseWarp.nii.gz' - else: - suffix = 'Warp.nii.gz' - return '%s%d%s' % (prefix, count, suffix), inverse_mode - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['forward_transforms'] = [] - outputs['forward_invert_flags'] = [] - outputs['reverse_transforms'] = [] - outputs['reverse_invert_flags'] = [] + def _post_run(self): + self.outputs.forward_transforms = [] + self.outputs.forward_invert_flags = [] + self.outputs.reverse_transforms = [] + self.outputs.reverse_invert_flags = [] # invert_initial_moving_transform should be always defined, even if # there's no initial transform @@ -892,18 +891,18 @@ def _list_outputs(self): if self.inputs.write_composite_transform: filename = self.inputs.output_transform_prefix + 'Composite.h5' - outputs['composite_transform'] = os.path.abspath(filename) + self.outputs.composite_transform = os.path.abspath(filename) filename = self.inputs.output_transform_prefix + \ 'InverseComposite.h5' - outputs['inverse_composite_transform'] = os.path.abspath(filename) + self.outputs.inverse_composite_transform = os.path.abspath(filename) else: # If composite transforms are written, then individuals are not written (as of 2014-10-26 if not self.inputs.collapse_output_transforms: transform_count = 0 if isdefined(self.inputs.initial_moving_transform): - outputs['forward_transforms'].append(self.inputs.initial_moving_transform) - outputs['forward_invert_flags'].append(invert_initial_moving_transform) - outputs['reverse_transforms'].insert(0, self.inputs.initial_moving_transform) - outputs['reverse_invert_flags'].insert(0, not invert_initial_moving_transform) # Prepend + self.outputs.forward_transforms.append(self.inputs.initial_moving_transform) + self.outputs.forward_invert_flags.append(invert_initial_moving_transform) + self.outputs.reverse_transforms.insert(0, self.inputs.initial_moving_transform) + self.outputs.reverse_invert_flags.insert(0, not invert_initial_moving_transform) # Prepend transform_count += 1 elif isdefined(self.inputs.initial_moving_transform_com): forward_filename, forward_inversemode = self._output_filenames( @@ -915,11 +914,11 @@ def _list_outputs(self): transform_count, 'Initial', True) - outputs['forward_transforms'].append(os.path.abspath(forward_filename)) - outputs['forward_invert_flags'].append(False) - outputs['reverse_transforms'].insert(0, + self.outputs.forward_transforms.append(os.path.abspath(forward_filename)) + self.outputs.forward_invert_flags.append(False) + self.outputs.reverse_transforms.insert(0, os.path.abspath(reverse_filename)) - outputs['reverse_invert_flags'].insert(0, True) + self.outputs.reverse_invert_flags.insert(0, True) transform_count += 1 for count in range(len(self.inputs.transforms)): @@ -929,10 +928,10 @@ def _list_outputs(self): reverse_filename, reverse_inversemode = self._output_filenames( self.inputs.output_transform_prefix, transform_count, self.inputs.transforms[count], True) - outputs['forward_transforms'].append(os.path.abspath(forward_filename)) - outputs['forward_invert_flags'].append(forward_inversemode) - outputs['reverse_transforms'].insert(0, os.path.abspath(reverse_filename)) - outputs['reverse_invert_flags'].insert(0, reverse_inversemode) + self.outputs.forward_transforms.append(os.path.abspath(forward_filename)) + self.outputs.forward_invert_flags.append(forward_inversemode) + self.outputs.reverse_transforms.insert(0, os.path.abspath(reverse_filename)) + self.outputs.reverse_invert_flags.insert(0, reverse_inversemode) transform_count += 1 else: transform_count = 0 @@ -960,18 +959,18 @@ def _list_outputs(self): transform_count, transform, inverse=True) - outputs['forward_transforms'].append(os.path.abspath(forward_filename)) - outputs['forward_invert_flags'].append(forward_inversemode) - outputs['reverse_transforms'].append(os.path.abspath(reverse_filename)) - outputs['reverse_invert_flags'].append(reverse_inversemode) + self.outputs.forward_transforms.append(os.path.abspath(forward_filename)) + self.outputs.forward_invert_flags.append(forward_inversemode) + self.outputs.reverse_transforms.append(os.path.abspath(reverse_filename)) + self.outputs.reverse_invert_flags.append(reverse_inversemode) transform_count += 1 out_filename = self._get_outputfilenames(inverse=False) inv_out_filename = self._get_outputfilenames(inverse=True) if out_filename: - outputs['warped_image'] = os.path.abspath(out_filename) + self.outputs.warped_image = os.path.abspath(out_filename) if inv_out_filename: - outputs['inverse_warped_image'] = os.path.abspath(inv_out_filename) + self.outputs.inverse_warped_image = os.path.abspath(inv_out_filename) if len(self.inputs.save_state): - outputs['save_state'] = os.path.abspath(self.inputs.save_state) - return outputs + self.outputs.save_state = os.path.abspath(self.inputs.save_state) + diff --git a/nipype/interfaces/ants/resampling.py b/nipype/interfaces/ants/resampling.py index 6191324771..ece2a0803a 100644 --- a/nipype/interfaces/ants/resampling.py +++ b/nipype/interfaces/ants/resampling.py @@ -18,12 +18,14 @@ class WarpTimeSeriesImageMultiTransformInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(4, 3, argstr='%d', usedefault=True, desc='image dimension (3 or 4)', position=1) - input_image = File(argstr='%s', mandatory=True, copyfile=True, - desc=('image to apply transformation to (generally a ' - 'coregistered functional)')) - out_postfix = traits.Str('_wtsimt', argstr='%s', usedefault=True, - desc=('Postfix that is prepended to all output ' - 'files (default = _wtsimt)')) + input_image = File( + argstr='%s', mandatory=True, copyfile=True, + desc='image to apply transformation to (generally a coregistered functional)') + output_image = File(name_source='input_image', name_template='%s_wtsimt', argstr='%s', + keep_extension=True, desc='filename of output warped image') + out_postfix = traits.Str( + '_wtsimt', argstr='%s', deprecated=True, new_name='output_image', + desc='Postfix that is prepended to all output files (default = _wtsimt)') reference_image = File(argstr='-R %s', xor=['tightest_box'], desc='reference image space that you wish to warp INTO') tightest_box = traits.Bool(argstr='--tightest-bounding-box', @@ -46,6 +48,20 @@ class WarpTimeSeriesImageMultiTransformInputSpec(ANTSCommandInputSpec): 'E.g.: [1,4,5] inverts the 1st, 4th, and 5th Affines ' 'found in transformation_series')) + def _format_arg(self, opt, spec, val): + if opt == 'transformation_series': + series = [] + affine_counter = 0 + for transformation in val: + if 'Affine' in transformation and \ + isdefined(self.invert_affine): + affine_counter += 1 + if affine_counter in self.invert_affine: + series += ['-i'], + series += [transformation] + return ' '.join(series) + return super( + WarpTimeSeriesImageMultiTransformInputSpec, self)._format_arg(opt, spec, val) class WarpTimeSeriesImageMultiTransformOutputSpec(TraitedSpec): output_image = File(exists=True, desc='Warped image') @@ -72,33 +88,6 @@ class WarpTimeSeriesImageMultiTransform(ANTSCommand): input_spec = WarpTimeSeriesImageMultiTransformInputSpec output_spec = WarpTimeSeriesImageMultiTransformOutputSpec - def _format_arg(self, opt, spec, val): - if opt == 'out_postfix': - _, name, ext = split_filename( - os.path.abspath(self.inputs.input_image)) - return name + val + ext - if opt == 'transformation_series': - series = [] - affine_counter = 0 - for transformation in val: - if 'Affine' in transformation and \ - isdefined(self.inputs.invert_affine): - affine_counter += 1 - if affine_counter in self.inputs.invert_affine: - series += ['-i'], - series += [transformation] - return ' '.join(series) - return super(WarpTimeSeriesImageMultiTransform, self)._format_arg(opt, spec, val) - - def _list_outputs(self): - outputs = self._outputs().get() - _, name, ext = split_filename(os.path.abspath(self.inputs.input_image)) - outputs['output_image'] = os.path.join(os.getcwd(), - ''.join((name, - self.inputs.out_postfix, - ext))) - return outputs - def _run_interface(self, runtime, correct_return_codes=[0]): runtime = super(WarpTimeSeriesImageMultiTransform, self)._run_interface(runtime, correct_return_codes=[0, 1]) if "100 % complete" not in runtime.stdout: @@ -112,11 +101,12 @@ class WarpImageMultiTransformInputSpec(ANTSCommandInputSpec): input_image = File(argstr='%s', mandatory=True, desc=('image to apply transformation to (generally a ' 'coregistered functional)'), position=2) - output_image = File(genfile=True, hash_files=False, argstr='%s', - desc='name of the output warped image', position=3, xor=['out_postfix']) - out_postfix = File("_wimt", usedefault=True, hash_files=False, - desc=('Postfix that is prepended to all output ' - 'files (default = _wimt)'), xor=['output_image']) + output_image = File(name_source='input_image', name_template='%s_wimt', argstr='%s', + keep_extension=True, desc='filename of output warped image') + out_postfix = File( + "_wimt", usedefault=True, hash_files=False, deprecated=True, new_name='output_image', + desc=('Postfix that is prepended to all output files (default = _wimt)'), + xor=['output_image']) reference_image = File(argstr='-R %s', xor=['tightest_box'], desc='reference image space that you wish to warp INTO') tightest_box = traits.Bool(argstr='--tightest-bounding-box', @@ -142,6 +132,20 @@ class WarpImageMultiTransformInputSpec(ANTSCommandInputSpec): 'transformations are distinguished ' 'from warp fields by the word "affine" included in their filenames.')) + def _format_arg(self, opt, spec, val): + if opt == 'transformation_series': + series = [] + affine_counter = 0 + for transformation in val: + if "affine" in transformation.lower() and \ + isdefined(self.invert_affine): + affine_counter += 1 + if affine_counter in self.invert_affine: + series += '-i', + series += [transformation] + return ' '.join(series) + return super(WarpImageMultiTransformInputSpec, self)._format_arg(opt, spec, val) + class WarpImageMultiTransformOutputSpec(TraitedSpec): output_image = File(exists=True, desc='Warped image') @@ -178,36 +182,6 @@ class WarpImageMultiTransform(ANTSCommand): input_spec = WarpImageMultiTransformInputSpec output_spec = WarpImageMultiTransformOutputSpec - def _gen_filename(self, name): - if name == 'output_image': - _, name, ext = split_filename( - os.path.abspath(self.inputs.input_image)) - return ''.join((name, self.inputs.out_postfix, ext)) - return None - - def _format_arg(self, opt, spec, val): - if opt == 'transformation_series': - series = [] - affine_counter = 0 - for transformation in val: - if "affine" in transformation.lower() and \ - isdefined(self.inputs.invert_affine): - affine_counter += 1 - if affine_counter in self.inputs.invert_affine: - series += '-i', - series += [transformation] - return ' '.join(series) - return super(WarpImageMultiTransform, self)._format_arg(opt, spec, val) - - def _list_outputs(self): - outputs = self._outputs().get() - if isdefined(self.inputs.output_image): - outputs['output_image'] = os.path.abspath(self.inputs.output_image) - else: - outputs['output_image'] = os.path.abspath( - self._gen_filename('output_image')) - return outputs - class ApplyTransformsInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(2, 3, 4, argstr='--dimensionality %d', @@ -224,8 +198,8 @@ class ApplyTransformsInputSpec(ANTSCommandInputSpec): desc=('image to apply transformation to (generally a ' 'coregistered functional)'), exists=True) - output_image = traits.Str(argstr='--output %s', desc='output file name', - genfile=True, hash_files=False) + output_image = File(name_source='input_image', name_template='%s_warped', keep_extension=True, + argstr='--output %s', desc='output file name', hash_files=False) out_postfix = traits.Str("_trans", usedefault=True, desc=('Postfix that is appended to all output ' 'files (default = _trans)')) @@ -250,10 +224,46 @@ class ApplyTransformsInputSpec(ANTSCommandInputSpec): File(exists=True), argstr='%s', mandatory=True, desc='transform files: will be applied in reverse order. For example, the last specified transform will be applied first') invert_transform_flags = InputMultiPath(traits.Bool()) default_value = traits.Float(0.0, argstr='--default-value %g', usedefault=True) - print_out_composite_warp_file = traits.Bool(False, requires=["output_image"], - desc='output a composite warp file instead of a transformed image') + print_out_composite_warp_file = traits.Bool( + False, usedefault=True, desc='output a composite warp file instead of a transformed image') float = traits.Bool(argstr='--float %d', default=False, desc='Use float instead of double for computations.') + def _get_transform_filenames(self): + retval = [] + for ii in range(len(self.transforms)): + if isdefined(self.invert_transform_flags): + if len(self.transforms) == len(self.invert_transform_flags): + invert_code = 1 if self.invert_transform_flags[ + ii] else 0 + retval.append("--transform [ %s, %d ]" % + (self.transforms[ii], invert_code)) + else: + raise Exception(("ERROR: The useInverse list must have the same number " + "of entries as the transformsFileName list.")) + else: + retval.append("--transform %s" % self.transforms[ii]) + return " ".join(retval) + + def _format_arg(self, opt, spec, val): + retval = super(ApplyTransformsInputSpec, self)._format_arg(opt, spec, val) + + if opt == "output_image": + if self.print_out_composite_warp_file: + modval = super(ApplyTransformsInputSpec, + self)._format_arg('print_out_composite_warp_file') + return '--output [ ' + retval[6:] + ', ' + modval + ' ]' + elif opt == "transforms": + return self._get_transform_filenames() + elif opt == 'interpolation': + if self.interpolation in ['BSpline', 'MultiLabel', 'Gaussian'] and \ + isdefined(self.interpolation_parameters): + return '--interpolation %s[ %s ]' % (self.interpolation, + ', '.join([str(param) + for param in self.interpolation_parameters])) + else: + return '--interpolation %s' % self.interpolation + return retval + class ApplyTransformsOutputSpec(TraitedSpec): output_image = File(exists=True, desc='Warped image') @@ -302,59 +312,6 @@ class ApplyTransforms(ANTSCommand): input_spec = ApplyTransformsInputSpec output_spec = ApplyTransformsOutputSpec - def _gen_filename(self, name): - if name == 'output_image': - output = self.inputs.output_image - if not isdefined(output): - _, name, ext = split_filename(self.inputs.input_image) - output = name + self.inputs.out_postfix + ext - return output - return None - - def _get_transform_filenames(self): - retval = [] - for ii in range(len(self.inputs.transforms)): - if isdefined(self.inputs.invert_transform_flags): - if len(self.inputs.transforms) == len(self.inputs.invert_transform_flags): - invert_code = 1 if self.inputs.invert_transform_flags[ - ii] else 0 - retval.append("--transform [ %s, %d ]" % - (self.inputs.transforms[ii], invert_code)) - else: - raise Exception(("ERROR: The useInverse list must have the same number " - "of entries as the transformsFileName list.")) - else: - retval.append("--transform %s" % self.inputs.transforms[ii]) - return " ".join(retval) - - def _get_output_warped_filename(self): - if isdefined(self.inputs.print_out_composite_warp_file): - return "--output [ %s, %d ]" % (self._gen_filename("output_image"), - int(self.inputs.print_out_composite_warp_file)) - else: - return "--output %s" % (self._gen_filename("output_image")) - - def _format_arg(self, opt, spec, val): - if opt == "output_image": - return self._get_output_warped_filename() - elif opt == "transforms": - return self._get_transform_filenames() - elif opt == 'interpolation': - if self.inputs.interpolation in ['BSpline', 'MultiLabel', 'Gaussian'] and \ - isdefined(self.inputs.interpolation_parameters): - return '--interpolation %s[ %s ]' % (self.inputs.interpolation, - ', '.join([str(param) - for param in self.inputs.interpolation_parameters])) - else: - return '--interpolation %s' % self.inputs.interpolation - return super(ApplyTransforms, self)._format_arg(opt, spec, val) - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_image'] = os.path.abspath( - self._gen_filename('output_image')) - return outputs - class ApplyTransformsToPointsInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(2, 3, 4, argstr='--dimensionality %d', @@ -381,6 +338,27 @@ class ApplyTransformsToPointsInputSpec(ANTSCommandInputSpec): invert_transform_flags = traits.List(traits.Bool(), desc='list indicating if a transform should be reversed') + def _get_transform_filenames(self): + retval = [] + for ii in range(len(self.transforms)): + if isdefined(self.invert_transform_flags): + if len(self.transforms) == len(self.invert_transform_flags): + invert_code = 1 if self.invert_transform_flags[ + ii] else 0 + retval.append("--transform [ %s, %d ]" % + (self.transforms[ii], invert_code)) + else: + raise Exception(("ERROR: The useInverse list must have the same number " + "of entries as the transformsFileName list.")) + else: + retval.append("--transform %s" % self.transforms[ii]) + return " ".join(retval) + + def _format_arg(self, opt, spec, val): + if opt == "transforms": + return self._get_transform_filenames() + return super(ApplyTransformsToPointsInputSpec, self)._format_arg(opt, spec, val) + class ApplyTransformsToPointsOutputSpec(TraitedSpec): output_file = File(exists=True, desc='csv file with transformed coordinates') @@ -408,24 +386,3 @@ class ApplyTransformsToPoints(ANTSCommand): _cmd = 'antsApplyTransformsToPoints' input_spec = ApplyTransformsToPointsInputSpec output_spec = ApplyTransformsToPointsOutputSpec - - def _get_transform_filenames(self): - retval = [] - for ii in range(len(self.inputs.transforms)): - if isdefined(self.inputs.invert_transform_flags): - if len(self.inputs.transforms) == len(self.inputs.invert_transform_flags): - invert_code = 1 if self.inputs.invert_transform_flags[ - ii] else 0 - retval.append("--transform [ %s, %d ]" % - (self.inputs.transforms[ii], invert_code)) - else: - raise Exception(("ERROR: The useInverse list must have the same number " - "of entries as the transformsFileName list.")) - else: - retval.append("--transform %s" % self.inputs.transforms[ii]) - return " ".join(retval) - - def _format_arg(self, opt, spec, val): - if opt == "transforms": - return self._get_transform_filenames() - return super(ApplyTransformsToPoints, self)._format_arg(opt, spec, val) diff --git a/nipype/interfaces/ants/segmentation.py b/nipype/interfaces/ants/segmentation.py index 619057b693..cd9bf36795 100644 --- a/nipype/interfaces/ants/segmentation.py +++ b/nipype/interfaces/ants/segmentation.py @@ -47,11 +47,57 @@ class AtroposInputSpec(ANTSCommandInputSpec): usedefault=True) use_mixture_model_proportions = traits.Bool( requires=['posterior_formulation']) - out_classified_image_name = File(argstr="%s", genfile=True, - hash_files=False) - save_posteriors = traits.Bool() - output_posteriors_name_template = traits.Str('POSTERIOR_%02d.nii.gz', - usedefault=True) + out_classified_image_name = File( + name_source='intensity_images', name_template='%s_labeled', keep_extension=True, + argstr='--output [%s]', hash_files=False) + save_posteriors = traits.Bool(False, usedefault=True, desc='save posterior probability maps') + posteriors = File('posterior_%02d.nii.gz', usedefault=True) + + def _format_arg(self, opt, spec, val): + if opt == 'initialization': + retval = "--initialization %s[%d" % (val, + self.number_of_tissue_classes) + if val == "PriorProbabilityImages": + _, _, ext = split_filename( + self.prior_probability_images[0]) + retval += ",priors/priorProbImages%02d" + \ + ext + ",%g" % self.prior_weighting + if isdefined(self.prior_probability_threshold): + retval += ",%g" % self.prior_probability_threshold + return retval + "]" + if opt == 'mrf_smoothing_factor': + retval = "--mrf [%g" % val + if isdefined(self.mrf_radius): + retval += ",%s" % self._format_xarray([str(s) for s in self.mrf_radius]) + return retval + "]" + if opt == "icm_use_synchronous_update": + retval = "--icm [%d" % val + if isdefined(self.maximum_number_of_icm_terations): + retval += ",%g" % self.maximum_number_of_icm_terations + return retval + "]" + if opt == "n_iterations": + retval = "--convergence [%d" % val + if isdefined(self.convergence_threshold): + retval += ",%g" % self.convergence_threshold + return retval + "]" + if opt == "posterior_formulation": + retval = "--posterior-formulation %s" % val + if isdefined(self.use_mixture_model_proportions): + retval += "[%d]" % self.use_mixture_model_proportions + return retval + if opt == "out_classified_image_name": + retval = super(AtroposInputSpec, self)._format_arg(opt, spec, val) + if self.save_posteriors: + retval = retval[:-1] + ', ' + super( + AtroposInputSpec, self)._format_arg('posteriors') + ']' + return retval + return super(AtroposInputSpec, self)._format_arg(opt, spec, val) + + def parse_args(self, skip=None): + if skip is None: + skip = [] + skip += ['save_posteriors', 'posteriors'] + return super(AtroposInputSpec, self).parse_args(skip=skip) class AtroposOutputSpec(TraitedSpec): @@ -99,45 +145,6 @@ class Atropos(ANTSCommand): output_spec = AtroposOutputSpec _cmd = 'Atropos' - def _format_arg(self, opt, spec, val): - if opt == 'initialization': - retval = "--initialization %s[%d" % (val, - self.inputs.number_of_tissue_classes) - if val == "PriorProbabilityImages": - _, _, ext = split_filename( - self.inputs.prior_probability_images[0]) - retval += ",priors/priorProbImages%02d" + \ - ext + ",%g" % self.inputs.prior_weighting - if isdefined(self.inputs.prior_probability_threshold): - retval += ",%g" % self.inputs.prior_probability_threshold - return retval + "]" - if opt == 'mrf_smoothing_factor': - retval = "--mrf [%g" % val - if isdefined(self.inputs.mrf_radius): - retval += ",%s" % self._format_xarray([str(s) for s in self.inputs.mrf_radius]) - return retval + "]" - if opt == "icm_use_synchronous_update": - retval = "--icm [%d" % val - if isdefined(self.inputs.maximum_number_of_icm_terations): - retval += ",%g" % self.inputs.maximum_number_of_icm_terations - return retval + "]" - if opt == "n_iterations": - retval = "--convergence [%d" % val - if isdefined(self.inputs.convergence_threshold): - retval += ",%g" % self.inputs.convergence_threshold - return retval + "]" - if opt == "posterior_formulation": - retval = "--posterior-formulation %s" % val - if isdefined(self.inputs.use_mixture_model_proportions): - retval += "[%d]" % self.inputs.use_mixture_model_proportions - return retval - if opt == "out_classified_image_name": - retval = "--output [%s" % val - if isdefined(self.inputs.save_posteriors): - retval += ",%s" % self.inputs.output_posteriors_name_template - return retval + "]" - return super(ANTSCommand, self)._format_arg(opt, spec, val) - def _run_interface(self, runtime, correct_return_codes=[0]): if self.inputs.initialization == "PriorProbabilityImages": priors_directory = os.path.join(os.getcwd(), "priors") @@ -153,25 +160,6 @@ def _run_interface(self, runtime, correct_return_codes=[0]): runtime = super(Atropos, self)._run_interface(runtime) return runtime - def _gen_filename(self, name): - if name == 'out_classified_image_name': - output = self.inputs.out_classified_image_name - if not isdefined(output): - _, name, ext = split_filename(self.inputs.intensity_images[0]) - output = name + '_labeled' + ext - return output - return None - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['classified_image'] = os.path.abspath( - self._gen_filename('out_classified_image_name')) - if isdefined(self.inputs.save_posteriors) and self.inputs.save_posteriors: - outputs['posteriors'] = [] - for i in range(self.inputs.number_of_tissue_classes): - outputs['posteriors'].append(os.path.abspath(self.inputs.output_posteriors_name_template % (i + 1))) - return outputs - class LaplacianThicknessInputSpec(ANTSCommandInputSpec): input_wm = File(argstr='%s', mandatory=True, copyfile=True, @@ -224,14 +212,12 @@ def _gen_filename(self, name): return output return None - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): _, name, ext = split_filename(os.path.abspath(self.inputs.input_wm)) - outputs['output_image'] = os.path.join(os.getcwd(), + self.outputs.output_image = os.path.join(os.getcwd(), ''.join((name, self.inputs.output_image, ext))) - return outputs class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec): @@ -243,9 +229,10 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec): 'coregistered functional)')) mask_image = File(argstr='--mask-image %s') weight_image = File(argstr='--weight-image %s') - output_image = traits.Str(argstr='--output %s', - desc='output file name', genfile=True, - hash_files=False) + output_image = File( + name_source='input_image', name_template='%s_corrected', + argstr='--output %s', desc='output file name', keep_extension=True, + hash_files=False) bspline_fitting_distance = traits.Float(argstr="--bspline-fitting %s") bspline_order = traits.Int(requires=['bspline_fitting_distance']) shrink_factor = traits.Int(argstr="--shrink-factor %d") @@ -254,9 +241,41 @@ class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec): save_bias = traits.Bool(False, mandatory=True, usedefault=True, desc=('True if the estimated bias should be saved' ' to file.'), xor=['bias_image']) - bias_image = File(desc='Filename for the estimated bias.', - hash_files=False) + bias_image = File(name_source='input_image', name_template='%s_bias', + keep_extension=True, hash_files=False, argstr='%s', + desc='Filename for the estimated bias.') + def _format_arg(self, name, trait_spec, value): + if name == 'bspline_fitting_distance': + if isdefined(self.bspline_order): + newval = '[ %g, %d ]' % (value, self.bspline_order) + else: + newval = '[ %g ]' % value + return trait_spec.argstr % newval + + if name == 'n_iterations': + if isdefined(self.convergence_threshold): + newval = '[ %s, %g ]' % (self._format_xarray([str(elt) for elt in value]), + self.convergence_threshold) + else: + newval = '[ %s ]' % self._format_xarray([str(elt) for elt in value]) + return trait_spec.argstr % newval + + if name == 'output_image' and self.save_bias: + val_out = super(N4BiasFieldCorrectionInputSpec, self)._format_arg( + name, trait_spec, value) + val_bias = super(N4BiasFieldCorrectionInputSpec, self)._format_arg('bias_image') + retval = '[ ' + val_out[9:] + ', ' + val_bias + ' ]' + return trait_spec.argstr % retval + + return super(N4BiasFieldCorrectionInputSpec, + self)._format_arg(name, trait_spec, value) + + def parse_args(self, skip=None): + if skip is None: + skip = [] + skip += ['save_bias', 'bias_image'] + return super(N4BiasFieldCorrectionInputSpec, self).parse_args(skip=skip) class N4BiasFieldCorrectionOutputSpec(TraitedSpec): output_image = File(exists=True, desc='Warped image') @@ -323,64 +342,6 @@ class N4BiasFieldCorrection(ANTSCommand): input_spec = N4BiasFieldCorrectionInputSpec output_spec = N4BiasFieldCorrectionOutputSpec - def _gen_filename(self, name): - if name == 'output_image': - output = self.inputs.output_image - if not isdefined(output): - _, name, ext = split_filename(self.inputs.input_image) - output = name + '_corrected' + ext - return output - - if name == 'bias_image': - output = self.inputs.bias_image - if not isdefined(output): - _, name, ext = split_filename(self.inputs.input_image) - output = name + '_bias' + ext - return output - return None - - def _format_arg(self, name, trait_spec, value): - if ((name == 'output_image') and - (self.inputs.save_bias or isdefined(self.inputs.bias_image))): - bias_image = self._gen_filename('bias_image') - output = self._gen_filename('output_image') - newval = '[ %s, %s ]' % (output, bias_image) - return trait_spec.argstr % newval - - if name == 'bspline_fitting_distance': - if isdefined(self.inputs.bspline_order): - newval = '[ %g, %d ]' % (value, self.inputs.bspline_order) - else: - newval = '[ %g ]' % value - return trait_spec.argstr % newval - - if name == 'n_iterations': - if isdefined(self.inputs.convergence_threshold): - newval = '[ %s, %g ]' % (self._format_xarray([str(elt) for elt in value]), - self.inputs.convergence_threshold) - else: - newval = '[ %s ]' % self._format_xarray([str(elt) for elt in value]) - return trait_spec.argstr % newval - - return super(N4BiasFieldCorrection, - self)._format_arg(name, trait_spec, value) - - def _parse_inputs(self, skip=None): - if skip is None: - skip = [] - skip += ['save_bias', 'bias_image'] - return super(N4BiasFieldCorrection, self)._parse_inputs(skip=skip) - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_image'] = os.path.abspath( - self._gen_filename('output_image')) - - if self.inputs.save_bias or isdefined(self.inputs.bias_image): - outputs['bias_image'] = os.path.abspath( - self._gen_filename('bias_image')) - return outputs - class CorticalThicknessInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(3, 2, argstr='-d %d', usedefault=True, @@ -432,7 +393,7 @@ class CorticalThicknessInputSpec(ANTSCommandInputSpec): posterior_formulation = traits.Str(argstr='-b %s', desc=('Atropos posterior formulation and whether or not' 'to use mixture model proportions.' - '''e.g 'Socrates[1]' (default) or 'Aristotle[1]'.''' + """e.g 'Socrates[1]' (default) or 'Aristotle[1]'.""" 'Choose the latter if you' 'want use the distance priors (see also the -l option' 'for label propagation control).')) @@ -449,7 +410,7 @@ class CorticalThicknessInputSpec(ANTSCommandInputSpec): desc='Cortical ROI labels to use as a prior for ATITH.') label_propagation = traits.Str(argstr='-l %s', desc=('Incorporate a distance prior one the posterior formulation. Should be' - '''of the form 'label[lambda,boundaryProbability]' where label''' + """of the form 'label[lambda,boundaryProbability]' where label""" 'is a value of 1,2,3,... denoting label ID. The label' 'probability for anything outside the current label' ' = boundaryProbability * exp( -lambda * distanceFromBoundary )' @@ -467,6 +428,29 @@ class CorticalThicknessInputSpec(ANTSCommandInputSpec): 'Requires single thread computation for complete reproducibility.')) + def _format_arg(self, opt, spec, val): + if opt == 'anatomical_image': + retval = '-a %s' % val + return retval + if opt == 'brain_template': + retval = '-e %s' % val + return retval + if opt == 'brain_probability_mask': + retval = '-m %s' % val + return retval + if opt == 'out_prefix': + retval = '-o %s' % val + return retval + if opt == 't1_registration_template': + retval = '-t %s' % val + return retval + if opt == 'segmentation_priors': + _, _, ext = split_filename(self.segmentation_priors[0]) + retval = "-p nipype_priors/BrainSegmentationPrior%02d" + ext + return retval + return super(CorticalThicknessInputSpec, self)._format_arg(opt, spec, val) + + class CorticalThicknessOutputSpec(TraitedSpec): BrainExtractionMask = File(exists=True, desc='brain extraction mask') BrainSegmentation = File(exists=True, desc='brain segmentaion image') @@ -510,28 +494,6 @@ class CorticalThickness(ANTSCommand): output_spec = CorticalThicknessOutputSpec _cmd = 'antsCorticalThickness.sh' - def _format_arg(self, opt, spec, val): - if opt == 'anatomical_image': - retval = '-a %s' % val - return retval - if opt == 'brain_template': - retval = '-e %s' % val - return retval - if opt == 'brain_probability_mask': - retval = '-m %s' % val - return retval - if opt == 'out_prefix': - retval = '-o %s' % val - return retval - if opt == 't1_registration_template': - retval = '-t %s' % val - return retval - if opt == 'segmentation_priors': - _, _, ext = split_filename(self.inputs.segmentation_priors[0]) - retval = "-p nipype_priors/BrainSegmentationPrior%02d" + ext - return retval - return super(ANTSCommand, self)._format_arg(opt, spec, val) - def _run_interface(self, runtime, correct_return_codes=[0]): priors_directory = os.path.join(os.getcwd(), "nipype_priors") if not os.path.exists(priors_directory): @@ -545,17 +507,16 @@ def _run_interface(self, runtime, correct_return_codes=[0]): runtime = super(CorticalThickness, self)._run_interface(runtime) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['BrainExtractionMask'] = os.path.join(os.getcwd(), + def _post_run(self): + self.outputs.BrainExtractionMask = os.path.join(os.getcwd(), self.inputs.out_prefix + 'BrainExtractionMask.' + self.inputs.image_suffix) - outputs['BrainSegmentation'] = os.path.join(os.getcwd(), + self.outputs.BrainSegmentation = os.path.join(os.getcwd(), self.inputs.out_prefix + 'BrainSegmentation.' + self.inputs.image_suffix) - outputs['BrainSegmentationN4'] = os.path.join(os.getcwd(), + self.outputs.BrainSegmentationN4 = os.path.join(os.getcwd(), self.inputs.out_prefix + 'BrainSegmentation0N4.' + self.inputs.image_suffix) @@ -565,37 +526,36 @@ def _list_outputs(self): self.inputs.out_prefix + 'BrainSegmentationPosteriors%02d.' % (i + 1) + self.inputs.image_suffix)) - outputs['BrainSegmentationPosteriors'] = posteriors - outputs['CorticalThickness'] = os.path.join(os.getcwd(), + self.outputs.BrainSegmentationPosteriors = posteriors + self.outputs.CorticalThickness = os.path.join(os.getcwd(), self.inputs.out_prefix + 'CorticalThickness.' + self.inputs.image_suffix) - outputs['TemplateToSubject1GenericAffine'] = os.path.join(os.getcwd(), + self.outputs.TemplateToSubject1GenericAffine = os.path.join(os.getcwd(), self.inputs.out_prefix + 'TemplateToSubject1GenericAffine.mat') - outputs['TemplateToSubject0Warp'] = os.path.join(os.getcwd(), + self.outputs.TemplateToSubject0Warp = os.path.join(os.getcwd(), self.inputs.out_prefix + 'TemplateToSubject0Warp.' + self.inputs.image_suffix) - outputs['SubjectToTemplate1Warp'] = os.path.join(os.getcwd(), + self.outputs.SubjectToTemplate1Warp = os.path.join(os.getcwd(), self.inputs.out_prefix + 'SubjectToTemplate1Warp.' + self.inputs.image_suffix) - outputs['SubjectToTemplate0GenericAffine'] = os.path.join(os.getcwd(), + self.outputs.SubjectToTemplate0GenericAffine = os.path.join(os.getcwd(), self.inputs.out_prefix + 'SubjectToTemplate0GenericAffine.mat') - outputs['SubjectToTemplateLogJacobian'] = os.path.join(os.getcwd(), + self.outputs.SubjectToTemplateLogJacobian = os.path.join(os.getcwd(), self.inputs.out_prefix + 'SubjectToTemplateLogJacobian.' + self.inputs.image_suffix) - outputs['CorticalThicknessNormedToTemplate'] = os.path.join(os.getcwd(), + self.outputs.CorticalThicknessNormedToTemplate = os.path.join(os.getcwd(), self.inputs.out_prefix + 'CorticalThickness.' + self.inputs.image_suffix) - outputs['BrainVolumes'] = os.path.join(os.getcwd(), + self.outputs.BrainVolumes = os.path.join(os.getcwd(), self.inputs.out_prefix + 'brainvols.csv') - return outputs class antsCorticalThickness(CorticalThickness): @@ -671,17 +631,15 @@ class BrainExtraction(ANTSCommand): output_spec = BrainExtractionOutputSpec _cmd = 'antsBrainExtraction.sh' - def _list_outputs(self): - outputs = self._outputs().get() - outputs['BrainExtractionMask'] = os.path.join(os.getcwd(), + def _post_run(self): + self.outputs.BrainExtractionMask = os.path.join(os.getcwd(), self.inputs.out_prefix + 'BrainExtractionMask.' + self.inputs.image_suffix) - outputs['BrainExtractionBrain'] = os.path.join(os.getcwd(), + self.outputs.BrainExtractionBrain = os.path.join(os.getcwd(), self.inputs.out_prefix + 'BrainExtractionBrain.' + self.inputs.image_suffix) - return outputs class antsBrainExtraction(BrainExtraction): @@ -729,6 +687,24 @@ class JointFusionInputSpec(ANTSCommandInputSpec): atlas_group_weights = traits.ListInt(argstr='-gpw %d...', desc=('Assign the voting weights to ' 'each atlas group')) + def _format_arg(self, opt, spec, val): + if opt == 'method': + if '[' in val: + retval = '-m {0}'.format(val) + else: + retval = '-m {0}[{1},{2}]'.format( + self.method, self.alpha, self.beta) + elif opt == 'patch_radius': + retval = '-rp {0}'.format(self._format_xarray(val)) + elif opt == 'search_radius': + retval = '-rs {0}'.format(self._format_xarray(val)) + else: + if opt == 'warped_intensity_images': + assert len(val) == self.modalities * len(self.warped_label_images), \ + "Number of intensity images and label maps must be the same {0}!={1}".format( + len(val), len(self.warped_label_images)) + return super(JointFusionInputSpec, self)._format_arg(opt, spec, val) + return retval class JointFusionOutputSpec(TraitedSpec): @@ -771,30 +747,9 @@ class JointFusion(ANTSCommand): output_spec = JointFusionOutputSpec _cmd = 'jointfusion' - def _format_arg(self, opt, spec, val): - if opt == 'method': - if '[' in val: - retval = '-m {0}'.format(val) - else: - retval = '-m {0}[{1},{2}]'.format( - self.inputs.method, self.inputs.alpha, self.inputs.beta) - elif opt == 'patch_radius': - retval = '-rp {0}'.format(self._format_xarray(val)) - elif opt == 'search_radius': - retval = '-rs {0}'.format(self._format_xarray(val)) - else: - if opt == 'warped_intensity_images': - assert len(val) == self.inputs.modalities * len(self.inputs.warped_label_images), \ - "Number of intensity images and label maps must be the same {0}!={1}".format( - len(val), len(self.inputs.warped_label_images)) - return super(ANTSCommand, self)._format_arg(opt, spec, val) - return retval - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_label_image'] = os.path.abspath( + def _post_run(self): + self.outputs.output_label_image = os.path.abspath( self.inputs.output_label_image) - return outputs class DenoiseImageInputSpec(ANTSCommandInputSpec): @@ -820,11 +775,28 @@ class DenoiseImageInputSpec(ANTSCommandInputSpec): save_noise = traits.Bool(False, mandatory=True, usedefault=True, desc=('True if the estimated noise should be saved ' 'to file.'), xor=['noise_image']) - noise_image = File(name_source=['input_image'], hash_files=False, + noise_image = File(name_source=['input_image'], hash_files=False, argstr='%s', keep_extension=True, name_template='%s_noise', desc='Filename for the estimated noise.') verbose = traits.Bool(False, argstr="-v", desc=('Verbose output.')) + def _format_arg(self, name, trait_spec, value): + if (name == 'output_image') and self.save_noise: + val_out = super(DenoiseImageInputSpec, self)._format_arg( + name, trait_spec, value) + val_noise = super(DenoiseImageInputSpec, self)._format_arg('noise_image') + newval = '[ ' + val_out[3:] + ', ' + val_noise + ' ]' + return trait_spec.argstr % newval + + return super(DenoiseImageInputSpec, + self)._format_arg(name, trait_spec, value) + + def parse_args(self, skip=None): + if skip is None: + skip = [] + skip += ['save_noise', 'noise_image'] + return super(DenoiseImageInputSpec, self).parse_args(skip) + class DenoiseImageOutputSpec(TraitedSpec): output_image = File(exists=True) @@ -860,16 +832,6 @@ class DenoiseImage(ANTSCommand): output_spec = DenoiseImageOutputSpec _cmd = 'DenoiseImage' - def _format_arg(self, name, trait_spec, value): - if ((name == 'output_image') and - (self.inputs.save_noise or isdefined(self.inputs.noise_image))): - newval = '[ %s, %s ]' % (self._filename_from_source('output_image'), - self._filename_from_source('noise_image')) - return trait_spec.argstr % newval - - return super(DenoiseImage, - self)._format_arg(name, trait_spec, value) - class AntsJointFusionInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(3, 2, 4, argstr='-d %d', usedefault=False, @@ -938,6 +900,46 @@ class AntsJointFusionInputSpec(ANTSCommandInputSpec): 'file name format.') verbose = traits.Bool(False, argstr="-v", desc=('Verbose output.')) + def _format_arg(self, opt, spec, val): + if opt == 'exclusion_image_label': + retval = [] + for ii in range(len(self.exclusion_image_label)): + retval.append('-e {0}[{1}]'.format( + self.exclusion_image_label[ii], + self.exclusion_image[ii])) + retval = ' '.join(retval) + elif opt == 'patch_radius': + retval = '-p {0}'.format(self._format_xarray(val)) + elif opt == 'search_radius': + retval = '-s {0}'.format(self._format_xarray(val)) + elif opt == 'out_label_fusion': + if isdefined(self.out_intensity_fusion_name_format): + if isdefined(self.out_label_post_prob_name_format): + if isdefined(self.out_atlas_voting_weight_name_format): + retval = '-o [{0}, {1}, {2}, {3}]'.format(self.out_label_fusion, + self.out_intensity_fusion_name_format, + self.out_label_post_prob_name_format, + self.out_atlas_voting_weight_name_format) + else: + retval = '-o [{0}, {1}, {2}]'.format(self.out_label_fusion, + self.out_intensity_fusion_name_format, + self.out_label_post_prob_name_format) + else: + retval = '-o [{0}, {1}]'.format(self.out_label_fusion, + self.out_intensity_fusion_name_format) + else: + retval = '-o {0}'.format(self.out_label_fusion) + elif opt == 'out_intensity_fusion_name_format': + retval = '' + if not isdefined(self.out_label_fusion): + retval = '-o {0}'.format(self.out_intensity_fusion_name_format) + else: + if opt == 'atlas_segmentation_image': + assert len(val) == len(self.atlas_image), "Number of specified " \ + "segmentations should be identical to the number of atlas image " \ + "sets {0}!={1}".format(len(val), len(self.atlas_image)) + return super(AntsJointFusionInputSpec, self)._format_arg(opt, spec, val) + return retval class AntsJointFusionOutputSpec(TraitedSpec): out_label_fusion = File(exists=True) @@ -1010,49 +1012,8 @@ class AntsJointFusion(ANTSCommand): output_spec = AntsJointFusionOutputSpec _cmd = 'antsJointFusion' - def _format_arg(self, opt, spec, val): - if opt == 'exclusion_image_label': - retval = [] - for ii in range(len(self.inputs.exclusion_image_label)): - retval.append('-e {0}[{1}]'.format( - self.inputs.exclusion_image_label[ii], - self.inputs.exclusion_image[ii])) - retval = ' '.join(retval) - elif opt == 'patch_radius': - retval = '-p {0}'.format(self._format_xarray(val)) - elif opt == 'search_radius': - retval = '-s {0}'.format(self._format_xarray(val)) - elif opt == 'out_label_fusion': - if isdefined(self.inputs.out_intensity_fusion_name_format): - if isdefined(self.inputs.out_label_post_prob_name_format): - if isdefined(self.inputs.out_atlas_voting_weight_name_format): - retval = '-o [{0}, {1}, {2}, {3}]'.format(self.inputs.out_label_fusion, - self.inputs.out_intensity_fusion_name_format, - self.inputs.out_label_post_prob_name_format, - self.inputs.out_atlas_voting_weight_name_format) - else: - retval = '-o [{0}, {1}, {2}]'.format(self.inputs.out_label_fusion, - self.inputs.out_intensity_fusion_name_format, - self.inputs.out_label_post_prob_name_format) - else: - retval = '-o [{0}, {1}]'.format(self.inputs.out_label_fusion, - self.inputs.out_intensity_fusion_name_format) - else: - retval = '-o {0}'.format(self.inputs.out_label_fusion) - elif opt == 'out_intensity_fusion_name_format': - retval = '' - if not isdefined(self.inputs.out_label_fusion): - retval = '-o {0}'.format(self.inputs.out_intensity_fusion_name_format) - else: - if opt == 'atlas_segmentation_image': - assert len(val) == len(self.inputs.atlas_image), "Number of specified " \ - "segmentations should be identical to the number of atlas image " \ - "sets {0}!={1}".format(len(val), len(self.inputs.atlas_image)) - return super(ANTSCommand, self)._format_arg(opt, spec, val) - return retval - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if isdefined(self.inputs.out_label_fusion): outputs['out_label_fusion'] = os.path.abspath( self.inputs.out_label_fusion) @@ -1066,4 +1027,4 @@ def _list_outputs(self): outputs['out_atlas_voting_weight_name_format'] = os.path.abspath( self.inputs.out_atlas_voting_weight_name_format) - return outputs + diff --git a/nipype/interfaces/ants/tests/test_auto_ANTS.py b/nipype/interfaces/ants/tests/test_auto_ANTS.py index 36f153c532..ca316c4589 100644 --- a/nipype/interfaces/ants/tests/test_auto_ANTS.py +++ b/nipype/interfaces/ants/tests/test_auto_ANTS.py @@ -82,11 +82,20 @@ def test_ANTS_inputs(): def test_ANTS_outputs(): - output_map = dict(affine_transform=dict(), - inverse_warp_transform=dict(), + output_map = dict(affine_transform=dict(keep_extension=False, + name_source='output_transform_prefix', + name_template='%sAffine.txt', + ), + inverse_warp_transform=dict(keep_extension=False, + name_source='output_transform_prefix', + name_template='%sInverseWarp.nii.gz', + ), metaheader=dict(), metaheader_raw=dict(), - warp_transform=dict(), + warp_transform=dict(keep_extension=False, + name_source='output_transform_prefix', + name_template='%sWarp.nii.gz', + ), ) outputs = ANTS.output_spec() diff --git a/nipype/interfaces/ants/tests/test_auto_ANTSCommand.py b/nipype/interfaces/ants/tests/test_auto_ANTSCommand.py index 1c2a67f3bb..d930db7d72 100644 --- a/nipype/interfaces/ants/tests/test_auto_ANTSCommand.py +++ b/nipype/interfaces/ants/tests/test_auto_ANTSCommand.py @@ -24,3 +24,11 @@ def test_ANTSCommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_ANTSCommand_outputs(): + output_map = dict() + outputs = ANTSCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/ants/tests/test_auto_ApplyTransforms.py b/nipype/interfaces/ants/tests/test_auto_ApplyTransforms.py index 63d4f78e08..a483727bc5 100644 --- a/nipype/interfaces/ants/tests/test_auto_ApplyTransforms.py +++ b/nipype/interfaces/ants/tests/test_auto_ApplyTransforms.py @@ -35,10 +35,12 @@ def test_ApplyTransforms_inputs(): out_postfix=dict(usedefault=True, ), output_image=dict(argstr='--output %s', - genfile=True, hash_files=False, + keep_extension=True, + name_source='input_image', + name_template='%s_warped', ), - print_out_composite_warp_file=dict(requires=['output_image'], + print_out_composite_warp_file=dict(usedefault=True, ), reference_image=dict(argstr='--reference-image %s', mandatory=True, diff --git a/nipype/interfaces/ants/tests/test_auto_Atropos.py b/nipype/interfaces/ants/tests/test_auto_Atropos.py index e19aa5591c..ccf48272be 100644 --- a/nipype/interfaces/ants/tests/test_auto_Atropos.py +++ b/nipype/interfaces/ants/tests/test_auto_Atropos.py @@ -44,19 +44,22 @@ def test_Atropos_inputs(): ), number_of_tissue_classes=dict(mandatory=True, ), - out_classified_image_name=dict(argstr='%s', - genfile=True, + out_classified_image_name=dict(argstr='--output [%s]', hash_files=False, - ), - output_posteriors_name_template=dict(usedefault=True, + keep_extension=True, + name_source='intensity_images', + name_template='%s_labeled', ), posterior_formulation=dict(argstr='%s', ), + posteriors=dict(usedefault=True, + ), prior_probability_images=dict(), prior_probability_threshold=dict(requires=['prior_weighting'], ), prior_weighting=dict(), - save_posteriors=dict(), + save_posteriors=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), use_mixture_model_proportions=dict(requires=['posterior_formulation'], diff --git a/nipype/interfaces/ants/tests/test_auto_DenoiseImage.py b/nipype/interfaces/ants/tests/test_auto_DenoiseImage.py index 01b610ea30..ec4ea18dc4 100644 --- a/nipype/interfaces/ants/tests/test_auto_DenoiseImage.py +++ b/nipype/interfaces/ants/tests/test_auto_DenoiseImage.py @@ -18,7 +18,8 @@ def test_DenoiseImage_inputs(): input_image=dict(argstr='-i %s', mandatory=True, ), - noise_image=dict(hash_files=False, + noise_image=dict(argstr='%s', + hash_files=False, keep_extension=True, name_source=['input_image'], name_template='%s_noise', diff --git a/nipype/interfaces/ants/tests/test_auto_N4BiasFieldCorrection.py b/nipype/interfaces/ants/tests/test_auto_N4BiasFieldCorrection.py index 18921d811c..dc0e97f6b1 100644 --- a/nipype/interfaces/ants/tests/test_auto_N4BiasFieldCorrection.py +++ b/nipype/interfaces/ants/tests/test_auto_N4BiasFieldCorrection.py @@ -6,7 +6,11 @@ def test_N4BiasFieldCorrection_inputs(): input_map = dict(args=dict(argstr='%s', ), - bias_image=dict(hash_files=False, + bias_image=dict(argstr='%s', + hash_files=False, + keep_extension=True, + name_source='input_image', + name_template='%s_bias', ), bspline_fitting_distance=dict(argstr='--bspline-fitting %s', ), @@ -34,8 +38,10 @@ def test_N4BiasFieldCorrection_inputs(): usedefault=True, ), output_image=dict(argstr='--output %s', - genfile=True, hash_files=False, + keep_extension=True, + name_source='input_image', + name_template='%s_corrected', ), save_bias=dict(mandatory=True, usedefault=True, diff --git a/nipype/interfaces/ants/tests/test_auto_WarpImageMultiTransform.py b/nipype/interfaces/ants/tests/test_auto_WarpImageMultiTransform.py index 09770d9d0f..96a8f71d8f 100644 --- a/nipype/interfaces/ants/tests/test_auto_WarpImageMultiTransform.py +++ b/nipype/interfaces/ants/tests/test_auto_WarpImageMultiTransform.py @@ -24,15 +24,16 @@ def test_WarpImageMultiTransform_inputs(): num_threads=dict(nohash=True, usedefault=True, ), - out_postfix=dict(hash_files=False, + out_postfix=dict(deprecated=True, + hash_files=False, + new_name='output_image', usedefault=True, xor=['output_image'], ), output_image=dict(argstr='%s', - genfile=True, - hash_files=False, - position=3, - xor=['out_postfix'], + keep_extension=True, + name_source='input_image', + name_template='%s_wimt', ), reference_image=dict(argstr='-R %s', xor=['tightest_box'], diff --git a/nipype/interfaces/ants/tests/test_auto_WarpTimeSeriesImageMultiTransform.py b/nipype/interfaces/ants/tests/test_auto_WarpTimeSeriesImageMultiTransform.py index 0e46ce34a5..364a9e7e9f 100644 --- a/nipype/interfaces/ants/tests/test_auto_WarpTimeSeriesImageMultiTransform.py +++ b/nipype/interfaces/ants/tests/test_auto_WarpTimeSeriesImageMultiTransform.py @@ -25,7 +25,13 @@ def test_WarpTimeSeriesImageMultiTransform_inputs(): usedefault=True, ), out_postfix=dict(argstr='%s', - usedefault=True, + deprecated=True, + new_name='output_image', + ), + output_image=dict(argstr='%s', + keep_extension=True, + name_source='input_image', + name_template='%s_wtsimt', ), reference_image=dict(argstr='-R %s', xor=['tightest_box'], diff --git a/nipype/interfaces/ants/tests/test_spec_JointFusion.py b/nipype/interfaces/ants/tests/test_spec_JointFusion.py index ed6d283032..470c394613 100644 --- a/nipype/interfaces/ants/tests/test_spec_JointFusion.py +++ b/nipype/interfaces/ants/tests/test_spec_JointFusion.py @@ -47,7 +47,7 @@ def test_JointFusion_radius(): for attr in ['patch_radius', 'search_radius']: for x in range(5): set_radius(attr, x, x + 1, x**x) - yield assert_equal, at._format_arg(attr, None, getattr(at.inputs, attr))[4:], '{0}x{1}x{2}'.format(x, x + 1, x**x) + yield assert_equal, at.inputs._format_arg(attr, None, getattr(at.inputs, attr))[4:], '{0}x{1}x{2}'.format(x, x + 1, x**x) def test_JointFusion_cmd(): @@ -75,4 +75,4 @@ def test_JointFusion_cmd(): segmentation_images[1]) yield assert_equal, at.cmdline, expected_command # setting intensity or labels with unequal lengths raises error - yield assert_raises, AssertionError, at._format_arg, 'warped_intensity_images', InputMultiPath, warped_intensity_images + [example_data('im3.nii')] + yield assert_raises, AssertionError, at.inputs._format_arg, 'warped_intensity_images', InputMultiPath, warped_intensity_images + [example_data('im3.nii')] diff --git a/nipype/interfaces/ants/utils.py b/nipype/interfaces/ants/utils.py index c3253b7256..8dd99f5b96 100644 --- a/nipype/interfaces/ants/utils.py +++ b/nipype/interfaces/ants/utils.py @@ -45,12 +45,10 @@ class AverageAffineTransform(ANTSCommand): def _format_arg(self, opt, spec, val): return super(AverageAffineTransform, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['affine_transform'] = os.path.abspath( + def _post_run(self): + self.outputs.affine_transform = os.path.abspath( self.inputs.output_affine_transform) - return outputs - + class AverageImagesInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(3, 2, argstr='%d', mandatory=True, @@ -87,12 +85,10 @@ class AverageImages(ANTSCommand): def _format_arg(self, opt, spec, val): return super(AverageImages, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_average_image'] = os.path.realpath( + def _post_run(self): + self.outputs.output_average_image = os.path.realpath( self.inputs.output_average_image) - return outputs - + class MultiplyImagesInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(3, 2, argstr='%d', usedefault=False, mandatory=True, position=0, @@ -128,12 +124,10 @@ class MultiplyImages(ANTSCommand): def _format_arg(self, opt, spec, val): return super(MultiplyImages, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_product_image'] = os.path.abspath( + def _post_run(self): + self.outputs.output_product_image = os.path.abspath( self.inputs.output_product_image) - return outputs - + class JacobianDeterminantInputSpec(ANTSCommandInputSpec): dimension = traits.Enum(3, 2, argstr='%d', usedefault=False, mandatory=True, @@ -187,12 +181,11 @@ def _gen_filename(self, name): return output return None - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if self.inputs.use_log == 1: - outputs['jacobian_image'] = os.path.abspath( + self.outputs.jacobian_image = os.path.abspath( self._gen_filename('output_prefix') + 'logjacobian.nii.gz') else: - outputs['jacobian_image'] = os.path.abspath( + self.outputs.jacobian_image = os.path.abspath( self._gen_filename('output_prefix') + 'jacobian.nii.gz') - return outputs + \ No newline at end of file diff --git a/nipype/interfaces/ants/visualization.py b/nipype/interfaces/ants/visualization.py index 624f8e10b1..98efd559e5 100644 --- a/nipype/interfaces/ants/visualization.py +++ b/nipype/interfaces/ants/visualization.py @@ -64,12 +64,10 @@ class ConvertScalarImageToRGB(ANTSCommand): def _format_arg(self, opt, spec, val): return super(ConvertScalarImageToRGB, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_image'] = os.path.join(os.getcwd(), + def _post_run(self): + self.outputs.output_image = os.path.join(os.getcwd(), self.inputs.output_image) - return outputs - + class CreateTiledMosaicInputSpec(ANTSCommandInputSpec): input_image = File(argstr='-i %s', exists=True, @@ -149,8 +147,7 @@ class CreateTiledMosaic(ANTSCommand): input_spec = CreateTiledMosaicInputSpec output_spec = CreateTiledMosaicOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output_image'] = os.path.join(os.getcwd(), + def _post_run(self): + self.outputs.output_image = os.path.join(os.getcwd(), self.inputs.output_image) - return outputs + \ No newline at end of file diff --git a/nipype/interfaces/base.py b/nipype/interfaces/base.py index 098e6223bd..1c9f3e3f74 100644 --- a/nipype/interfaces/base.py +++ b/nipype/interfaces/base.py @@ -10,63 +10,50 @@ from __future__ import print_function from __future__ import division -from future import standard_library -standard_library.install_aliases() -from builtins import range -from builtins import object -from configparser import NoOptionError from copy import deepcopy -import datetime import errno import os -import re +import os.path as op import platform from socket import getfqdn from string import Template import select import subprocess import sys -import random -import time -import fnmatch -from textwrap import wrap from datetime import datetime as dt from dateutil.parser import parse as parseutc -from warnings import warn +from future import standard_library +standard_library.install_aliases() +from builtins import range +from builtins import object -from .traits_extension import (traits, Undefined, TraitDictObject, - TraitListObject, TraitError, - isdefined, File, Directory, - has_metadata) -from ..utils.filemanip import (md5, hash_infile, FileNotFoundError, - hash_timestamp, save_json, - split_filename) -from ..utils.misc import is_container, trim, str2bool +from configparser import NoOptionError +from ..utils.filemanip import md5, FileNotFoundError +from ..utils.misc import trim, str2bool, is_container + +# Make all the traits and spec interfaces available through base +# for backwards compatibility, even though import * is discouraged +# in production environments. +from .traits_extension import * # pylint: disable=W0611 +from .specs import * # pylint: disable=W0611 +#from .traits_extension import isdefined, Undefined +# from .specs import (BaseInterfaceInputSpec, CommandLineInputSpec, +# StdOutCommandLineInputSpec, StdOutCommandLineOutputSpec, +# MpiCommandLineInputSpec, +# SEMLikeCommandLineInputSpec, TraitedSpec) from ..utils.provenance import write_provenance from .. import config, logging, LooseVersion from .. import __version__ from ..external.six import string_types -nipype_version = LooseVersion(__version__) - -iflogger = logging.getLogger('interface') - - +IFLOGGER = logging.getLogger('interface') __docformat__ = 'restructuredtext' -class NipypeInterfaceError(Exception): - def __init__(self, value): - self.value = value - - def __str__(self): - return repr(self.value) - - def _unlock_display(ndisplay): - lockf = os.path.join('/tmp', '.X%d-lock' % ndisplay) + lockf = op.join('/tmp', '.X%d-lock' % ndisplay) try: os.remove(lockf) except: @@ -76,21 +63,18 @@ def _unlock_display(ndisplay): def _exists_in_path(cmd, environ): - ''' + """ Based on a code snippet from http://orip.org/2009/08/python-checking-if-executable-exists-in.html - ''' - - if 'PATH' in environ: - input_environ = environ.get("PATH") - else: - input_environ = os.environ.get("PATH", "") + """ + # Read environ fron variable, use system's environ as failback + input_environ = environ.get("PATH", os.environ.get("PATH", "")) extensions = os.environ.get("PATHEXT", "").split(os.pathsep) for directory in input_environ.split(os.pathsep): - base = os.path.join(directory, cmd) + base = op.join(directory, cmd) options = [base] + [(base + ext) for ext in extensions] for filename in options: - if os.path.exists(filename): + if op.exists(filename): return True, filename return False, None @@ -109,7 +93,7 @@ def load_template(name): """ - full_fname = os.path.join(os.path.dirname(__file__), + full_fname = op.join(op.dirname(__file__), 'script_templates', name) template_file = open(full_fname) template = Template(template_file.read()) @@ -126,7 +110,7 @@ class Bunch(object): Examples -------- - >>> from nipype.interfaces.base import Bunch + >>> from nipype.interfaces.specs import Bunch >>> inputs = Bunch(infile='subj.nii', fwhm=6.0, register_to_mean=True) >>> inputs Bunch(fwhm=6.0, infile='subj.nii', register_to_mean=True) @@ -159,17 +143,17 @@ def items(self): def iteritems(self): """iterates over bunch attributes as key, value pairs""" - warn('iteritems is deprecated, use items instead') + IFLOGGER.warn('iteritems is deprecated, use items instead') return list(self.items()) def get(self, *args): - '''Support dictionary get() functionality - ''' + """Support dictionary get() functionality + """ return self.__dict__.get(*args) def set(self, **kwargs): - '''Support dictionary get() functionality - ''' + """Support dictionary get() functionality + """ return self.__dict__.update(**kwargs) def dictcopy(self): @@ -186,42 +170,21 @@ def __repr__(self): """ outstr = ['Bunch('] first = True - for k, v in sorted(self.items()): + for k, input_value in sorted(self.items()): if not first: outstr.append(', ') - if isinstance(v, dict): + if isinstance(input_value, dict): pairs = [] - for key, value in sorted(v.items()): + for key, value in sorted(input_value.items()): pairs.append("'%s': %s" % (key, value)) - v = '{' + ', '.join(pairs) + '}' - outstr.append('%s=%s' % (k, v)) + input_value = '{' + ', '.join(pairs) + '}' + outstr.append('%s=%s' % (k, input_value)) else: - outstr.append('%s=%r' % (k, v)) + outstr.append('%s=%r' % (k, input_value)) first = False outstr.append(')') return ''.join(outstr) - def _hash_infile(self, adict, key): - # Inject file hashes into adict[key] - stuff = adict[key] - if not is_container(stuff): - stuff = [stuff] - file_list = [] - for afile in stuff: - if os.path.isfile(afile): - md5obj = md5() - with open(afile, 'rb') as fp: - while True: - data = fp.read(8192) - if not data: - break - md5obj.update(data) - md5hex = md5obj.hexdigest() - else: - md5hex = None - file_list.append((afile, md5hex)) - return file_list - def _get_bunch_hash(self): """Return a dictionary of our items with hashes for each file. @@ -258,7 +221,7 @@ def _get_bunch_hash(self): else: item = val try: - if os.path.isfile(item): + if op.isfile(item): infile_list.append(key) except TypeError: # `item` is not a file or string. @@ -274,21 +237,43 @@ def _get_bunch_hash(self): sorted_dict = str(sorted(dict_nofilename.items())) return dict_withhash, md5(sorted_dict.encode()).hexdigest() + def _hash_infile(self, adict, key): + """Compute hashes of files""" + # Inject file hashes into adict[key] + stuff = adict[key] + if not is_container(stuff): + stuff = [stuff] + file_list = [] + for fname in stuff: + if op.isfile(fname): + md5obj = md5() + with open(fname, 'rb') as filep: + while True: + data = filep.read(8192) + if not data: + break + md5obj.update(data) + md5hex = md5obj.hexdigest() + else: + md5hex = None + file_list.append((fname, md5hex)) + return file_list + def __pretty__(self, p, cycle): - '''Support for the pretty module + """Support for the pretty module - pretty is included in ipython.externals for ipython > 0.10''' + pretty is included in ipython.externals for ipython > 0.10""" if cycle: p.text('Bunch(...)') else: p.begin_group(6, 'Bunch(') first = True - for k, v in sorted(self.items()): + for k, input_value in sorted(self.items()): if not first: p.text(',') p.breakable() p.text(k + '=') - p.pretty(v) + p.pretty(input_value) first = False p.end_group(6, ')') @@ -334,330 +319,6 @@ def version(self): return self._version -class BaseTraitedSpec(traits.HasTraits): - """Provide a few methods necessary to support nipype interface api - - The inputs attribute of interfaces call certain methods that are not - available in traits.HasTraits. These are provided here. - - new metadata: - - * usedefault : set this to True if the default value of the trait should be - used. Unless this is set, the attributes are set to traits.Undefined - - new attribute: - - * get_hashval : returns a tuple containing the state of the trait as a dict - and hashvalue corresponding to dict. - - XXX Reconsider this in the long run, but it seems like the best - solution to move forward on the refactoring. - """ - - def __init__(self, **kwargs): - """ Initialize handlers and inputs""" - # NOTE: In python 2.6, object.__init__ no longer accepts input - # arguments. HasTraits does not define an __init__ and - # therefore these args were being ignored. - # super(TraitedSpec, self).__init__(*args, **kwargs) - super(BaseTraitedSpec, self).__init__(**kwargs) - traits.push_exception_handler(reraise_exceptions=True) - undefined_traits = {} - for trait in self.copyable_trait_names(): - if not self.traits()[trait].usedefault: - undefined_traits[trait] = Undefined - self.trait_set(trait_change_notify=False, **undefined_traits) - self._generate_handlers() - self.set(**kwargs) - - def items(self): - """ Name, trait generator for user modifiable traits - """ - for name in sorted(self.copyable_trait_names()): - yield name, self.traits()[name] - - def __repr__(self): - """ Return a well-formatted representation of the traits """ - outstr = [] - for name, value in sorted(self.trait_get().items()): - outstr.append('%s = %s' % (name, value)) - return '\n' + '\n'.join(outstr) + '\n' - - def _generate_handlers(self): - """Find all traits with the 'xor' metadata and attach an event - handler to them. - """ - has_xor = dict(xor=lambda t: t is not None) - xors = self.trait_names(**has_xor) - for elem in xors: - self.on_trait_change(self._xor_warn, elem) - has_requires = dict(requires=lambda t: t is not None) - requires = self.trait_names(**has_requires) - for elem in requires: - self.on_trait_change(self._requires_warn, elem) - has_deprecation = dict(deprecated=lambda t: t is not None) - deprecated = self.trait_names(**has_deprecation) - for elem in deprecated: - self.on_trait_change(self._deprecated_warn, elem) - - def _xor_warn(self, obj, name, old, new): - """ Generates warnings for xor traits - """ - if isdefined(new): - trait_spec = self.traits()[name] - # for each xor, set to default_value - for trait_name in trait_spec.xor: - if trait_name == name: - # skip ourself - continue - if isdefined(getattr(self, trait_name)): - self.trait_set(trait_change_notify=False, - **{'%s' % name: Undefined}) - msg = ('Input "%s" is mutually exclusive with input "%s", ' - 'which is already set') % (name, trait_name) - raise IOError(msg) - - def _requires_warn(self, obj, name, old, new): - """Part of the xor behavior - """ - if isdefined(new): - trait_spec = self.traits()[name] - msg = None - for trait_name in trait_spec.requires: - if not isdefined(getattr(self, trait_name)): - if not msg: - msg = 'Input %s requires inputs: %s' \ - % (name, ', '.join(trait_spec.requires)) - if msg: # only one requires warning at a time. - warn(msg) - - def _deprecated_warn(self, obj, name, old, new): - """Checks if a user assigns a value to a deprecated trait - """ - if isdefined(new): - trait_spec = self.traits()[name] - msg1 = ('Input %s in interface %s is deprecated.' % - (name, - self.__class__.__name__.split('InputSpec')[0])) - msg2 = ('Will be removed or raise an error as of release %s' - % trait_spec.deprecated) - if trait_spec.new_name: - if trait_spec.new_name not in self.copyable_trait_names(): - raise TraitError(msg1 + ' Replacement trait %s not found' % - trait_spec.new_name) - msg3 = 'It has been replaced by %s.' % trait_spec.new_name - else: - msg3 = '' - msg = ' '.join((msg1, msg2, msg3)) - if LooseVersion(str(trait_spec.deprecated)) < nipype_version: - raise TraitError(msg) - else: - if trait_spec.new_name: - msg += 'Unsetting old value %s; setting new value %s.' % ( - name, trait_spec.new_name) - warn(msg) - if trait_spec.new_name: - self.trait_set(trait_change_notify=False, - **{'%s' % name: Undefined, - '%s' % trait_spec.new_name: new}) - - def _hash_infile(self, adict, key): - """ Inject file hashes into adict[key]""" - stuff = adict[key] - if not is_container(stuff): - stuff = [stuff] - file_list = [] - for afile in stuff: - if is_container(afile): - hashlist = self._hash_infile({'infiles': afile}, 'infiles') - hash = [val[1] for val in hashlist] - else: - if config.get('execution', - 'hash_method').lower() == 'timestamp': - hash = hash_timestamp(afile) - elif config.get('execution', - 'hash_method').lower() == 'content': - hash = hash_infile(afile) - else: - raise Exception("Unknown hash method: %s" % - config.get('execution', 'hash_method')) - file_list.append((afile, hash)) - return file_list - - def get(self, **kwargs): - """ Returns traited class as a dict - - Augments the trait get function to return a dictionary without - notification handles - """ - out = super(BaseTraitedSpec, self).get(**kwargs) - out = self._clean_container(out, Undefined) - return out - - def get_traitsfree(self, **kwargs): - """ Returns traited class as a dict - - Augments the trait get function to return a dictionary without - any traits. The dictionary does not contain any attributes that - were Undefined - """ - out = super(BaseTraitedSpec, self).get(**kwargs) - out = self._clean_container(out, skipundefined=True) - return out - - def _clean_container(self, object, undefinedval=None, skipundefined=False): - """Convert a traited obejct into a pure python representation. - """ - if isinstance(object, TraitDictObject) or isinstance(object, dict): - out = {} - for key, val in list(object.items()): - if isdefined(val): - out[key] = self._clean_container(val, undefinedval) - else: - if not skipundefined: - out[key] = undefinedval - elif (isinstance(object, TraitListObject) or - isinstance(object, list) or isinstance(object, tuple)): - out = [] - for val in object: - if isdefined(val): - out.append(self._clean_container(val, undefinedval)) - else: - if not skipundefined: - out.append(undefinedval) - else: - out.append(None) - if isinstance(object, tuple): - out = tuple(out) - else: - if isdefined(object): - out = object - else: - if not skipundefined: - out = undefinedval - return out - - def get_hashval(self, hash_method=None): - """Return a dictionary of our items with hashes for each file. - - Searches through dictionary items and if an item is a file, it - calculates the md5 hash of the file contents and stores the - file name and hash value as the new key value. - - However, the overall bunch hash is calculated only on the hash - value of a file. The path and name of the file are not used in - the overall hash calculation. - - Returns - ------- - dict_withhash : dict - Copy of our dictionary with the new file hashes included - with each file. - hashvalue : str - The md5 hash value of the traited spec - - """ - - dict_withhash = [] - dict_nofilename = [] - for name, val in sorted(self.get().items()): - if isdefined(val): - trait = self.trait(name) - if has_metadata(trait.trait_type, "nohash", True): - continue - hash_files = (not has_metadata(trait.trait_type, "hash_files", - False) and not - has_metadata(trait.trait_type, "name_source")) - dict_nofilename.append((name, - self._get_sorteddict(val, hash_method=hash_method, - hash_files=hash_files))) - dict_withhash.append((name, - self._get_sorteddict(val, True, hash_method=hash_method, - hash_files=hash_files))) - return dict_withhash, md5(str(dict_nofilename).encode()).hexdigest() - - def _get_sorteddict(self, object, dictwithhash=False, hash_method=None, - hash_files=True): - if isinstance(object, dict): - out = [] - for key, val in sorted(object.items()): - if isdefined(val): - out.append((key, - self._get_sorteddict(val, dictwithhash, - hash_method=hash_method, - hash_files=hash_files))) - elif isinstance(object, (list, tuple)): - out = [] - for val in object: - if isdefined(val): - out.append(self._get_sorteddict(val, dictwithhash, - hash_method=hash_method, - hash_files=hash_files)) - if isinstance(object, tuple): - out = tuple(out) - else: - if isdefined(object): - if (hash_files and isinstance(object, string_types) and - os.path.isfile(object)): - if hash_method is None: - hash_method = config.get('execution', 'hash_method') - - if hash_method.lower() == 'timestamp': - hash = hash_timestamp(object) - elif hash_method.lower() == 'content': - hash = hash_infile(object) - else: - raise Exception("Unknown hash method: %s" % hash_method) - if dictwithhash: - out = (object, hash) - else: - out = hash - elif isinstance(object, float): - out = '%.10f' % object - else: - out = object - return out - - -class DynamicTraitedSpec(BaseTraitedSpec): - """ A subclass to handle dynamic traits - - This class is a workaround for add_traits and clone_traits not - functioning well together. - """ - - def __deepcopy__(self, memo): - """ bug in deepcopy for HasTraits results in weird cloning behavior for - added traits - """ - id_self = id(self) - if id_self in memo: - return memo[id_self] - dup_dict = deepcopy(self.get(), memo) - # access all keys - for key in self.copyable_trait_names(): - _ = getattr(self, key) - # clone once - dup = self.clone_traits(memo=memo) - for key in self.copyable_trait_names(): - try: - _ = getattr(dup, key) - except: - pass - # clone twice - dup = self.clone_traits(memo=memo) - dup.set(**dup_dict) - return dup - - -class TraitedSpec(BaseTraitedSpec): - """ Create a subclass with strict traits. - - This is used in 90% of the cases. - """ - _ = traits.Disallow - - class Interface(object): """This is an abstract definition for Interface objects. @@ -688,7 +349,7 @@ def __init__(self, **inputs): raise NotImplementedError @classmethod - def help(cls): + def help(cls, returnhelp=False): """ Prints class help""" raise NotImplementedError @@ -702,40 +363,22 @@ def _outputs_help(cls): """ Prints outputs help""" raise NotImplementedError - @classmethod - def _outputs(cls): - """ Initializes outputs""" - raise NotImplementedError - @property def version(self): raise NotImplementedError - def run(self): - """Execute the command.""" + def _pre_run(self, **inputs): raise NotImplementedError - def aggregate_outputs(self, runtime=None, needed_outputs=None): - """Called to populate outputs""" + def _post_run(self, **inputs): raise NotImplementedError - def _list_outputs(self): - """ List expected outputs""" - raise NotImplementedError - def _get_filecopy_info(self): - """ Provides information about file inputs to copy or link to cwd. - Necessary for pipeline operation - """ + def run(self): + """Execute the command.""" raise NotImplementedError -class BaseInterfaceInputSpec(TraitedSpec): - ignore_exception = traits.Bool(False, desc="Print an error message instead \ -of throwing an exception in case the interface fails to run", usedefault=True, - nohash=True) - - class BaseInterface(Interface): """Implements common interface functionality. @@ -755,6 +398,7 @@ class BaseInterface(Interface): """ input_spec = BaseInterfaceInputSpec + output_spec = TraitedSpec _version = None _additional_metadata = [] _redirect_x = False @@ -764,11 +408,11 @@ def __init__(self, **inputs): raise Exception('No input_spec in class: %s' % self.__class__.__name__) self.inputs = self.input_spec(**inputs) + self.outputs = self.output_spec() @classmethod def help(cls, returnhelp=False): - """ Prints class help - """ + """ Prints class help """ if cls.__doc__: # docstring = cls.__doc__.split('\n') @@ -777,219 +421,27 @@ def help(cls, returnhelp=False): else: docstring = [''] - allhelp = '\n'.join(docstring + cls._inputs_help() + [''] + - cls._outputs_help() + ['']) + allhelp = '\n'.join(docstring + ['Inputs::'] + cls.input_spec().help() + [''] + + ['Outputs::', ''] + cls.output_spec().help() + ['']) if returnhelp: return allhelp else: print(allhelp) - @classmethod - def _get_trait_desc(self, inputs, name, spec): - desc = spec.desc - xor = spec.xor - requires = spec.requires - argstr = spec.argstr - - manhelpstr = ['\t%s' % name] - - type_info = spec.full_info(inputs, name, None) - - default = '' - if spec.usedefault: - default = ', nipype default value: %s' % str(spec.default_value()[1]) - line = "(%s%s)" % (type_info, default) - - manhelpstr = wrap(line, 70, - initial_indent=manhelpstr[0] + ': ', - subsequent_indent='\t\t ') - - if desc: - for line in desc.split('\n'): - line = re.sub("\s+", " ", line) - manhelpstr += wrap(line, 70, - initial_indent='\t\t', - subsequent_indent='\t\t') - - if argstr: - pos = spec.position - if pos is not None: - manhelpstr += wrap('flag: %s, position: %s' % (argstr, pos), 70, - initial_indent='\t\t', - subsequent_indent='\t\t') - else: - manhelpstr += wrap('flag: %s' % argstr, 70, - initial_indent='\t\t', - subsequent_indent='\t\t') - - if xor: - line = '%s' % ', '.join(xor) - manhelpstr += wrap(line, 70, - initial_indent='\t\tmutually_exclusive: ', - subsequent_indent='\t\t ') - - if requires: - others = [field for field in requires if field != name] - line = '%s' % ', '.join(others) - manhelpstr += wrap(line, 70, - initial_indent='\t\trequires: ', - subsequent_indent='\t\t ') - return manhelpstr - - @classmethod - def _inputs_help(cls): - """ Prints description for input parameters - """ - helpstr = ['Inputs::'] - - inputs = cls.input_spec() - if len(list(inputs.traits(transient=None).items())) == 0: - helpstr += ['', '\tNone'] - return helpstr - - manhelpstr = ['', '\t[Mandatory]'] - mandatory_items = inputs.traits(mandatory=True) - for name, spec in sorted(mandatory_items.items()): - manhelpstr += cls._get_trait_desc(inputs, name, spec) - - opthelpstr = ['', '\t[Optional]'] - for name, spec in sorted(inputs.traits(transient=None).items()): - if name in mandatory_items: - continue - opthelpstr += cls._get_trait_desc(inputs, name, spec) - - if manhelpstr: - helpstr += manhelpstr - if opthelpstr: - helpstr += opthelpstr - return helpstr - - @classmethod - def _outputs_help(cls): - """ Prints description for output parameters - """ - helpstr = ['Outputs::', ''] - if cls.output_spec: - outputs = cls.output_spec() - for name, spec in sorted(outputs.traits(transient=None).items()): - helpstr += cls._get_trait_desc(outputs, name, spec) - if len(helpstr) == 2: - helpstr += ['\tNone'] - return helpstr - - def _outputs(self): - """ Returns a bunch containing output fields for the class - """ - outputs = None - if self.output_spec: - outputs = self.output_spec() - return outputs - - @classmethod - def _get_filecopy_info(cls): - """ Provides information about file inputs to copy or link to cwd. - Necessary for pipeline operation - """ - info = [] - if cls.input_spec is None: - return info - metadata = dict(copyfile=lambda t: t is not None) - for name, spec in sorted(cls.input_spec().traits(**metadata).items()): - info.append(dict(key=name, - copy=spec.copyfile)) - return info - - def _check_requires(self, spec, name, value): - """ check if required inputs are satisfied - """ - if spec.requires: - values = [not isdefined(getattr(self.inputs, field)) - for field in spec.requires] - if any(values) and isdefined(value): - msg = ("%s requires a value for input '%s' because one of %s " - "is set. For a list of required inputs, see %s.help()" % - (self.__class__.__name__, name, - ', '.join(spec.requires), self.__class__.__name__)) - raise ValueError(msg) - - def _check_xor(self, spec, name, value): - """ check if mutually exclusive inputs are satisfied - """ - if spec.xor: - values = [isdefined(getattr(self.inputs, field)) - for field in spec.xor] - if not any(values) and not isdefined(value): - msg = ("%s requires a value for one of the inputs '%s'. " - "For a list of required inputs, see %s.help()" % - (self.__class__.__name__, ', '.join(spec.xor), - self.__class__.__name__)) - raise ValueError(msg) - - def _check_mandatory_inputs(self): - """ Raises an exception if a mandatory input is Undefined - """ - for name, spec in list(self.inputs.traits(mandatory=True).items()): - value = getattr(self.inputs, name) - self._check_xor(spec, name, value) - if not isdefined(value) and spec.xor is None: - msg = ("%s requires a value for input '%s'. " - "For a list of required inputs, see %s.help()" % - (self.__class__.__name__, name, self.__class__.__name__)) - raise ValueError(msg) - if isdefined(value): - self._check_requires(spec, name, value) - for name, spec in list(self.inputs.traits(mandatory=None, - transient=None).items()): - self._check_requires(spec, name, getattr(self.inputs, name)) - - def _check_version_requirements(self, trait_object, raise_exception=True): - """ Raises an exception on version mismatch - """ - unavailable_traits = [] - # check minimum version - check = dict(min_ver=lambda t: t is not None) - names = trait_object.trait_names(**check) - - if names and self.version: - version = LooseVersion(str(self.version)) - for name in names: - min_ver = LooseVersion(str(trait_object.traits()[name].min_ver)) - if min_ver > version: - unavailable_traits.append(name) - if not isdefined(getattr(trait_object, name)): - continue - if raise_exception: - raise Exception('Trait %s (%s) (version %s < required %s)' % - (name, self.__class__.__name__, - version, min_ver)) - check = dict(max_ver=lambda t: t is not None) - names = trait_object.trait_names(**check) - for name in names: - max_ver = LooseVersion(str(trait_object.traits()[name].max_ver)) - if max_ver < version: - unavailable_traits.append(name) - if not isdefined(getattr(trait_object, name)): - continue - if raise_exception: - raise Exception('Trait %s (%s) (version %s > required %s)' % - (name, self.__class__.__name__, - version, max_ver)) - return unavailable_traits - def _run_wrapper(self, runtime): sysdisplay = os.getenv('DISPLAY') if self._redirect_x: try: from xvfbwrapper import Xvfb except ImportError: - iflogger.error('Xvfb wrapper could not be imported') + IFLOGGER.error('Xvfb wrapper could not be imported') raise vdisp = Xvfb(nolisten='tcp') vdisp.start() vdisp_num = vdisp.vdisplay_num - iflogger.info('Redirecting X to :%d' % vdisp_num) + IFLOGGER.info('Redirecting X to :%d', vdisp_num) runtime.environ['DISPLAY'] = ':%d' % vdisp_num runtime = self._run_interface(runtime) @@ -1000,17 +452,53 @@ def _run_wrapper(self, runtime): else: os.environ['DISPLAY'] = sysdisplay - iflogger.info('Freeing X :%d' % vdisp_num) + IFLOGGER.info('Freeing X :%d', vdisp_num) vdisp.stop() _unlock_display(vdisp_num) return runtime - def _run_interface(self, runtime): + def _run_interface(self, runtime, *kwargs): """ Core function that executes interface """ raise NotImplementedError + def _pre_run(self, **inputs): + self.outputs = self.output_spec() + self.inputs.set(**inputs) + self.inputs.check_inputs() + if self.version: + self.inputs.check_version(LooseVersion(str(self.version))) + + def _post_run(self): + if self.output_spec is None: + IFLOGGER.warn('Interface does not have an output specification') + return None + + ns_outputs = {} + for ns_input, ns_spec in list(self.inputs.namesource_items()): + ns_pointer = getattr(ns_spec, 'out_name', None) + if ns_pointer is not None: + ns_outputs[ns_pointer] = ns_input + + # Search for inputs with the same name + for out_name, spec in list(self.outputs.items()): + if out_name in ns_outputs.keys(): + value = getattr(self.inputs, ns_outputs[out_name], Undefined) + else: + value = getattr(self.inputs, out_name, Undefined) + + if isdefined(value): + setattr(self.outputs, out_name, op.abspath(value)) + + # Search for outputs with name source + for out_name, spec in self.outputs.namesource_items(): + if isdefined(getattr(self.outputs, out_name)): + continue + value = self.outputs.format_ns(spec.name_source, out_name, self.inputs) + setattr(self.outputs, out_name, value) + + def run(self, **inputs): """Execute this interface. @@ -1026,40 +514,18 @@ def run(self, **inputs): results : an InterfaceResult object containing a copy of the instance that was executed, provenance information and, if successful, results """ - self.inputs.set(**inputs) - self._check_mandatory_inputs() - self._check_version_requirements(self.inputs) interface = self.__class__ + self._pre_run(**inputs) # initialize provenance tracking env = deepcopy(dict(os.environ)) - runtime = Bunch(cwd=os.getcwd(), - returncode=None, - duration=None, - environ=env, - startTime=dt.isoformat(dt.utcnow()), - endTime=None, - platform=platform.platform(), - hostname=getfqdn(), - version=self.version) + runtime = Bunch( + cwd=os.getcwd(), returncode=None, duration=None, environ=env, + startTime=dt.isoformat(dt.utcnow()), endTime=None, traceback=None, + platform=platform.platform(), hostname=getfqdn(), version=self.version) + try: runtime = self._run_wrapper(runtime) - outputs = self.aggregate_outputs(runtime) - runtime.endTime = dt.isoformat(dt.utcnow()) - timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime) - runtime.duration = (timediff.days * 86400 + timediff.seconds + - timediff.microseconds / 100000.) - results = InterfaceResult(interface, runtime, - inputs=self.inputs.get_traitsfree(), - outputs=outputs) - prov_record = None - if str2bool(config.get('execution', 'write_provenance')): - prov_record = write_provenance(results) - results.provenance = prov_record - except Exception as e: - runtime.endTime = dt.isoformat(dt.utcnow()) - timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime) - runtime.duration = (timediff.days * 86400 + timediff.seconds + - timediff.microseconds / 100000.) + except Exception as e: # pylint: disable=W0703 if len(e.args) == 0: e.args = ("") @@ -1081,67 +547,29 @@ def run(self, **inputs): # exception raising inhibition for special cases import traceback runtime.traceback = traceback.format_exc() - runtime.traceback_args = e.args - inputs = None - try: - inputs = self.inputs.get_traitsfree() - except Exception as e: - pass - results = InterfaceResult(interface, runtime, inputs=inputs) - prov_record = None - if str2bool(config.get('execution', 'write_provenance')): - try: - prov_record = write_provenance(results) - except Exception: - prov_record = None - results.provenance = prov_record - if hasattr(self.inputs, 'ignore_exception') and \ - isdefined(self.inputs.ignore_exception) and \ - self.inputs.ignore_exception: - pass - else: - raise + runtime.traceback_args = e.args # pylint: disable=W0201 + + runtime.endTime = dt.isoformat(dt.utcnow()) + timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime) + runtime.duration = (timediff.days * 86400 + timediff.seconds + + timediff.microseconds / 1e5) + results = InterfaceResult(interface, runtime, + inputs=self.inputs.get_traitsfree()) + + if runtime.traceback is None: + self._post_run() + results.outputs = self.outputs + + prov_record = None + if str2bool(config.get('execution', 'write_provenance')): + prov_record = write_provenance(results) + results.provenance = prov_record + + if (runtime.traceback is not None and + not getattr(self.inputs, 'ignore_exception', False)): + raise return results - def _list_outputs(self): - """ List the expected outputs - """ - if self.output_spec: - raise NotImplementedError - else: - return None - - def aggregate_outputs(self, runtime=None, needed_outputs=None): - """ Collate expected outputs and check for existence - """ - predicted_outputs = self._list_outputs() - outputs = self._outputs() - if predicted_outputs: - _unavailable_outputs = [] - if outputs: - _unavailable_outputs = \ - self._check_version_requirements(self._outputs()) - for key, val in list(predicted_outputs.items()): - if needed_outputs and key not in needed_outputs: - continue - if key in _unavailable_outputs: - raise KeyError(('Output trait %s not available in version ' - '%s of interface %s. Please inform ' - 'developers.') % (key, self.version, - self.__class__.__name__)) - try: - setattr(outputs, key, val) - _ = getattr(outputs, key) - except TraitError as error: - if hasattr(error, 'info') and \ - error.info.startswith("an existing"): - msg = ("File/Directory '%s' not found for %s output " - "'%s'." % (val, self.__class__.__name__, key)) - raise FileNotFoundError(msg) - else: - raise error - return outputs - @property def version(self): if self._version is None: @@ -1193,12 +621,12 @@ def _read(self, drain): tmp = buf rest = None self._buf = rest - now = datetime.datetime.now().isoformat() + now = dt.now().isoformat() rows = tmp.split('\n') self._rows += [(now, '%s %s:%s' % (self._name, now, r), r) for r in rows] for idx in range(self._lastidx, len(self._rows)): - iflogger.info(self._rows[idx][1]) + IFLOGGER.info(self._rows[idx][1]) self._lastidx = len(self._rows) @@ -1217,8 +645,8 @@ def run_command(runtime, output=None, timeout=0.01, redirect_x=False): cmdline = 'xvfb-run -a ' + cmdline if output == 'file': - errfile = os.path.join(runtime.cwd, 'stderr.nipype') - outfile = os.path.join(runtime.cwd, 'stdout.nipype') + errfile = op.join(runtime.cwd, 'stderr.nipype') + outfile = op.join(runtime.cwd, 'stdout.nipype') stderr = open(errfile, 'wt') # t=='text'===default stdout = open(outfile, 'wt') @@ -1236,8 +664,8 @@ def run_command(runtime, output=None, timeout=0.01, redirect_x=False): cwd=runtime.cwd, env=runtime.environ) result = {} - errfile = os.path.join(runtime.cwd, 'stderr.nipype') - outfile = os.path.join(runtime.cwd, 'stdout.nipype') + errfile = op.join(runtime.cwd, 'stderr.nipype') + outfile = op.join(runtime.cwd, 'stdout.nipype') if output == 'stream': streams = [Stream('stdout', proc.stdout), Stream('stderr', proc.stderr)] @@ -1245,7 +673,7 @@ def _process(drain=0): try: res = select.select(streams, [], [], timeout) except select.error as e: - iflogger.info(str(e)) + IFLOGGER.info(str(e)) if e[0] == errno.EINTR: return else: @@ -1311,39 +739,17 @@ def get_dependencies(name, environ): """ PIPE = subprocess.PIPE if sys.platform == 'darwin': - proc = subprocess.Popen('otool -L `which %s`' % name, - stdout=PIPE, - stderr=PIPE, - shell=True, - env=environ) + proc = subprocess.Popen( + 'otool -L `which %s`' % name, stdout=PIPE, stderr=PIPE, shell=True, env=environ) elif 'linux' in sys.platform: - proc = subprocess.Popen('ldd `which %s`' % name, - stdout=PIPE, - stderr=PIPE, - shell=True, - env=environ) + proc = subprocess.Popen( + 'ldd `which %s`' % name, stdout=PIPE, stderr=PIPE, shell=True, env=environ) else: return 'Platform %s not supported' % sys.platform - o, e = proc.communicate() + o, _ = proc.communicate() return o.rstrip() -class CommandLineInputSpec(BaseInterfaceInputSpec): - args = traits.Str(argstr='%s', desc='Additional parameters to the command') - environ = traits.DictStrStr(desc='Environment variables', usedefault=True, - nohash=True) - # This input does not have a "usedefault=True" so the set_default_terminal_output() - # method would work - terminal_output = traits.Enum('stream', 'allatonce', 'file', 'none', - desc=('Control terminal output: `stream` - ' - 'displays to terminal immediately (default), ' - '`allatonce` - waits till command is ' - 'finished to display output, `file` - ' - 'writes output to file, `none` - output' - ' is ignored'), - nohash=True) - - class CommandLine(BaseInterface): """Implements functionality to interact with command line programs class must be instantiated with a command argument @@ -1427,8 +833,9 @@ def cmd(self): def cmdline(self): """ `command` plus any arguments (args) validates arguments and generates command line""" - self._check_mandatory_inputs() - allargs = self._parse_inputs() + self.outputs = self.output_spec() + self.inputs.check_inputs() + allargs = self.inputs.parse_args() allargs.insert(0, self.cmd) return ' '.join(allargs) @@ -1442,13 +849,11 @@ def raise_exception(self, runtime): @classmethod def help(cls, returnhelp=False): allhelp = super(CommandLine, cls).help(returnhelp=True) - - allhelp = "Wraps command **%s**\n\n" % cls._cmd + allhelp + allhelp = "Wraps command ``%s``\n\n" % cls._cmd + allhelp if returnhelp: return allhelp - else: - print(allhelp) + print(allhelp) def _get_environ(self): out_environ = {} @@ -1458,31 +863,27 @@ def _get_environ(self): out_environ = {'DISPLAY': display_var} except NoOptionError: pass - iflogger.debug(out_environ) + IFLOGGER.debug(out_environ) if isdefined(self.inputs.environ): out_environ.update(self.inputs.environ) return out_environ def version_from_command(self, flag='-v'): cmdname = self.cmd.split()[0] - if _exists_in_path(cmdname): - env = dict(os.environ) + env = dict(os.environ) + if _exists_in_path(cmdname, env): out_environ = self._get_environ() env.update(out_environ) - proc = subprocess.Popen(' '.join((cmdname, flag)), - shell=True, - env=env, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - o, e = proc.communicate() - return o + proc = subprocess.Popen(' '.join((cmdname, flag)), shell=True, env=env, + stdout=subprocess.PIPE, stderr=subprocess.PIPE,) + out, _ = proc.communicate() + return out def _run_wrapper(self, runtime): runtime = self._run_interface(runtime) return runtime - def _run_interface(self, runtime, correct_return_codes=[0]): + def _run_interface(self, runtime, **kwargs): """Execute command via subprocess Parameters @@ -1495,6 +896,10 @@ def _run_interface(self, runtime, correct_return_codes=[0]): adds stdout, stderr, merged, cmdline, dependencies, command_path """ + correct_return_codes = [0] + if 'correct_return_codes' in kwargs.keys(): + correct_return_codes = kwargs[correct_return_codes] + setattr(runtime, 'stdout', None) setattr(runtime, 'stderr', None) setattr(runtime, 'cmdline', self.cmdline) @@ -1517,196 +922,15 @@ def _run_interface(self, runtime, correct_return_codes=[0]): return runtime - def _format_arg(self, name, trait_spec, value): - """A helper function for _parse_inputs - - Formats a trait containing argstr metadata - """ - argstr = trait_spec.argstr - iflogger.debug('%s_%s' % (name, str(value))) - if trait_spec.is_trait_type(traits.Bool) and "%" not in argstr: - if value: - # Boolean options have no format string. Just append options - # if True. - return argstr - else: - return None - # traits.Either turns into traits.TraitCompound and does not have any - # inner_traits - elif trait_spec.is_trait_type(traits.List) \ - or (trait_spec.is_trait_type(traits.TraitCompound) and - isinstance(value, list)): - # This is a bit simple-minded at present, and should be - # construed as the default. If more sophisticated behavior - # is needed, it can be accomplished with metadata (e.g. - # format string for list member str'ification, specifying - # the separator, etc.) - - # Depending on whether we stick with traitlets, and whether or - # not we beef up traitlets.List, we may want to put some - # type-checking code here as well - sep = trait_spec.sep - if sep is None: - sep = ' ' - if argstr.endswith('...'): - - # repeatable option - # --id %d... will expand to - # --id 1 --id 2 --id 3 etc.,. - argstr = argstr.replace('...', '') - return sep.join([argstr % elt for elt in value]) - else: - return argstr % sep.join(str(elt) for elt in value) - else: - # Append options using format string. - return argstr % value - - def _filename_from_source(self, name, chain=None): - if chain is None: - chain = [] - - trait_spec = self.inputs.trait(name) - retval = getattr(self.inputs, name) - source_ext = None - if not isdefined(retval) or "%s" in retval: - if not trait_spec.name_source: - return retval - if isdefined(retval) and "%s" in retval: - name_template = retval - else: - name_template = trait_spec.name_template - if not name_template: - name_template = "%s_generated" - - ns = trait_spec.name_source - while isinstance(ns, list): - if len(ns) > 1: - iflogger.warn('Only one name_source per trait is allowed') - ns = ns[0] - - if not isinstance(ns, string_types): - raise ValueError(('name_source of \'%s\' trait sould be an ' - 'input trait name') % name) - - if isdefined(getattr(self.inputs, ns)): - name_source = ns - source = getattr(self.inputs, name_source) - while isinstance(source, list): - source = source[0] - - # special treatment for files - try: - _, base, source_ext = split_filename(source) - except AttributeError: - base = source - else: - if name in chain: - raise NipypeInterfaceError('Mutually pointing name_sources') - - chain.append(name) - base = self._filename_from_source(ns, chain) - if isdefined(base): - _, _, source_ext = split_filename(base) - - chain = None - retval = name_template % base - _, _, ext = split_filename(retval) - if trait_spec.keep_extension and (ext or source_ext): - if (ext is None or not ext) and source_ext: - retval = retval + source_ext - else: - retval = self._overload_extension(retval, name) - return retval - - def _gen_filename(self, name): - raise NotImplementedError - - def _overload_extension(self, value, name=None): - return value - - def _list_outputs(self): - metadata = dict(name_source=lambda t: t is not None) - traits = self.inputs.traits(**metadata) - if traits: - outputs = self.output_spec().get() - for name, trait_spec in traits.items(): - out_name = name - if trait_spec.output_name is not None: - out_name = trait_spec.output_name - outputs[out_name] = \ - os.path.abspath(self._filename_from_source(name)) - return outputs - - def _parse_inputs(self, skip=None): - """Parse all inputs using the ``argstr`` format string in the Trait. - - Any inputs that are assigned (not the default_value) are formatted - to be added to the command line. - - Returns - ------- - all_args : list - A list of all inputs formatted for the command line. - - """ - all_args = [] - initial_args = {} - final_args = {} - metadata = dict(argstr=lambda t: t is not None) - for name, spec in sorted(self.inputs.traits(**metadata).items()): - if skip and name in skip: - continue - value = getattr(self.inputs, name) - if spec.genfile or spec.name_source: - value = self._filename_from_source(name) - if not isdefined(value): - value = self._gen_filename(name) - if not isdefined(value): - continue - arg = self._format_arg(name, spec, value) - if arg is None: - continue - pos = spec.position - if pos is not None: - if int(pos) >= 0: - initial_args[pos] = arg - else: - final_args[pos] = arg - else: - all_args.append(arg) - first_args = [arg for pos, arg in sorted(initial_args.items())] - last_args = [arg for pos, arg in sorted(final_args.items())] - return first_args + all_args + last_args - - -class StdOutCommandLineInputSpec(CommandLineInputSpec): - out_file = File(argstr="> %s", position=-1, genfile=True) - class StdOutCommandLine(CommandLine): + """A command line that writes into the output stream""" input_spec = StdOutCommandLineInputSpec - - def _gen_filename(self, name): - if name is 'out_file': - return self._gen_outfilename() - else: - return None - - def _gen_outfilename(self): - raise NotImplementedError - - -class MpiCommandLineInputSpec(CommandLineInputSpec): - use_mpi = traits.Bool(False, - desc="Whether or not to run the command with mpiexec", - usedefault=True) - n_procs = traits.Int(desc="Num processors to specify to mpiexec. Do not " - "specify if this is managed externally (e.g. through " - "SGE)") + output_spec = StdOutCommandLineOutputSpec class MpiCommandLine(CommandLine): - '''Implements functionality to interact with command line programs + """Implements functionality to interact with command line programs that can be run with MPI (i.e. using 'mpiexec'). Examples @@ -1721,7 +945,7 @@ class MpiCommandLine(CommandLine): >>> mpi_cli.inputs.n_procs = 8 >>> mpi_cli.cmdline 'mpiexec -n 8 my_mpi_prog -v' - ''' + """ input_spec = MpiCommandLineInputSpec @property @@ -1730,7 +954,7 @@ def cmdline(self): result = [] if self.inputs.use_mpi: result.append('mpiexec') - if self.inputs.n_procs: + if isdefined(self.inputs.n_procs): result.append('-n %d' % self.inputs.n_procs) result.append(super(MpiCommandLine, self).cmdline) return ' '.join(result) @@ -1744,140 +968,19 @@ class SEMLikeCommandLine(CommandLine): used but only for the reduced (by excluding those that do not have corresponding inputs list of outputs. """ + input_spec = SEMLikeCommandLineInputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - return self._outputs_from_inputs(outputs) - - def _outputs_from_inputs(self, outputs): - for name in list(outputs.keys()): + def _post_run(self): + for name in list(self.outputs.keys()): corresponding_input = getattr(self.inputs, name) if isdefined(corresponding_input): if (isinstance(corresponding_input, bool) and corresponding_input): - outputs[name] = \ - os.path.abspath(self._outputs_filenames[name]) + setattr(self.outputs, name, op.abspath( + self._outputs_filenames[name])) else: if isinstance(corresponding_input, list): - outputs[name] = [os.path.abspath(inp) - for inp in corresponding_input] + setattr(self.outputs, name, + [op.abspath(inp) for inp in corresponding_input]) else: - outputs[name] = os.path.abspath(corresponding_input) - return outputs - - def _format_arg(self, name, spec, value): - if name in list(self._outputs_filenames.keys()): - if isinstance(value, bool): - if value: - value = os.path.abspath(self._outputs_filenames[name]) - else: - return "" - return super(SEMLikeCommandLine, self)._format_arg(name, spec, value) - - -class MultiPath(traits.List): - """ Abstract class - shared functionality of input and output MultiPath - """ - - def validate(self, object, name, value): - if not isdefined(value) or \ - (isinstance(value, list) and len(value) == 0): - return Undefined - newvalue = value - - if not isinstance(value, list) \ - or (self.inner_traits() and - isinstance(self.inner_traits()[0].trait_type, - traits.List) and not - isinstance(self.inner_traits()[0].trait_type, - InputMultiPath) and - isinstance(value, list) and - value and not - isinstance(value[0], list)): - newvalue = [value] - value = super(MultiPath, self).validate(object, name, newvalue) - - if len(value) > 0: - return value - - self.error(object, name, value) - - -class OutputMultiPath(MultiPath): - """ Implements a user friendly traits that accepts one or more - paths to files or directories. This is the output version which - return a single string whenever possible (when it was set to a - single value or a list of length 1). Default value of this trait - is _Undefined. It does not accept empty lists. - - XXX This should only be used as a final resort. We should stick to - established Traits to the extent possible. - - XXX This needs to be vetted by somebody who understands traits - - >>> from nipype.interfaces.base import OutputMultiPath - >>> class A(TraitedSpec): - ... foo = OutputMultiPath(File(exists=False)) - >>> a = A() - >>> a.foo - - - >>> a.foo = '/software/temp/foo.txt' - >>> a.foo - '/software/temp/foo.txt' - - >>> a.foo = ['/software/temp/foo.txt'] - >>> a.foo - '/software/temp/foo.txt' - - >>> a.foo = ['/software/temp/foo.txt', '/software/temp/goo.txt'] - >>> a.foo - ['/software/temp/foo.txt', '/software/temp/goo.txt'] - - """ - - def get(self, object, name): - value = self.get_value(object, name) - if len(value) == 0: - return Undefined - elif len(value) == 1: - return value[0] - else: - return value - - def set(self, object, name, value): - self.set_value(object, name, value) - - -class InputMultiPath(MultiPath): - """ Implements a user friendly traits that accepts one or more - paths to files or directories. This is the input version which - always returns a list. Default value of this trait - is _Undefined. It does not accept empty lists. - - XXX This should only be used as a final resort. We should stick to - established Traits to the extent possible. - - XXX This needs to be vetted by somebody who understands traits - - >>> from nipype.interfaces.base import InputMultiPath - >>> class A(TraitedSpec): - ... foo = InputMultiPath(File(exists=False)) - >>> a = A() - >>> a.foo - - - >>> a.foo = '/software/temp/foo.txt' - >>> a.foo - ['/software/temp/foo.txt'] - - >>> a.foo = ['/software/temp/foo.txt'] - >>> a.foo - ['/software/temp/foo.txt'] - - >>> a.foo = ['/software/temp/foo.txt', '/software/temp/goo.txt'] - >>> a.foo - ['/software/temp/foo.txt', '/software/temp/goo.txt'] - - """ - pass + setattr(self.outputs, name, op.abspath(corresponding_input)) diff --git a/nipype/interfaces/camino/calib.py b/nipype/interfaces/camino/calib.py index a56e501e7c..d74b103599 100644 --- a/nipype/interfaces/camino/calib.py +++ b/nipype/interfaces/camino/calib.py @@ -120,12 +120,11 @@ class SFPICOCalibData(StdOutCommandLine): input_spec = SFPICOCalibDataInputSpec output_spec = SFPICOCalibDataOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['PICOCalib'] = os.path.abspath(self._gen_outfilename()) - outputs['calib_info'] = os.path.abspath(self.inputs.info_file) - return outputs - + def _post_run(self): + + self.outputs.PICOCalib = os.path.abspath(self._gen_outfilename()) + self.outputs.calib_info = os.path.abspath(self.inputs.info_file) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.scheme_file) return name + '_PICOCalib.Bfloat' @@ -227,11 +226,10 @@ class SFLUTGen(StdOutCommandLine): input_spec = SFLUTGenInputSpec output_spec = SFLUTGenOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['lut_one_fibre'] = self.inputs.outputstem + '_oneFibreSurfaceCoeffs.Bdouble' - outputs['lut_two_fibres'] = self.inputs.outputstem + '_twoFibreSurfaceCoeffs.Bdouble' - return outputs - + def _post_run(self): + + self.outputs.lut_one_fibre = self.inputs.outputstem + '_oneFibreSurfaceCoeffs.Bdouble' + self.outputs.lut_two_fibres = self.inputs.outputstem + '_twoFibreSurfaceCoeffs.Bdouble' + def _gen_outfilename(self): return '/dev/null' diff --git a/nipype/interfaces/camino/connectivity.py b/nipype/interfaces/camino/connectivity.py index 3a41c801e2..099b2d92b4 100644 --- a/nipype/interfaces/camino/connectivity.py +++ b/nipype/interfaces/camino/connectivity.py @@ -132,13 +132,12 @@ class Conmat(CommandLine): input_spec = ConmatInputSpec output_spec = ConmatOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + output_root = self._gen_outputroot() - outputs['conmat_sc'] = os.path.abspath(output_root + "sc.csv") - outputs['conmat_ts'] = os.path.abspath(output_root + "ts.csv") - return outputs - + self.outputs.conmat_sc = os.path.abspath(output_root + "sc.csv") + self.outputs.conmat_ts = os.path.abspath(output_root + "ts.csv") + def _gen_outfilename(self): return self._gen_outputroot() diff --git a/nipype/interfaces/camino/convert.py b/nipype/interfaces/camino/convert.py index cdde8a2b88..2ea8a8446a 100644 --- a/nipype/interfaces/camino/convert.py +++ b/nipype/interfaces/camino/convert.py @@ -56,11 +56,10 @@ class Image2Voxel(StdOutCommandLine): input_spec = Image2VoxelInputSpec output_spec = Image2VoxelOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['voxel_order'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.voxel_order = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '.B' + self.inputs.out_type @@ -114,11 +113,10 @@ class FSL2Scheme(StdOutCommandLine): input_spec = FSL2SchemeInputSpec output_spec = FSL2SchemeOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['scheme'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.scheme = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.bvec_file) return name + '.scheme' @@ -170,11 +168,10 @@ class VtkStreamlines(StdOutCommandLine): input_spec = VtkStreamlinesInputSpec output_spec = VtkStreamlinesOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['vtk'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.vtk = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '.vtk' @@ -298,12 +295,11 @@ def _get_actual_outputroot(self, outputroot): actual_outputroot = os.path.join('procstream_outfiles', outputroot) return actual_outputroot - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['proc'] = os.path.abspath(self._gen_outfilename()) - outputs['outputroot_files'] = self.outputroot_files - return outputs - + def _post_run(self): + + self.outputs.proc = os.path.abspath(self._gen_outfilename()) + self.outputs.outputroot_files = self.outputroot_files + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_proc' @@ -351,11 +347,10 @@ class TractShredder(StdOutCommandLine): input_spec = TractShredderInputSpec output_spec = TractShredderOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['shredded'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.shredded = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + "_shredded" @@ -390,14 +385,13 @@ class DT2NIfTI(CommandLine): input_spec = DT2NIfTIInputSpec output_spec = DT2NIfTIOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + output_root = self._gen_outputroot() - outputs["dt"] = os.path.abspath(output_root + "dt.nii") - outputs["exitcode"] = os.path.abspath(output_root + "exitcode.nii") - outputs["lns0"] = os.path.abspath(output_root + "lns0.nii") - return outputs - + self.outputs.dt = os.path.abspath(output_root + "dt.nii") + self.outputs.exitcode = os.path.abspath(output_root + "exitcode.nii") + self.outputs.lns0 = os.path.abspath(output_root + "lns0.nii") + def _gen_outfilename(self): return self._gen_outputroot() @@ -473,11 +467,10 @@ class NIfTIDT2Camino(CommandLine): input_spec = NIfTIDT2CaminoInputSpec output_spec = NIfTIDT2CaminoOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["out_file"] = self._gen_filename('out_file') - return outputs - + def _post_run(self): + + self.outputs.out_file = self._gen_filename('out_file') + def _gen_filename(self, name): if name == 'out_file': _, filename, _ = split_filename(self.inputs.in_file) @@ -627,11 +620,10 @@ class AnalyzeHeader(StdOutCommandLine): input_spec = AnalyzeHeaderInputSpec output_spec = AnalyzeHeaderOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['header'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.header = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + ".hdr" @@ -681,11 +673,10 @@ class Shredder(StdOutCommandLine): input_spec = ShredderInputSpec output_spec = ShredderOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['shredded_file'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.shredded_file = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + "_shredded" diff --git a/nipype/interfaces/camino/dti.py b/nipype/interfaces/camino/dti.py index 8402fcf45f..1702f31fdc 100644 --- a/nipype/interfaces/camino/dti.py +++ b/nipype/interfaces/camino/dti.py @@ -61,11 +61,10 @@ class DTIFit(StdOutCommandLine): input_spec = DTIFitInputSpec output_spec = DTIFitOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['tensor_fitted'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.tensor_fitted = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_DT.Bdouble' @@ -147,11 +146,10 @@ class DTMetric(CommandLine): input_spec = DTMetricInputSpec output_spec = DTMetricOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['metric_stats'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.metric_stats = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): return self._gen_outputfile() @@ -251,11 +249,10 @@ class ModelFit(StdOutCommandLine): input_spec = ModelFitInputSpec output_spec = ModelFitOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['fitted_data'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.fitted_data = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_fit.Bdouble' @@ -333,11 +330,10 @@ class DTLUTGen(StdOutCommandLine): input_spec = DTLUTGenInputSpec output_spec = DTLUTGenOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['dtLUT'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.dtLUT = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.scheme_file) return name + '.dat' @@ -397,11 +393,10 @@ class PicoPDFs(StdOutCommandLine): input_spec = PicoPDFsInputSpec output_spec = PicoPDFsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['pdfs'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.pdfs = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_pdfs.Bdouble' @@ -564,15 +559,14 @@ class Track(CommandLine): input_spec = TrackInputSpec output_spec = TrackOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.out_file): out_file_path = os.path.abspath(self.inputs.out_file) else: out_file_path = os.path.abspath(self._gen_outfilename()) - outputs['tracked'] = out_file_path - return outputs - + self.outputs.tracked = out_file_path + def _gen_filename(self, name): if name is 'out_file': return self._gen_outfilename() @@ -872,11 +866,10 @@ class ComputeMeanDiffusivity(StdOutCommandLine): input_spec = ComputeMeanDiffusivityInputSpec output_spec = ComputeMeanDiffusivityOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["md"] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.md = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + "_MD.img" # Need to change to self.inputs.outputdatatype @@ -934,11 +927,10 @@ class ComputeFractionalAnisotropy(StdOutCommandLine): input_spec = ComputeFractionalAnisotropyInputSpec output_spec = ComputeFractionalAnisotropyOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['fa'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.fa = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_FA.Bdouble' # Need to change to self.inputs.outputdatatype @@ -998,11 +990,10 @@ class ComputeTensorTrace(StdOutCommandLine): input_spec = ComputeTensorTraceInputSpec output_spec = ComputeTensorTraceOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['trace'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.trace = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_TrD.img' # Need to change to self.inputs.outputdatatype @@ -1058,11 +1049,10 @@ class ComputeEigensystem(StdOutCommandLine): input_spec = ComputeEigensystemInputSpec output_spec = ComputeEigensystemOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["eigen"] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.eigen = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) datatype = self.inputs.outputdatatype diff --git a/nipype/interfaces/camino/odf.py b/nipype/interfaces/camino/odf.py index e39bc81117..2d8d6f1754 100644 --- a/nipype/interfaces/camino/odf.py +++ b/nipype/interfaces/camino/odf.py @@ -82,11 +82,10 @@ class QBallMX(StdOutCommandLine): input_spec = QBallMXInputSpec output_spec = QBallMXOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['qmat'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.qmat = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.scheme_file) return name + '_qmat.Bdouble' @@ -159,11 +158,10 @@ class LinRecon(StdOutCommandLine): input_spec = LinReconInputSpec output_spec = LinReconOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['recon_data'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.recon_data = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.scheme_file) return name + '_recondata.Bdouble' @@ -283,11 +281,10 @@ class MESD(StdOutCommandLine): input_spec = MESDInputSpec output_spec = MESDOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['mesd_data'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.mesd_data = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.scheme_file) return name + '_MESD.Bdouble' @@ -430,11 +427,10 @@ class SFPeaks(StdOutCommandLine): input_spec = SFPeaksInputSpec output_spec = SFPeaksOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['peaks'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.peaks = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) return name + '_peaks.Bdouble' diff --git a/nipype/interfaces/camino/tests/test_auto_AnalyzeHeader.py b/nipype/interfaces/camino/tests/test_auto_AnalyzeHeader.py index 324fe35d1b..2339ef9c65 100644 --- a/nipype/interfaces/camino/tests/test_auto_AnalyzeHeader.py +++ b/nipype/interfaces/camino/tests/test_auto_AnalyzeHeader.py @@ -44,8 +44,8 @@ def test_AnalyzeHeader_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), picoseed=dict(argstr='-picoseed %s', units='mm', diff --git a/nipype/interfaces/camino/tests/test_auto_ComputeEigensystem.py b/nipype/interfaces/camino/tests/test_auto_ComputeEigensystem.py index d62e37c212..9dccbfc586 100644 --- a/nipype/interfaces/camino/tests/test_auto_ComputeEigensystem.py +++ b/nipype/interfaces/camino/tests/test_auto_ComputeEigensystem.py @@ -24,8 +24,8 @@ def test_ComputeEigensystem_inputs(): maxcomponents=dict(argstr='-maxcomponents %d', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outputdatatype=dict(argstr='-outputdatatype %s', usedefault=True, diff --git a/nipype/interfaces/camino/tests/test_auto_ComputeFractionalAnisotropy.py b/nipype/interfaces/camino/tests/test_auto_ComputeFractionalAnisotropy.py index 0a022eb1c3..63b5199358 100644 --- a/nipype/interfaces/camino/tests/test_auto_ComputeFractionalAnisotropy.py +++ b/nipype/interfaces/camino/tests/test_auto_ComputeFractionalAnisotropy.py @@ -21,8 +21,8 @@ def test_ComputeFractionalAnisotropy_inputs(): inputmodel=dict(argstr='-inputmodel %s', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outputdatatype=dict(argstr='-outputdatatype %s', ), diff --git a/nipype/interfaces/camino/tests/test_auto_ComputeTensorTrace.py b/nipype/interfaces/camino/tests/test_auto_ComputeTensorTrace.py index b7d7561cdb..e98e5e7b16 100644 --- a/nipype/interfaces/camino/tests/test_auto_ComputeTensorTrace.py +++ b/nipype/interfaces/camino/tests/test_auto_ComputeTensorTrace.py @@ -21,8 +21,8 @@ def test_ComputeTensorTrace_inputs(): inputmodel=dict(argstr='-inputmodel %s', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outputdatatype=dict(argstr='-outputdatatype %s', ), diff --git a/nipype/interfaces/camino/tests/test_auto_DTIFit.py b/nipype/interfaces/camino/tests/test_auto_DTIFit.py index 8607d3d7ae..3720d95f58 100644 --- a/nipype/interfaces/camino/tests/test_auto_DTIFit.py +++ b/nipype/interfaces/camino/tests/test_auto_DTIFit.py @@ -22,8 +22,8 @@ def test_DTIFit_inputs(): position=3, ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), scheme_file=dict(argstr='%s', mandatory=True, diff --git a/nipype/interfaces/camino/tests/test_auto_DTLUTGen.py b/nipype/interfaces/camino/tests/test_auto_DTLUTGen.py index 6cae7fee81..30e884b1c1 100644 --- a/nipype/interfaces/camino/tests/test_auto_DTLUTGen.py +++ b/nipype/interfaces/camino/tests/test_auto_DTLUTGen.py @@ -28,8 +28,8 @@ def test_DTLUTGen_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), samples=dict(argstr='-samples %d', units='NA', diff --git a/nipype/interfaces/camino/tests/test_auto_FSL2Scheme.py b/nipype/interfaces/camino/tests/test_auto_FSL2Scheme.py index b182c5a862..70432d5936 100644 --- a/nipype/interfaces/camino/tests/test_auto_FSL2Scheme.py +++ b/nipype/interfaces/camino/tests/test_auto_FSL2Scheme.py @@ -38,8 +38,8 @@ def test_FSL2Scheme_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), terminal_output=dict(nohash=True, ), diff --git a/nipype/interfaces/camino/tests/test_auto_Image2Voxel.py b/nipype/interfaces/camino/tests/test_auto_Image2Voxel.py index 57da324d6c..c55f9189de 100644 --- a/nipype/interfaces/camino/tests/test_auto_Image2Voxel.py +++ b/nipype/interfaces/camino/tests/test_auto_Image2Voxel.py @@ -17,8 +17,8 @@ def test_Image2Voxel_inputs(): position=1, ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), out_type=dict(argstr='-outputdatatype %s', position=2, diff --git a/nipype/interfaces/camino/tests/test_auto_LinRecon.py b/nipype/interfaces/camino/tests/test_auto_LinRecon.py index 311bd70fdf..3e3aa55fad 100644 --- a/nipype/interfaces/camino/tests/test_auto_LinRecon.py +++ b/nipype/interfaces/camino/tests/test_auto_LinRecon.py @@ -23,8 +23,8 @@ def test_LinRecon_inputs(): normalize=dict(argstr='-normalize', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), qball_mat=dict(argstr='%s', mandatory=True, diff --git a/nipype/interfaces/camino/tests/test_auto_MESD.py b/nipype/interfaces/camino/tests/test_auto_MESD.py index 018d820a96..3fd26c8ae5 100644 --- a/nipype/interfaces/camino/tests/test_auto_MESD.py +++ b/nipype/interfaces/camino/tests/test_auto_MESD.py @@ -36,8 +36,8 @@ def test_MESD_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), scheme_file=dict(argstr='-schemefile %s', mandatory=True, diff --git a/nipype/interfaces/camino/tests/test_auto_ModelFit.py b/nipype/interfaces/camino/tests/test_auto_ModelFit.py index f56a605962..64f0d1cdfa 100644 --- a/nipype/interfaces/camino/tests/test_auto_ModelFit.py +++ b/nipype/interfaces/camino/tests/test_auto_ModelFit.py @@ -33,8 +33,8 @@ def test_ModelFit_inputs(): noisemap=dict(argstr='-noisemap %s', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outlier=dict(argstr='-outliermap %s', ), diff --git a/nipype/interfaces/camino/tests/test_auto_NIfTIDT2Camino.py b/nipype/interfaces/camino/tests/test_auto_NIfTIDT2Camino.py index dd710905b2..ca4c47a787 100644 --- a/nipype/interfaces/camino/tests/test_auto_NIfTIDT2Camino.py +++ b/nipype/interfaces/camino/tests/test_auto_NIfTIDT2Camino.py @@ -21,8 +21,8 @@ def test_NIfTIDT2Camino_inputs(): lns0_file=dict(argstr='-lns0 %s', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), s0_file=dict(argstr='-s0 %s', ), diff --git a/nipype/interfaces/camino/tests/test_auto_PicoPDFs.py b/nipype/interfaces/camino/tests/test_auto_PicoPDFs.py index 4f4a0b75be..3a855b7ccf 100644 --- a/nipype/interfaces/camino/tests/test_auto_PicoPDFs.py +++ b/nipype/interfaces/camino/tests/test_auto_PicoPDFs.py @@ -32,8 +32,8 @@ def test_PicoPDFs_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), pdf=dict(argstr='-pdf %s', position=4, diff --git a/nipype/interfaces/camino/tests/test_auto_ProcStreamlines.py b/nipype/interfaces/camino/tests/test_auto_ProcStreamlines.py index 99ecff3624..560074ecb8 100644 --- a/nipype/interfaces/camino/tests/test_auto_ProcStreamlines.py +++ b/nipype/interfaces/camino/tests/test_auto_ProcStreamlines.py @@ -53,8 +53,8 @@ def test_ProcStreamlines_inputs(): noresample=dict(argstr='-noresample', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outputacm=dict(argstr='-outputacm', requires=['outputroot', 'seedfile'], diff --git a/nipype/interfaces/camino/tests/test_auto_QBallMX.py b/nipype/interfaces/camino/tests/test_auto_QBallMX.py index 9a4b2375c8..c73e15921c 100644 --- a/nipype/interfaces/camino/tests/test_auto_QBallMX.py +++ b/nipype/interfaces/camino/tests/test_auto_QBallMX.py @@ -19,8 +19,8 @@ def test_QBallMX_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), rbfpointset=dict(argstr='-rbfpointset %d', units='NA', diff --git a/nipype/interfaces/camino/tests/test_auto_SFLUTGen.py b/nipype/interfaces/camino/tests/test_auto_SFLUTGen.py index 6d59c40c3e..5b2811235d 100644 --- a/nipype/interfaces/camino/tests/test_auto_SFLUTGen.py +++ b/nipype/interfaces/camino/tests/test_auto_SFLUTGen.py @@ -30,8 +30,8 @@ def test_SFLUTGen_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), outputstem=dict(argstr='-outputstem %s', usedefault=True, diff --git a/nipype/interfaces/camino/tests/test_auto_SFPICOCalibData.py b/nipype/interfaces/camino/tests/test_auto_SFPICOCalibData.py index 4adfe50709..e187704b71 100644 --- a/nipype/interfaces/camino/tests/test_auto_SFPICOCalibData.py +++ b/nipype/interfaces/camino/tests/test_auto_SFPICOCalibData.py @@ -24,8 +24,8 @@ def test_SFPICOCalibData_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), scheme_file=dict(argstr='-schemefile %s', mandatory=True, diff --git a/nipype/interfaces/camino/tests/test_auto_SFPeaks.py b/nipype/interfaces/camino/tests/test_auto_SFPeaks.py index 69c85404c1..632547c793 100644 --- a/nipype/interfaces/camino/tests/test_auto_SFPeaks.py +++ b/nipype/interfaces/camino/tests/test_auto_SFPeaks.py @@ -33,8 +33,8 @@ def test_SFPeaks_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), pdthresh=dict(argstr='-pdthresh %f', units='NA', diff --git a/nipype/interfaces/camino/tests/test_auto_Shredder.py b/nipype/interfaces/camino/tests/test_auto_Shredder.py index 7f36415c0c..347daf4e14 100644 --- a/nipype/interfaces/camino/tests/test_auto_Shredder.py +++ b/nipype/interfaces/camino/tests/test_auto_Shredder.py @@ -25,8 +25,8 @@ def test_Shredder_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), space=dict(argstr='%d', position=3, diff --git a/nipype/interfaces/camino/tests/test_auto_TractShredder.py b/nipype/interfaces/camino/tests/test_auto_TractShredder.py index d18ec2e9ca..91286e9b64 100644 --- a/nipype/interfaces/camino/tests/test_auto_TractShredder.py +++ b/nipype/interfaces/camino/tests/test_auto_TractShredder.py @@ -25,8 +25,8 @@ def test_TractShredder_inputs(): units='NA', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), space=dict(argstr='%d', position=3, diff --git a/nipype/interfaces/camino/tests/test_auto_VtkStreamlines.py b/nipype/interfaces/camino/tests/test_auto_VtkStreamlines.py index 805f4709cb..68b16ea128 100644 --- a/nipype/interfaces/camino/tests/test_auto_VtkStreamlines.py +++ b/nipype/interfaces/camino/tests/test_auto_VtkStreamlines.py @@ -26,8 +26,8 @@ def test_VtkStreamlines_inputs(): interpolatescalars=dict(argstr='-interpolatescalars', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), scalar_file=dict(argstr='-scalarfile %s', position=3, diff --git a/nipype/interfaces/camino/utils.py b/nipype/interfaces/camino/utils.py index 19fe6ac768..da68872341 100644 --- a/nipype/interfaces/camino/utils.py +++ b/nipype/interfaces/camino/utils.py @@ -55,11 +55,10 @@ class ImageStats(CommandLine): input_spec = ImageStatsInputSpec output_spec = ImageStatsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.out_file = os.path.abspath(self._gen_outfilename()) + def _gen_outfilename(self): output_root = self.inputs.output_root first_file = self.inputs.in_files[0] diff --git a/nipype/interfaces/camino2trackvis/convert.py b/nipype/interfaces/camino2trackvis/convert.py index 9075a06ee2..94291a55b2 100644 --- a/nipype/interfaces/camino2trackvis/convert.py +++ b/nipype/interfaces/camino2trackvis/convert.py @@ -73,11 +73,10 @@ class Camino2Trackvis(CommandLine): input_spec = Camino2TrackvisInputSpec output_spec = Camino2TrackvisOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['trackvis'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.trackvis = os.path.abspath(self._gen_outfilename()) + def _gen_filename(self, name): if name is 'out_file': return self._gen_outfilename() @@ -124,11 +123,10 @@ class Trackvis2Camino(CommandLine): input_spec = Trackvis2CaminoInputSpec output_spec = Trackvis2CaminoOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['camino'] = os.path.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.camino = os.path.abspath(self._gen_outfilename()) + def _gen_filename(self, name): if name is 'out_file': return self._gen_outfilename() diff --git a/nipype/interfaces/cmtk/cmtk.py b/nipype/interfaces/cmtk/cmtk.py index f527cccc0a..ff293f3912 100644 --- a/nipype/interfaces/cmtk/cmtk.py +++ b/nipype/interfaces/cmtk/cmtk.py @@ -496,8 +496,8 @@ def _run_interface(self, runtime): matrix_file, matrix_mat_file, endpoint_name, self.inputs.count_region_intersections) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.out_matrix_file): path, name, _ = split_filename(self.inputs.out_matrix_file) out_matrix_file = op.abspath(name + '.pck') @@ -506,8 +506,8 @@ def _list_outputs(self): out_matrix_file = op.abspath(self._gen_outfilename('.pck')) out_intersection_matrix_file = op.abspath(self._gen_outfilename('_intersections.pck')) - outputs['matrix_file'] = out_matrix_file - outputs['intersection_matrix_file'] = out_intersection_matrix_file + self.outputs.matrix_file = out_matrix_file + self.outputs.intersection_matrix_file = out_intersection_matrix_file matrix_mat_file = op.abspath(self.inputs.out_matrix_mat_file) path, name, ext = split_filename(matrix_mat_file) @@ -515,59 +515,58 @@ def _list_outputs(self): ext = '.mat' matrix_mat_file = matrix_mat_file + ext - outputs['matrix_mat_file'] = matrix_mat_file + self.outputs.matrix_mat_file = matrix_mat_file if isdefined(self.inputs.out_mean_fiber_length_matrix_mat_file): - outputs['mean_fiber_length_matrix_mat_file'] = op.abspath(self.inputs.out_mean_fiber_length_matrix_mat_file) + self.outputs.mean_fiber_length_matrix_mat_file = op.abspath(self.inputs.out_mean_fiber_length_matrix_mat_file) else: - outputs['mean_fiber_length_matrix_mat_file'] = op.abspath(self._gen_outfilename('_mean_fiber_length.mat')) + self.outputs.mean_fiber_length_matrix_mat_file = op.abspath(self._gen_outfilename('_mean_fiber_length.mat')) if isdefined(self.inputs.out_median_fiber_length_matrix_mat_file): - outputs['median_fiber_length_matrix_mat_file'] = op.abspath(self.inputs.out_median_fiber_length_matrix_mat_file) + self.outputs.median_fiber_length_matrix_mat_file = op.abspath(self.inputs.out_median_fiber_length_matrix_mat_file) else: - outputs['median_fiber_length_matrix_mat_file'] = op.abspath(self._gen_outfilename('_median_fiber_length.mat')) + self.outputs.median_fiber_length_matrix_mat_file = op.abspath(self._gen_outfilename('_median_fiber_length.mat')) if isdefined(self.inputs.out_fiber_length_std_matrix_mat_file): - outputs['fiber_length_std_matrix_mat_file'] = op.abspath(self.inputs.out_fiber_length_std_matrix_mat_file) + self.outputs.fiber_length_std_matrix_mat_file = op.abspath(self.inputs.out_fiber_length_std_matrix_mat_file) else: - outputs['fiber_length_std_matrix_mat_file'] = op.abspath(self._gen_outfilename('_fiber_length_std.mat')) + self.outputs.fiber_length_std_matrix_mat_file = op.abspath(self._gen_outfilename('_fiber_length_std.mat')) if isdefined(self.inputs.out_intersection_matrix_mat_file): - outputs['intersection_matrix_mat_file'] = op.abspath(self.inputs.out_intersection_matrix_mat_file) + self.outputs.intersection_matrix_mat_file = op.abspath(self.inputs.out_intersection_matrix_mat_file) else: - outputs['intersection_matrix_mat_file'] = op.abspath(self._gen_outfilename('_intersections.mat')) + self.outputs.intersection_matrix_mat_file = op.abspath(self._gen_outfilename('_intersections.mat')) if isdefined(self.inputs.out_endpoint_array_name): endpoint_name = self.inputs.out_endpoint_array_name - outputs['endpoint_file'] = op.abspath(self.inputs.out_endpoint_array_name + '_endpoints.npy') - outputs['endpoint_file_mm'] = op.abspath(self.inputs.out_endpoint_array_name + '_endpointsmm.npy') - outputs['fiber_length_file'] = op.abspath(self.inputs.out_endpoint_array_name + '_final_fiberslength.npy') - outputs['fiber_label_file'] = op.abspath(self.inputs.out_endpoint_array_name + '_filtered_fiberslabel.npy') - outputs['fiber_labels_noorphans'] = op.abspath(self.inputs.out_endpoint_array_name + '_final_fiberslabels.npy') + self.outputs.endpoint_file = op.abspath(self.inputs.out_endpoint_array_name + '_endpoints.npy') + self.outputs.endpoint_file_mm = op.abspath(self.inputs.out_endpoint_array_name + '_endpointsmm.npy') + self.outputs.fiber_length_file = op.abspath(self.inputs.out_endpoint_array_name + '_final_fiberslength.npy') + self.outputs.fiber_label_file = op.abspath(self.inputs.out_endpoint_array_name + '_filtered_fiberslabel.npy') + self.outputs.fiber_labels_noorphans = op.abspath(self.inputs.out_endpoint_array_name + '_final_fiberslabels.npy') else: _, endpoint_name, _ = split_filename(self.inputs.tract_file) - outputs['endpoint_file'] = op.abspath(endpoint_name + '_endpoints.npy') - outputs['endpoint_file_mm'] = op.abspath(endpoint_name + '_endpointsmm.npy') - outputs['fiber_length_file'] = op.abspath(endpoint_name + '_final_fiberslength.npy') - outputs['fiber_label_file'] = op.abspath(endpoint_name + '_filtered_fiberslabel.npy') - outputs['fiber_labels_noorphans'] = op.abspath(endpoint_name + '_final_fiberslabels.npy') + self.outputs.endpoint_file = op.abspath(endpoint_name + '_endpoints.npy') + self.outputs.endpoint_file_mm = op.abspath(endpoint_name + '_endpointsmm.npy') + self.outputs.fiber_length_file = op.abspath(endpoint_name + '_final_fiberslength.npy') + self.outputs.fiber_label_file = op.abspath(endpoint_name + '_filtered_fiberslabel.npy') + self.outputs.fiber_labels_noorphans = op.abspath(endpoint_name + '_final_fiberslabels.npy') if self.inputs.count_region_intersections: - outputs['matrix_files'] = [out_matrix_file, out_intersection_matrix_file] - outputs['matlab_matrix_files'] = [outputs['matrix_mat_file'], - outputs['mean_fiber_length_matrix_mat_file'], outputs['median_fiber_length_matrix_mat_file'], - outputs['fiber_length_std_matrix_mat_file'], outputs['intersection_matrix_mat_file']] + self.outputs.matrix_files = [out_matrix_file, out_intersection_matrix_file] + self.outputs.matlab_matrix_files = [self.outputs.matrix_mat_file, + self.outputs.mean_fiber_length_matrix_mat_file, self.outputs.median_fiber_length_matrix_mat_file, + self.outputs.fiber_length_std_matrix_mat_file, self.outputs.intersection_matrix_mat_file] else: - outputs['matrix_files'] = [out_matrix_file] - outputs['matlab_matrix_files'] = [outputs['matrix_mat_file'], - outputs['mean_fiber_length_matrix_mat_file'], outputs['median_fiber_length_matrix_mat_file'], - outputs['fiber_length_std_matrix_mat_file']] - - outputs['filtered_tractography'] = op.abspath(endpoint_name + '_streamline_final.trk') - outputs['filtered_tractography_by_intersections'] = op.abspath(endpoint_name + '_intersections_streamline_final.trk') - outputs['filtered_tractographies'] = [outputs['filtered_tractography'], outputs['filtered_tractography_by_intersections']] - outputs['stats_file'] = op.abspath(endpoint_name + '_statistics.mat') - return outputs - + self.outputs.matrix_files = [out_matrix_file] + self.outputs.matlab_matrix_files = [self.outputs.matrix_mat_file, + self.outputs.mean_fiber_length_matrix_mat_file, self.outputs.median_fiber_length_matrix_mat_file, + self.outputs.fiber_length_std_matrix_mat_file] + + self.outputs.filtered_tractography = op.abspath(endpoint_name + '_streamline_final.trk') + self.outputs.filtered_tractography_by_intersections = op.abspath(endpoint_name + '_intersections_streamline_final.trk') + self.outputs.filtered_tractographies = [self.outputs.filtered_tractography, self.outputs.filtered_tractography_by_intersections] + self.outputs.stats_file = op.abspath(endpoint_name + '_statistics.mat') + def _gen_outfilename(self, ext): if ext.endswith("mat") and isdefined(self.inputs.out_matrix_mat_file): _, name, _ = split_filename(self.inputs.out_matrix_mat_file) @@ -718,18 +717,16 @@ def _run_interface(self, runtime): file.close() return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if isdefined(self.inputs.out_roi_file): - outputs['roi_file'] = op.abspath(self.inputs.out_roi_file) + self.outputs.roi_file = op.abspath(self.inputs.out_roi_file) else: - outputs['roi_file'] = op.abspath(self._gen_outfilename('nii')) + self.outputs.roi_file = op.abspath(self._gen_outfilename('nii')) if isdefined(self.inputs.out_dict_file): - outputs['dict_file'] = op.abspath(self.inputs.out_dict_file) + self.outputs.dict_file = op.abspath(self.inputs.out_dict_file) else: - outputs['dict_file'] = op.abspath(self._gen_outfilename('pck')) - return outputs - + self.outputs.dict_file = op.abspath(self._gen_outfilename('pck')) + def _gen_outfilename(self, ext): _, name, _ = split_filename(self.inputs.aparc_aseg_file) if self.inputs.use_freesurfer_LUT: @@ -789,7 +786,6 @@ def _run_interface(self, runtime): iflogger.info('Saving node network to {path}'.format(path=op.abspath(self.inputs.out_filename))) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['node_network'] = op.abspath(self.inputs.out_filename) - return outputs + def _post_run(self): + self.outputs.node_network = op.abspath(self.inputs.out_filename) + \ No newline at end of file diff --git a/nipype/interfaces/cmtk/convert.py b/nipype/interfaces/cmtk/convert.py index 33ee7616b9..efbc48063a 100644 --- a/nipype/interfaces/cmtk/convert.py +++ b/nipype/interfaces/cmtk/convert.py @@ -197,14 +197,12 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): _, name, ext = split_filename(self.inputs.out_file) if not ext == '.cff': ext = '.cff' - outputs['connectome_file'] = op.abspath(name + ext) - return outputs - + self.outputs.connectome_file = op.abspath(name + ext) + class MergeCNetworksInputSpec(BaseInterfaceInputSpec): in_files = InputMultiPath(File(exists=True), mandatory=True, desc='List of CFF files to extract networks from') @@ -259,10 +257,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): _, name, ext = split_filename(self.inputs.out_file) if not ext == '.cff': ext = '.cff' - outputs['connectome_file'] = op.abspath(name + ext) - return outputs + self.outputs.connectome_file = op.abspath(name + ext) + \ No newline at end of file diff --git a/nipype/interfaces/cmtk/nbs.py b/nipype/interfaces/cmtk/nbs.py index 8fd539691f..03e3b01893 100644 --- a/nipype/interfaces/cmtk/nbs.py +++ b/nipype/interfaces/cmtk/nbs.py @@ -127,8 +127,8 @@ def _run_interface(self, runtime): iflogger.info('Saving output p-value network as {out}'.format(out=pval_path)) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + THRESH = self.inputs.threshold K = self.inputs.number_of_permutations @@ -138,10 +138,9 @@ def _list_outputs(self): path = op.abspath('NBS_Result_' + details) pval_path = op.abspath('NBS_P_vals_' + details) - outputs['nbs_network'] = path - outputs['nbs_pval_network'] = pval_path - outputs['network_files'] = [path, pval_path] - return outputs - + self.outputs.nbs_network = path + self.outputs.nbs_pval_network = pval_path + self.outputs.network_files = [path, pval_path] + def _gen_outfilename(self, name, ext): return name + '.' + ext diff --git a/nipype/interfaces/cmtk/nx.py b/nipype/interfaces/cmtk/nx.py index 64b817a746..6138204a70 100644 --- a/nipype/interfaces/cmtk/nx.py +++ b/nipype/interfaces/cmtk/nx.py @@ -482,23 +482,22 @@ def _run_interface(self, runtime): dicts.append(out_file) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["k_core"] = op.abspath(self._gen_outfilename(self.inputs.out_k_core, 'pck')) - outputs["k_shell"] = op.abspath(self._gen_outfilename(self.inputs.out_k_shell, 'pck')) - outputs["k_crust"] = op.abspath(self._gen_outfilename(self.inputs.out_k_crust, 'pck')) - outputs["gpickled_network_files"] = gpickled - outputs["k_networks"] = kntwks - outputs["node_measure_networks"] = nodentwks - outputs["edge_measure_networks"] = edgentwks - outputs["matlab_dict_measures"] = dicts - outputs["global_measures_matlab"] = op.abspath(self._gen_outfilename('globalmetrics', 'mat')) - outputs["node_measures_matlab"] = op.abspath(self._gen_outfilename('nodemetrics', 'mat')) - outputs["edge_measures_matlab"] = op.abspath(self._gen_outfilename('edgemetrics', 'mat')) - outputs["matlab_matrix_files"] = [outputs["global_measures_matlab"], outputs["node_measures_matlab"], outputs["edge_measures_matlab"]] - outputs["pickled_extra_measures"] = op.abspath(self._gen_outfilename(self.inputs.out_pickled_extra_measures, 'pck')) - return outputs - + def _post_run(self): + + self.outputs.k_core = op.abspath(self._gen_outfilename(self.inputs.out_k_core, 'pck')) + self.outputs.k_shell = op.abspath(self._gen_outfilename(self.inputs.out_k_shell, 'pck')) + self.outputs.k_crust = op.abspath(self._gen_outfilename(self.inputs.out_k_crust, 'pck')) + self.outputs.gpickled_network_files = gpickled + self.outputs.k_networks = kntwks + self.outputs.node_measure_networks = nodentwks + self.outputs.edge_measure_networks = edgentwks + self.outputs.matlab_dict_measures = dicts + self.outputs.global_measures_matlab = op.abspath(self._gen_outfilename('globalmetrics', 'mat')) + self.outputs.node_measures_matlab = op.abspath(self._gen_outfilename('nodemetrics', 'mat')) + self.outputs.edge_measures_matlab = op.abspath(self._gen_outfilename('edgemetrics', 'mat')) + self.outputs.matlab_matrix_files = [self.outputs.global_measures_matlab, self.outputs.node_measures_matlab, self.outputs.edge_measures_matlab] + self.outputs.pickled_extra_measures = op.abspath(self._gen_outfilename(self.inputs.out_pickled_extra_measures, 'pck')) + def _gen_outfilename(self, name, ext): return name + '.' + ext @@ -547,20 +546,19 @@ def _run_interface(self, runtime): network_name, matlab_network_list = average_networks(self.inputs.in_files, ntwk_res_file, self.inputs.group_id) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if not isdefined(self.inputs.out_gpickled_groupavg): - outputs["gpickled_groupavg"] = op.abspath(self._gen_outfilename(self.inputs.group_id + '_average', 'pck')) + self.outputs.gpickled_groupavg = op.abspath(self._gen_outfilename(self.inputs.group_id + '_average', 'pck')) else: - outputs["gpickled_groupavg"] = op.abspath(self.inputs.out_gpickled_groupavg) + self.outputs.gpickled_groupavg = op.abspath(self.inputs.out_gpickled_groupavg) if not isdefined(self.inputs.out_gexf_groupavg): - outputs["gexf_groupavg"] = op.abspath(self._gen_outfilename(self.inputs.group_id + '_average', 'gexf')) + self.outputs.gexf_groupavg = op.abspath(self._gen_outfilename(self.inputs.group_id + '_average', 'gexf')) else: - outputs["gexf_groupavg"] = op.abspath(self.inputs.out_gexf_groupavg) - - outputs["matlab_groupavgs"] = matlab_network_list - return outputs + self.outputs.gexf_groupavg = op.abspath(self.inputs.out_gexf_groupavg) + self.outputs.matlab_groupavgs = matlab_network_list + def _gen_outfilename(self, name, ext): return name + '.' + ext diff --git a/nipype/interfaces/cmtk/parcellation.py b/nipype/interfaces/cmtk/parcellation.py index 80b0e72ab0..8a664053e9 100644 --- a/nipype/interfaces/cmtk/parcellation.py +++ b/nipype/interfaces/cmtk/parcellation.py @@ -581,26 +581,24 @@ def _run_interface(self, runtime): crop_and_move_datasets(self.inputs.subject_id, self.inputs.subjects_dir, self.inputs.freesurfer_dir, self.inputs.parcellation_name, self.inputs.out_roi_file, self.inputs.dilation) return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if isdefined(self.inputs.out_roi_file): - outputs['roi_file'] = op.abspath(self.inputs.out_roi_file) + self.outputs.roi_file = op.abspath(self.inputs.out_roi_file) else: - outputs['roi_file'] = op.abspath( + self.outputs.roi_file = op.abspath( self._gen_outfilename('nii.gz', 'ROI')) if self.inputs.dilation is True: - outputs['roiv_file'] = op.abspath(self._gen_outfilename( + self.outputs.roiv_file = op.abspath(self._gen_outfilename( 'nii.gz', 'ROIv')) - outputs['white_matter_mask_file'] = op.abspath('fsmask_1mm.nii.gz') - outputs['cc_unknown_file'] = op.abspath('cc_unknown.nii.gz') - outputs['ribbon_file'] = op.abspath('ribbon.nii.gz') - outputs['aseg_file'] = op.abspath('aseg.nii.gz') - outputs['roi_file_in_structural_space'] = op.abspath( + self.outputs.white_matter_mask_file = op.abspath('fsmask_1mm.nii.gz') + self.outputs.cc_unknown_file = op.abspath('cc_unknown.nii.gz') + self.outputs.ribbon_file = op.abspath('ribbon.nii.gz') + self.outputs.aseg_file = op.abspath('aseg.nii.gz') + self.outputs.roi_file_in_structural_space = op.abspath( 'ROI_HR_th.nii.gz') if self.inputs.dilation is True: - outputs['dilated_roi_file_in_structural_space'] = op.abspath( + self.outputs.dilated_roi_file_in_structural_space = op.abspath( 'ROIv_HR_th.nii.gz') - return outputs - + def _gen_outfilename(self, ext, prefix='ROI'): return prefix + '_' + self.inputs.parcellation_name + '.' + ext diff --git a/nipype/interfaces/dcm2nii.py b/nipype/interfaces/dcm2nii.py index b78d925517..d5d91cd061 100644 --- a/nipype/interfaces/dcm2nii.py +++ b/nipype/interfaces/dcm2nii.py @@ -32,7 +32,7 @@ class Dcm2niiInputSpec(CommandLineInputSpec): gzip_output = traits.Bool(False, argstr='-g', usedefault=True) id_in_filename = traits.Bool(False, argstr='-i', usedefault=True) nii_output = traits.Bool(True, argstr='-n', usedefault=True) - output_dir = Directory(exists=True, argstr='-o %s', genfile=True) + output_dir = Directory('.', exists=True, argstr='-o %s', usedefault=True) protocol_in_filename = traits.Bool(True, argstr='-p', usedefault=True) reorient = traits.Bool(argstr='-r') spm_analyze = traits.Bool(argstr='-s', xor=['nii_output']) @@ -159,15 +159,14 @@ def _parse_stdout(self, stdout): skip = False return files, reoriented_files, reoriented_and_cropped_files, bvecs, bvals - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['converted_files'] = self.output_files - outputs['reoriented_files'] = self.reoriented_files - outputs['reoriented_and_cropped_files'] = self.reoriented_and_cropped_files - outputs['bvecs'] = self.bvecs - outputs['bvals'] = self.bvals - return outputs - + def _post_run(self): + + self.outputs.converted_files = self.output_files + self.outputs.reoriented_files = self.reoriented_files + self.outputs.reoriented_and_cropped_files = self.reoriented_and_cropped_files + self.outputs.bvecs = self.bvecs + self.outputs.bvals = self.bvals + def _gen_filename(self, name): if name == 'output_dir': return os.getcwd() diff --git a/nipype/interfaces/dcmstack.py b/nipype/interfaces/dcmstack.py index a06065fa41..18dc39bed9 100644 --- a/nipype/interfaces/dcmstack.py +++ b/nipype/interfaces/dcmstack.py @@ -53,10 +53,10 @@ class NiftiGeneratorBaseInputSpec(TraitedSpec): class NiftiGeneratorBase(BaseInterface): - '''Base class for interfaces that produce Nifti files, potentially with - embedded meta data.''' + """Base class for interfaces that produce Nifti files, potentially with + embedded meta data.""" def _get_out_path(self, meta, idx=None): - '''Return the output path for the gernerated Nifti.''' + """Return the output path for the gernerated Nifti.""" if self.inputs.out_format: out_fmt = self.inputs.out_format else: @@ -112,7 +112,7 @@ class DcmStackOutputSpec(TraitedSpec): class DcmStack(NiftiGeneratorBase): - '''Create one Nifti file from a set of DICOM files. Can optionally embed + """Create one Nifti file from a set of DICOM files. Can optionally embed meta data. Example @@ -124,7 +124,7 @@ class DcmStack(NiftiGeneratorBase): >>> stacker.run() # doctest: +SKIP >>> result.outputs.out_file # doctest: +SKIP '/path/to/cwd/sequence.nii.gz' - ''' + """ input_spec = DcmStackInputSpec output_spec = DcmStackOutputSpec @@ -161,19 +161,17 @@ def _run_interface(self, runtime): nb.save(nii, self.out_path) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.out_path - return outputs - + def _post_run(self): + self.outputs.out_file = self.out_path + class GroupAndStackOutputSpec(TraitedSpec): out_list = traits.List(desc="List of output nifti files") class GroupAndStack(DcmStack): - '''Create (potentially) multiple Nifti files for a set of DICOM files. - ''' + """Create (potentially) multiple Nifti files for a set of DICOM files. + """ input_spec = DcmStackInputSpec output_spec = GroupAndStackOutputSpec @@ -193,11 +191,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_list"] = self.out_list - return outputs - + def _post_run(self): + self.outputs.out_list = self.out_list + class LookupMetaInputSpec(TraitedSpec): in_file = File(mandatory=True, @@ -213,7 +209,7 @@ class LookupMetaInputSpec(TraitedSpec): class LookupMeta(BaseInterface): - '''Lookup meta data values from a Nifti with embedded meta data. + """Lookup meta data values from a Nifti with embedded meta data. Example ------- @@ -228,7 +224,7 @@ class LookupMeta(BaseInterface): 9500.0 >>> result.outputs.TE # doctest: +SKIP 95.0 - ''' + """ input_spec = LookupMetaInputSpec output_spec = DynamicTraitedSpec @@ -251,8 +247,7 @@ def _outputs(self): # Not sure why this is needed for out_name in list(self._meta_keys.values()): _ = getattr(outputs, out_name) - return outputs - + def _run_interface(self, runtime): # If the 'meta_keys' input is a list, covert it to a dict self._make_name_map() @@ -263,11 +258,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): outputs.update(self.result) - return outputs - + class CopyMetaInputSpec(TraitedSpec): src_file = File(mandatory=True, exists=True) @@ -284,8 +277,8 @@ class CopyMetaOutputSpec(TraitedSpec): class CopyMeta(BaseInterface): - '''Copy meta data from one Nifti file to another. Useful for preserving - meta data after some processing steps.''' + """Copy meta data from one Nifti file to another. Useful for preserving + meta data after some processing steps.""" input_spec = CopyMetaInputSpec output_spec = CopyMetaOutputSpec @@ -320,11 +313,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['dest_file'] = self.out_path - return outputs - + def _post_run(self): + self.outputs.dest_file = self.out_path + class MergeNiftiInputSpec(NiftiGeneratorBaseInputSpec): in_files = traits.List(mandatory=True, @@ -351,8 +342,8 @@ def key_func(src_nii): class MergeNifti(NiftiGeneratorBase): - '''Merge multiple Nifti files into one. Merges together meta data - extensions as well.''' + """Merge multiple Nifti files into one. Merges together meta data + extensions as well.""" input_spec = MergeNiftiInputSpec output_spec = MergeNiftiOutputSpec @@ -378,11 +369,9 @@ def _run_interface(self, runtime): nb.save(merged.nii_img, self.out_path) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self.out_path - return outputs - + def _post_run(self): + self.outputs.out_file = self.out_path + class SplitNiftiInputSpec(NiftiGeneratorBaseInputSpec): in_file = File(exists=True, mandatory=True, desc="Nifti file to split") @@ -396,10 +385,10 @@ class SplitNiftiOutputSpec(TraitedSpec): class SplitNifti(NiftiGeneratorBase): - ''' + """ Split one Nifti file into many along the specified dimension. Each result has an updated meta data extension as well. - ''' + """ input_spec = SplitNiftiInputSpec output_spec = SplitNiftiOutputSpec @@ -420,7 +409,6 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_list'] = self.out_list - return outputs + def _post_run(self): + self.outputs.out_list = self.out_list + \ No newline at end of file diff --git a/nipype/interfaces/diffusion_toolkit/dti.py b/nipype/interfaces/diffusion_toolkit/dti.py index 554f2bf38a..921c6a225a 100644 --- a/nipype/interfaces/diffusion_toolkit/dti.py +++ b/nipype/interfaces/diffusion_toolkit/dti.py @@ -87,26 +87,25 @@ def _format_arg(self, name, spec, value): return super(DTIRecon, self)._format_arg("bvecs", spec, new_val) return super(DTIRecon, self)._format_arg(name, spec, value) - def _list_outputs(self): + def _post_run(self): out_prefix = self.inputs.out_prefix output_type = self.inputs.output_type - outputs = self.output_spec().get() - outputs['ADC'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_adc.' + output_type)) - outputs['B0'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_b0.' + output_type)) - outputs['L1'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e1.' + output_type)) - outputs['L2'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e2.' + output_type)) - outputs['L3'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e3.' + output_type)) - outputs['exp'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_exp.' + output_type)) - outputs['FA'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_fa.' + output_type)) - outputs['FA_color'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_fa_color.' + output_type)) - outputs['tensor'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_tensor.' + output_type)) - outputs['V1'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v1.' + output_type)) - outputs['V2'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v2.' + output_type)) - outputs['V3'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v3.' + output_type)) - - return outputs - + + self.outputs.ADC = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_adc.' + output_type)) + self.outputs.B0 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_b0.' + output_type)) + self.outputs.L1 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e1.' + output_type)) + self.outputs.L2 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e2.' + output_type)) + self.outputs.L3 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_e3.' + output_type)) + self.outputs.exp = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_exp.' + output_type)) + self.outputs.FA = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_fa.' + output_type)) + self.outputs.FA_color = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_fa_color.' + output_type)) + self.outputs.tensor = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_tensor.' + output_type)) + self.outputs.V1 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v1.' + output_type)) + self.outputs.V2 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v2.' + output_type)) + self.outputs.V3 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_v3.' + output_type)) + + class DTITrackerInputSpec(CommandLineInputSpec): tensor_file = File(exists=True, desc="reconstructed tensor file") @@ -163,10 +162,10 @@ def _run_interface(self, runtime): return super(DTITracker, self)._run_interface(runtime) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['track_file'] = os.path.abspath(self.inputs.output_file) + def _post_run(self): + + self.outputs.track_file = os.path.abspath(self.inputs.output_file) if isdefined(self.inputs.output_mask) and self.inputs.output_mask: - outputs['mask_file'] = os.path.abspath(self.inputs.output_mask) + self.outputs.mask_file = os.path.abspath(self.inputs.output_mask) - return outputs + \ No newline at end of file diff --git a/nipype/interfaces/diffusion_toolkit/odf.py b/nipype/interfaces/diffusion_toolkit/odf.py index b2f0b2c6a7..501b67d288 100644 --- a/nipype/interfaces/diffusion_toolkit/odf.py +++ b/nipype/interfaces/diffusion_toolkit/odf.py @@ -85,11 +85,10 @@ def _format_arg(self, name, spec, value): return super(HARDIMat, self)._format_arg("bvecs", spec, new_val) return super(HARDIMat, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs - + def _post_run(self): + + self.outputs.out_file = os.path.abspath(self.inputs.out_file) + class ODFReconInputSpec(CommandLineInputSpec): DWI = File(desc='Input raw data', argstr='%s', exists=True, mandatory=True, position=1) @@ -138,20 +137,19 @@ class ODFRecon(CommandLine): _cmd = 'odf_recon' - def _list_outputs(self): + def _post_run(self): out_prefix = self.inputs.out_prefix output_type = self.inputs.output_type - outputs = self.output_spec().get() - outputs['B0'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_b0.' + output_type)) - outputs['DWI'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_dwi.' + output_type)) - outputs['max'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_max.' + output_type)) - outputs['ODF'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_odf.' + output_type)) + + self.outputs.B0 = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_b0.' + output_type)) + self.outputs.DWI = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_dwi.' + output_type)) + self.outputs.max = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_max.' + output_type)) + self.outputs.ODF = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_odf.' + output_type)) if isdefined(self.inputs.output_entropy): - outputs['entropy'] = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_entropy.' + output_type)) - - return outputs + self.outputs.entropy = os.path.abspath(fname_presuffix("", prefix=out_prefix, suffix='_entropy.' + output_type)) + class ODFTrackerInputSpec(CommandLineInputSpec): max = File(exists=True, mandatory=True) @@ -229,7 +227,7 @@ def _run_interface(self, runtime): return super(ODFTracker, self)._run_interface(runtime) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['track_file'] = os.path.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.track_file = os.path.abspath(self.inputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/diffusion_toolkit/postproc.py b/nipype/interfaces/diffusion_toolkit/postproc.py index 60d5b11115..55af4a4bb4 100644 --- a/nipype/interfaces/diffusion_toolkit/postproc.py +++ b/nipype/interfaces/diffusion_toolkit/postproc.py @@ -50,11 +50,10 @@ class SplineFilter(CommandLine): _cmd = "spline_filter" - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['smoothed_track_file'] = os.path.abspath(self.inputs.output_file) - return outputs - + def _post_run(self): + + self.outputs.smoothed_track_file = os.path.abspath(self.inputs.output_file) + class TrackMergeInputSpec(CommandLineInputSpec): track_files = InputMultiPath(File(exists=True), desc="file containing tracks to be filtered", position=0, argstr="%s...", mandatory=True) @@ -90,7 +89,7 @@ class TrackMerge(CommandLine): _cmd = "track_merge" - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['track_file'] = os.path.abspath(self.inputs.output_file) - return outputs + def _post_run(self): + + self.outputs.track_file = os.path.abspath(self.inputs.output_file) + \ No newline at end of file diff --git a/nipype/interfaces/dipy/preprocess.py b/nipype/interfaces/dipy/preprocess.py index 143f239e6c..cb168458be 100644 --- a/nipype/interfaces/dipy/preprocess.py +++ b/nipype/interfaces/dipy/preprocess.py @@ -66,11 +66,9 @@ def _run_interface(self, runtime): IFLOGGER.info('Resliced image saved as {i}'.format(i=out_file)) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + self.outputs.out_file = op.abspath(self._gen_outfilename()) + def _gen_outfilename(self): fname, fext = op.splitext(op.basename(self.inputs.in_file)) if fext == '.gz': @@ -158,11 +156,9 @@ def _run_interface(self, runtime): 'SNR={s}').format(i=out_file, s=str(s))) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + self.outputs.out_file = op.abspath(self._gen_outfilename()) + def _gen_outfilename(self): fname, fext = op.splitext(op.basename(self.inputs.in_file)) if fext == '.gz': diff --git a/nipype/interfaces/dipy/reconstruction.py b/nipype/interfaces/dipy/reconstruction.py index 6a01eacbf0..75c0e56de4 100644 --- a/nipype/interfaces/dipy/reconstruction.py +++ b/nipype/interfaces/dipy/reconstruction.py @@ -144,12 +144,10 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): for k in outputs.keys(): outputs[k] = self._gen_filename(k) - return outputs - + class EstimateResponseSHInputSpec(DipyBaseInterfaceInputSpec): in_evals = File( @@ -266,12 +264,10 @@ def _run_interface(self, runtime): None).to_filename(op.abspath(self.inputs.out_mask)) return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): outputs['response'] = op.abspath(self.inputs.response) outputs['out_mask'] = op.abspath(self.inputs.out_mask) - return outputs - + class CSDInputSpec(DipyBaseInterfaceInputSpec): in_mask = File(exists=True, desc=('input mask in which compute tensors')) @@ -360,9 +356,8 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): outputs['model'] = self._gen_filename('csdmodel', ext='.pklz') if self.inputs.save_fods: outputs['out_fods'] = self._gen_filename('fods') - return outputs + \ No newline at end of file diff --git a/nipype/interfaces/dipy/simulate.py b/nipype/interfaces/dipy/simulate.py index f4eb174a22..0e26b1d999 100644 --- a/nipype/interfaces/dipy/simulate.py +++ b/nipype/interfaces/dipy/simulate.py @@ -246,15 +246,13 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - outputs['out_mask'] = op.abspath(self.inputs.out_mask) - outputs['out_bvec'] = op.abspath(self.inputs.out_bvec) - outputs['out_bval'] = op.abspath(self.inputs.out_bval) - - return outputs + def _post_run(self): + self.outputs.out_file = op.abspath(self.inputs.out_file) + self.outputs.out_mask = op.abspath(self.inputs.out_mask) + self.outputs.out_bvec = op.abspath(self.inputs.out_bvec) + self.outputs.out_bval = op.abspath(self.inputs.out_bval) + def _compute_voxel(args): """ diff --git a/nipype/interfaces/dipy/tensors.py b/nipype/interfaces/dipy/tensors.py index c7d02c681c..455124e5fa 100644 --- a/nipype/interfaces/dipy/tensors.py +++ b/nipype/interfaces/dipy/tensors.py @@ -62,11 +62,9 @@ def _run_interface(self, runtime): IFLOGGER.info('DTI parameters image saved as {i}'.format(i=out_file)) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self._gen_filename('dti') - return outputs - + def _post_run(self): + self.outputs.out_file = self._gen_filename('dti') + class TensorModeInputSpec(DipyBaseInterfaceInputSpec): mask_file = File(exists=True, @@ -132,7 +130,6 @@ def _run_interface(self, runtime): IFLOGGER.info('Tensor mode image saved as {i}'.format(i=out_file)) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self._gen_filename('mode') - return outputs + def _post_run(self): + self.outputs.out_file = self._gen_filename('mode') + \ No newline at end of file diff --git a/nipype/interfaces/dipy/tests/test_auto_DipyBaseInterface.py b/nipype/interfaces/dipy/tests/test_auto_DipyBaseInterface.py index ce3bd17584..cf1a9f0169 100644 --- a/nipype/interfaces/dipy/tests/test_auto_DipyBaseInterface.py +++ b/nipype/interfaces/dipy/tests/test_auto_DipyBaseInterface.py @@ -14,3 +14,11 @@ def test_DipyBaseInterface_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_DipyBaseInterface_outputs(): + output_map = dict() + outputs = DipyBaseInterface.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/dipy/tests/test_auto_DipyDiffusionInterface.py b/nipype/interfaces/dipy/tests/test_auto_DipyDiffusionInterface.py index e785433355..287b1720e6 100644 --- a/nipype/interfaces/dipy/tests/test_auto_DipyDiffusionInterface.py +++ b/nipype/interfaces/dipy/tests/test_auto_DipyDiffusionInterface.py @@ -23,3 +23,11 @@ def test_DipyDiffusionInterface_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_DipyDiffusionInterface_outputs(): + output_map = dict() + outputs = DipyDiffusionInterface.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/dipy/tracks.py b/nipype/interfaces/dipy/tracks.py index 1b0ad6f381..45e5524be0 100644 --- a/nipype/interfaces/dipy/tracks.py +++ b/nipype/interfaces/dipy/tracks.py @@ -97,11 +97,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self.inputs.out_filename) - return outputs - + def _post_run(self): + self.outputs.out_file = op.abspath(self.inputs.out_filename) + class StreamlineTractographyInputSpec(BaseInterfaceInputSpec): in_file = File(exists=True, mandatory=True, desc=('input diffusion data')) @@ -272,8 +270,7 @@ def _run_interface(self, runtime): trkfilev.to_file(self._gen_filename('tracked', ext='.trk')) return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): outputs['tracks'] = self._gen_filename('tracked', ext='.trk') outputs['gfa'] = self._gen_filename('gfa') if self._save_peaks: @@ -285,8 +282,7 @@ def _list_outputs(self): outputs['out_seeds'] = self._gen_filename('seeds', ext='.txt') - return outputs - + def _gen_filename(self, name, ext=None): fname, fext = op.splitext(op.basename(self.inputs.in_file)) if fext == '.gz': diff --git a/nipype/interfaces/dynamic_slicer.py b/nipype/interfaces/dynamic_slicer.py index 1c26ef4acf..e5d9225d2f 100644 --- a/nipype/interfaces/dynamic_slicer.py +++ b/nipype/interfaces/dynamic_slicer.py @@ -13,6 +13,16 @@ class SlicerCommandLineInputSpec(DynamicTraitedSpec, CommandLineInputSpec): module = traits.Str(desc="name of the Slicer command line module you want to use") + def _format_arg(self, name, spec, value): + if name in [output_node.getElementsByTagName('name')[0].firstChild.nodeValue for output_node in self._outputs_nodes]: + if isinstance(value, bool): + fname = self._gen_filename(name) + else: + fname = value + return spec.argstr % fname + return super(SlicerCommandLineInputSpec, self)._format_arg(name, spec, value) + + class SlicerCommandLine(CommandLine): """Experimental Slicer wrapper. Work in progress. @@ -128,26 +138,17 @@ def _gen_filename_from_param(self, param): ext = {'image': '.nii', 'transform': '.txt', 'file': ''}[param.nodeName] return base + ext - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + for output_node in self._outputs_nodes: name = output_node.getElementsByTagName('name')[0].firstChild.nodeValue - outputs[name] = getattr(self.inputs, name) + setattr(self.outputs, name, getattr(self.inputs, name)) if isdefined(outputs[name]) and isinstance(outputs[name], bool): if outputs[name]: - outputs[name] = self._gen_filename(name) + setattr(self.outputs, name, self._gen_filename(name)) else: - outputs[name] = Undefined - return outputs + setattr(self.outputs, name, Undefined) - def _format_arg(self, name, spec, value): - if name in [output_node.getElementsByTagName('name')[0].firstChild.nodeValue for output_node in self._outputs_nodes]: - if isinstance(value, bool): - fname = self._gen_filename(name) - else: - fname = value - return spec.argstr % fname - return super(SlicerCommandLine, self)._format_arg(name, spec, value) # test = SlicerCommandLine(module="BRAINSFit") # test.inputs.fixedVolume = "/home/filo/workspace/fmri_tumour/data/pilot1/10_co_COR_3D_IR_PREP.nii" diff --git a/nipype/interfaces/elastix/registration.py b/nipype/interfaces/elastix/registration.py index b72123c321..9aa38e4b66 100644 --- a/nipype/interfaces/elastix/registration.py +++ b/nipype/interfaces/elastix/registration.py @@ -65,17 +65,16 @@ class Registration(CommandLine): input_spec = RegistrationInputSpec output_spec = RegistrationOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - + def _post_run(self): + out_dir = op.abspath(self.inputs.output_path) opts = ['WriteResultImage', 'ResultImageFormat'] regex = re.compile(r'^\((\w+)\s(.+)\)$') - outputs['transform'] = [] - outputs['warped_files'] = [] - outputs['warped_files_flags'] = [] + self.outputs.transform = [] + self.outputs.warped_files = [] + self.outputs.warped_files_flags = [] for i, params in enumerate(self.inputs.parameters): config = {} @@ -89,7 +88,7 @@ def _list_outputs(self): value = self._cast(m.group(2).strip()) config[m.group(1).strip()] = value - outputs['transform'].append(op.join(out_dir, + self.outputs.transform.append(op.join(out_dir, 'TransformParameters.%01d.txt' % i)) warped_file = None @@ -97,14 +96,13 @@ def _list_outputs(self): warped_file = op.join(out_dir, 'result.%01d.%s' % (i, config['ResultImageFormat'])) - outputs['warped_files'].append(warped_file) - outputs['warped_files_flags'].append(config['WriteResultImage']) - - if outputs['warped_files_flags'][-1]: - outputs['warped_file'] = outputs['warped_files'][-1] + self.outputs.warped_files.append(warped_file) + self.outputs.warped_files_flags.append(config['WriteResultImage']) - return outputs + if self.outputs.warped_files_flags[-1]: + self.outputs.warped_file = self.outputs.warped_files[-1] + def _cast(self, val): if val.startswith('"') and val.endswith('"'): if val == '"true"': @@ -157,12 +155,10 @@ class ApplyWarp(CommandLine): input_spec = ApplyWarpInputSpec output_spec = ApplyWarpOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): out_dir = op.abspath(self.inputs.output_path) - outputs['warped_file'] = op.join(out_dir, 'result.nii.gz') - return outputs - + self.outputs.warped_file = op.join(out_dir, 'result.nii.gz') + class AnalyzeWarpInputSpec(ElastixBaseInputSpec): transform_file = File(exists=True, mandatory=True, argstr='-tp %s', @@ -197,14 +193,12 @@ class AnalyzeWarp(CommandLine): input_spec = AnalyzeWarpInputSpec output_spec = AnalyzeWarpOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): out_dir = op.abspath(self.inputs.output_path) - outputs['disp_field'] = op.join(out_dir, 'deformationField.nii.gz') - outputs['jacdet_map'] = op.join(out_dir, 'spatialJacobian.nii.gz') - outputs['jacmat_map'] = op.join(out_dir, 'fullSpatialJacobian.nii.gz') - return outputs - + self.outputs.disp_field = op.join(out_dir, 'deformationField.nii.gz') + self.outputs.jacdet_map = op.join(out_dir, 'spatialJacobian.nii.gz') + self.outputs.jacmat_map = op.join(out_dir, 'fullSpatialJacobian.nii.gz') + class PointsWarpInputSpec(ElastixBaseInputSpec): points_file = File(exists=True, argstr='-def %s', mandatory=True, @@ -238,11 +232,10 @@ class PointsWarp(CommandLine): input_spec = PointsWarpInputSpec output_spec = PointsWarpOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): out_dir = op.abspath(self.inputs.output_path) fname, ext = op.splitext(op.basename(self.inputs.points_file)) - outputs['warped_file'] = op.join(out_dir, 'outputpoints%s' % ext) - return outputs + self.outputs.warped_file = op.join(out_dir, 'outputpoints%s' % ext) + \ No newline at end of file diff --git a/nipype/interfaces/elastix/utils.py b/nipype/interfaces/elastix/utils.py index ab034dac07..3573559aad 100644 --- a/nipype/interfaces/elastix/utils.py +++ b/nipype/interfaces/elastix/utils.py @@ -130,11 +130,10 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['output_file'] = getattr(self, '_out_file') - return outputs - + def _post_run(self): + + self.outputs.output_file = getattr(self, '_out_file') + def _get_outfile(self): val = getattr(self, '_out_file') if val is not None and val != '': diff --git a/nipype/interfaces/freesurfer/base.py b/nipype/interfaces/freesurfer/base.py index 54d1bb2c41..4498d88b84 100644 --- a/nipype/interfaces/freesurfer/base.py +++ b/nipype/interfaces/freesurfer/base.py @@ -128,7 +128,7 @@ def run(self, **inputs): def _gen_fname(self, basename, fname=None, cwd=None, suffix='_fs', use_ext=True): - '''Define a generic mapping for a single outfile + """Define a generic mapping for a single outfile The filename is potentially autogenerated by suffixing inputs.infile @@ -142,7 +142,7 @@ def _gen_fname(self, basename, fname=None, cwd=None, suffix='_fs', prefix paths with cwd, otherwise os.getcwd() suffix : string default suffix - ''' + """ if basename == '': msg = 'Unable to generate filename for command %s. ' % self.cmd msg += 'basename is not set!' diff --git a/nipype/interfaces/freesurfer/model.py b/nipype/interfaces/freesurfer/model.py index ed40041249..deb156f96e 100644 --- a/nipype/interfaces/freesurfer/model.py +++ b/nipype/interfaces/freesurfer/model.py @@ -97,19 +97,18 @@ class MRISPreproc(FSCommand): input_spec = MRISPreprocInputSpec output_spec = MRISPreprocOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + outfile = self.inputs.out_file - outputs['out_file'] = outfile + self.outputs.out_file = outfile if not isdefined(outfile): - outputs['out_file'] = os.path.join(os.getcwd(), + self.outputs.out_file = os.path.join(os.getcwd(), 'concat_%s_%s.mgz' % (self.inputs.hemi, self.inputs.target)) - return outputs def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -277,27 +276,27 @@ def _format_arg(self, name, spec, value): return spec.argstr % (_si.subject_id, _si.hemi, _si.surf_geo) return super(GLMFit, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + # Get the top-level output directory if not isdefined(self.inputs.glm_dir): glmdir = os.getcwd() else: glmdir = os.path.abspath(self.inputs.glm_dir) - outputs["glm_dir"] = glmdir + self.outputs.glm_dir = glmdir # Assign the output files that always get created - outputs["beta_file"] = os.path.join(glmdir, "beta.mgh") - outputs["error_var_file"] = os.path.join(glmdir, "rvar.mgh") - outputs["error_stddev_file"] = os.path.join(glmdir, "rstd.mgh") - outputs["mask_file"] = os.path.join(glmdir, "mask.mgh") - outputs["fwhm_file"] = os.path.join(glmdir, "fwhm.dat") - outputs["dof_file"] = os.path.join(glmdir, "dof.dat") + self.outputs.beta_file = os.path.join(glmdir, "beta.mgh") + self.outputs.error_var_file = os.path.join(glmdir, "rvar.mgh") + self.outputs.error_stddev_file = os.path.join(glmdir, "rstd.mgh") + self.outputs.mask_file = os.path.join(glmdir, "mask.mgh") + self.outputs.fwhm_file = os.path.join(glmdir, "fwhm.dat") + self.outputs.dof_file = os.path.join(glmdir, "dof.dat") # Assign the conditional outputs if isdefined(self.inputs.save_residual) and self.inputs.save_residual: - outputs["error_file"] = os.path.join(glmdir, "eres.mgh") + self.outputs.error_file = os.path.join(glmdir, "eres.mgh") if isdefined(self.inputs.save_estimate) and self.inputs.save_estimate: - outputs["estimate_file"] = os.path.join(glmdir, "yhat.mgh") + self.outputs.estimate_file = os.path.join(glmdir, "yhat.mgh") # Get the contrast directory name(s) if isdefined(self.inputs.contrast): @@ -311,20 +310,19 @@ def _list_outputs(self): contrasts = ["osgm"] # Add in the contrast images - outputs["sig_file"] = [os.path.join(glmdir, c, "sig.mgh") for c in contrasts] - outputs["ftest_file"] = [os.path.join(glmdir, c, "F.mgh") for c in contrasts] - outputs["gamma_file"] = [os.path.join(glmdir, c, "gamma.mgh") for c in contrasts] - outputs["gamma_var_file"] = [os.path.join(glmdir, c, "gammavar.mgh") for c in contrasts] + self.outputs.sig_file = [os.path.join(glmdir, c, "sig.mgh") for c in contrasts] + self.outputs.ftest_file = [os.path.join(glmdir, c, "F.mgh") for c in contrasts] + self.outputs.gamma_file = [os.path.join(glmdir, c, "gamma.mgh") for c in contrasts] + self.outputs.gamma_var_file = [os.path.join(glmdir, c, "gammavar.mgh") for c in contrasts] # Add in the PCA results, if relevant if isdefined(self.inputs.pca) and self.inputs.pca: pcadir = os.path.join(glmdir, "pca-eres") - outputs["spatial_eigenvectors"] = os.path.join(pcadir, "v.mgh") - outputs["frame_eigenvectors"] = os.path.join(pcadir, "u.mtx") - outputs["singluar_values"] = os.path.join(pcadir, "sdiag.mat") - outputs["svd_stats_file"] = os.path.join(pcadir, "stats.dat") + self.outputs.spatial_eigenvectors = os.path.join(pcadir, "v.mgh") + self.outputs.frame_eigenvectors = os.path.join(pcadir, "u.mtx") + self.outputs.singluar_values = os.path.join(pcadir, "sdiag.mat") + self.outputs.svd_stats_file = os.path.join(pcadir, "stats.dat") - return outputs def _gen_filename(self, name): if name == 'glm_dir': @@ -357,9 +355,10 @@ class BinarizeInputSpec(FSTraitedSpec): ventricles = traits.Bool(argstr='--ventricles', desc='set match vals those for aseg ventricles+choroid (not 4th)') wm_ven_csf = traits.Bool(argstr='--wm+vcsf', xor=['min', 'max'], - desc='WM and ventricular CSF, including choroid (not 4th)') - binary_file = File(argstr='--o %s', genfile=True, - desc='binary output volume') + desc='WM and ventricular CSF, including choroid (not 4th)') + binary_file = File( + argstr='--o %s', name_source='in_file', name_template='_bin', + keep_extension=True, desc='binary output volume') out_type = traits.Enum('nii', 'nii.gz', 'mgz', argstr='', desc='output file type') count_file = traits.Either(traits.Bool, File, @@ -406,9 +405,9 @@ class Binarize(FSCommand): Examples -------- - >>> binvol = Binarize(in_file='structural.nii', min=10, binary_file='foo_out.nii') + >>> binvol = Binarize(in_file='structural.nii', min=10) >>> binvol.cmdline - 'mri_binarize --o foo_out.nii --i structural.nii --min 10.000000' + 'mri_binarize --o structural_bin.nii --i structural.nii --min 10.000000' """ @@ -416,8 +415,8 @@ class Binarize(FSCommand): input_spec = BinarizeInputSpec output_spec = BinarizeOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + outfile = self.inputs.binary_file if not isdefined(outfile): if isdefined(self.inputs.out_type): @@ -430,23 +429,22 @@ def _list_outputs(self): outfile = fname_presuffix(self.inputs.in_file, newpath=os.getcwd(), suffix='_thresh') - outputs['binary_file'] = os.path.abspath(outfile) + self.outputs.binary_file = os.path.abspath(outfile) value = self.inputs.count_file if isdefined(value): if isinstance(value, bool): if value: - outputs['count_file'] = fname_presuffix(self.inputs.in_file, + self.outputs.count_file = fname_presuffix(self.inputs.in_file, suffix='_count.txt', newpath=os.getcwd(), use_ext=False) else: - outputs['count_file'] = value - return outputs + self.outputs.count_file = value def _format_arg(self, name, spec, value): if name == 'count_file': if isinstance(value, bool): - fname = self._list_outputs()[name] + fname = getattr(self.outputs, name) else: fname = value return spec.argstr % fname @@ -456,7 +454,7 @@ def _format_arg(self, name, spec, value): def _gen_filename(self, name): if name == 'binary_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -525,18 +523,17 @@ class Concatenate(FSCommand): input_spec = ConcatenateInputSpec output_spec = ConcatenateOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if not isdefined(self.inputs.concatenated_file): - outputs['concatenated_file'] = os.path.join(os.getcwd(), + self.outputs.concatenated_file = os.path.join(os.getcwd(), 'concat_output.nii.gz') else: - outputs['concatenated_file'] = self.inputs.concatenated_file - return outputs + self.outputs.concatenated_file = self.inputs.concatenated_file def _gen_filename(self, name): if name == 'concatenated_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -633,12 +630,12 @@ class SegStats(FSCommand): input_spec = SegStatsInputSpec output_spec = SegStatsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.summary_file): - outputs['summary_file'] = os.path.abspath(self.inputs.summary_file) + self.outputs.summary_file = os.path.abspath(self.inputs.summary_file) else: - outputs['summary_file'] = os.path.join(os.getcwd(), 'summary.stats') + self.outputs.summary_file = os.path.join(os.getcwd(), 'summary.stats') suffices = dict(avgwf_txt_file='_avgwf.txt', avgwf_file='_avgwf.nii.gz', sf_avg_file='sfavg.txt') if isdefined(self.inputs.segmentation_file): @@ -651,17 +648,16 @@ def _list_outputs(self): value = getattr(self.inputs, name) if isdefined(value): if isinstance(value, bool): - outputs[name] = fname_presuffix(src, suffix=suffix, + setattr(self.outputs, name, fname_presuffix(src, suffix=suffix, newpath=os.getcwd(), - use_ext=False) + use_ext=False)) else: - outputs[name] = os.path.abspath(value) - return outputs + setattr(self.outputs, name, os.path.abspath(value)) def _format_arg(self, name, spec, value): if name in ['avgwf_txt_file', 'avgwf_file', 'sf_avg_file']: if isinstance(value, bool): - fname = self._list_outputs()[name] + fname = getattr(self.outputs, name) else: fname = value return spec.argstr % fname @@ -669,7 +665,7 @@ def _format_arg(self, name, spec, value): def _gen_filename(self, name): if name == 'summary_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -752,8 +748,8 @@ class Label2Vol(FSCommand): input_spec = Label2VolInputSpec output_spec = Label2VolOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + outfile = self.inputs.vol_label_file if not isdefined(outfile): for key in ['label_file', 'annot_file', 'seg_file']: @@ -767,12 +763,11 @@ def _list_outputs(self): outfile = fname_presuffix(src, suffix='_vol.nii.gz', newpath=os.getcwd(), use_ext=False) - outputs['vol_label_file'] = outfile - return outputs + self.outputs.vol_label_file = outfile def _gen_filename(self, name): if name == 'vol_label_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -830,15 +825,13 @@ class MS_LDA(FSCommand): input_spec = MS_LDAInputSpec output_spec = MS_LDAOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if isdefined(self.inputs.output_synth): - outputs['vol_synth_file'] = os.path.abspath(self.inputs.output_synth) + self.outputs.vol_synth_file = os.path.abspath(self.inputs.output_synth) else: - outputs['vol_synth_file'] = os.path.abspath(self.inputs.vol_synth_file) + self.outputs.vol_synth_file = os.path.abspath(self.inputs.vol_synth_file) if not isdefined(self.inputs.use_weights) or self.inputs.use_weights is False: - outputs['weight_file'] = os.path.abspath(self.inputs.weight_file) - return outputs + self.outputs.weight_file = os.path.abspath(self.inputs.weight_file) def _verify_weights_file_exists(self): if not os.path.exists(os.path.abspath(self.inputs.weight_file)): diff --git a/nipype/interfaces/freesurfer/preprocess.py b/nipype/interfaces/freesurfer/preprocess.py index 4eeb049be3..60b9f167aa 100644 --- a/nipype/interfaces/freesurfer/preprocess.py +++ b/nipype/interfaces/freesurfer/preprocess.py @@ -66,11 +66,10 @@ class ParseDICOMDir(FSCommand): input_spec = ParseDICOMDirInputSpec output_spec = ParseDICOMDirOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.dicom_info_file): - outputs['dicom_info_file'] = os.path.join(os.getcwd(), self.inputs.dicom_info_file) - return outputs + self.outputs.dicom_info_file = os.path.join(os.getcwd(), self.inputs.dicom_info_file) class UnpackSDICOMDirInputSpec(FSTraitedSpec): @@ -374,8 +373,8 @@ def _get_outfilename(self): use_ext=False) return os.path.abspath(outfile) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + outfile = self._get_outfilename() if isdefined(self.inputs.split) and self.inputs.split: size = load(self.inputs.in_file).shape @@ -411,8 +410,7 @@ def _list_outputs(self): outfiles.append(fname_presuffix(outfile, suffix='%03d' % (i + 1))) outfile = outfiles - outputs['out_file'] = outfile - return outputs + self.outputs.out_file = outfile def _gen_filename(self, name): if name == 'out_file': @@ -585,10 +583,9 @@ def _get_outfilename(self): suffix='_resample') return outfile - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['resampled_file'] = self._get_outfilename() - return outputs + def _post_run(self): + + self.outputs.resampled_file = self._get_outfilename() def _gen_filename(self, name): if name == 'resampled_file': @@ -736,7 +733,7 @@ def _gen_filename(self, name): return self._gen_subjects_dir() return None - def _list_outputs(self): + def _post_run(self): """ See io.FreeSurferSource.outputs for the list of outputs returned """ @@ -750,14 +747,12 @@ def _list_outputs(self): else: hemi = 'both' - outputs = self._outputs().get() outputs.update(FreeSurferSource(subject_id=self.inputs.subject_id, subjects_dir=subjects_dir, hemi=hemi)._list_outputs()) - outputs['subject_id'] = self.inputs.subject_id - outputs['subjects_dir'] = subjects_dir - return outputs + self.outputs.subject_id = self.inputs.subject_id + self.outputs.subjects_dir = subjects_dir def _is_resuming(self): subjects_dir = self.inputs.subjects_dir @@ -868,25 +863,25 @@ class BBRegister(FSCommand): input_spec = BBRegisterInputSpec output_spec = BBRegisterOutputSpec - def _list_outputs(self): + def _post_run(self): + - outputs = self.output_spec().get() _in = self.inputs if isdefined(_in.out_reg_file): - outputs['out_reg_file'] = op.abspath(_in.out_reg_file) + self.outputs.out_reg_file = op.abspath(_in.out_reg_file) elif _in.source_file: suffix = '_bbreg_%s.dat' % _in.subject_id - outputs['out_reg_file'] = fname_presuffix(_in.source_file, + self.outputs.out_reg_file = fname_presuffix(_in.source_file, suffix=suffix, use_ext=False) if isdefined(_in.registered_file): if isinstance(_in.registered_file, bool): - outputs['registered_file'] = fname_presuffix(_in.source_file, + self.outputs.registered_file = fname_presuffix(_in.source_file, suffix='_bbreg') else: - outputs['registered_file'] = op.abspath(_in.registered_file) + self.outputs.registered_file = op.abspath(_in.registered_file) if isdefined(_in.out_fsl_file): if isinstance(_in.out_fsl_file, bool): @@ -894,18 +889,17 @@ def _list_outputs(self): out_fsl_file = fname_presuffix(_in.source_file, suffix=suffix, use_ext=False) - outputs['out_fsl_file'] = out_fsl_file + self.outputs.out_fsl_file = out_fsl_file else: - outputs['out_fsl_file'] = op.abspath(_in.out_fsl_file) + self.outputs.out_fsl_file = op.abspath(_in.out_fsl_file) - outputs['min_cost_file'] = outputs['out_reg_file'] + '.mincost' - return outputs + self.outputs.min_cost_file = self.outputs.out_reg_file + '.mincost' def _format_arg(self, name, spec, value): if name in ['registered_file', 'out_fsl_file']: if isinstance(value, bool): - fname = self._list_outputs()[name] + fname = getattr(self.outputs, name) else: fname = value return spec.argstr % fname @@ -914,7 +908,7 @@ def _format_arg(self, name, spec, value): def _gen_filename(self, name): if name == 'out_reg_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -1017,10 +1011,9 @@ def _get_outfile(self): suffix='_warped') return outfile - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['transformed_file'] = os.path.abspath(self._get_outfile()) - return outputs + def _post_run(self): + + self.outputs.transformed_file = os.path.abspath(self._get_outfile()) def _gen_filename(self, name): if name == 'transformed_file': @@ -1082,18 +1075,17 @@ class Smooth(FSCommand): input_spec = SmoothInputSpec output_spec = SmoothOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + outfile = self.inputs.smoothed_file if not isdefined(outfile): outfile = self._gen_fname(self.inputs.in_file, suffix='_smooth') - outputs['smoothed_file'] = outfile - return outputs + self.outputs.smoothed_file = outfile def _gen_filename(self, name): if name == 'smoothed_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -1197,17 +1189,17 @@ def _format_arg(self, name, spec, value): "half_weights", "half_source_xfm", "half_targ_xfm"]: if name == option: if isinstance(value, bool): - fname = self._list_outputs()[name] + fname = getattr(self.outputs, name) else: fname = value return spec.argstr % fname return super(RobustRegister, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_reg_file'] = self.inputs.out_reg_file + def _post_run(self): + + self.outputs.out_reg_file = self.inputs.out_reg_file if not isdefined(self.inputs.out_reg_file) and self.inputs.source_file: - outputs['out_reg_file'] = fname_presuffix(self.inputs.source_file, + self.outputs.out_reg_file = fname_presuffix(self.inputs.source_file, suffix='_robustreg.lta', use_ext=False) prefices = dict(src=self.inputs.source_file, trg=self.inputs.target_file) suffices = dict(registered_file=("src", "_robustreg", True), @@ -1221,17 +1213,16 @@ def _list_outputs(self): value = getattr(self.inputs, name) if isdefined(value): if isinstance(value, bool): - outputs[name] = fname_presuffix(prefices[sufftup[0]], + setattr(self.outputs, name, fname_presuffix(prefices[sufftup[0]], suffix=sufftup[1], newpath=os.getcwd(), - use_ext=sufftup[2]) + use_ext=sufftup[2])) else: - outputs[name] = value - return outputs + setattr(self.outputs, name, value) def _gen_filename(self, name): if name == 'out_reg_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -1288,16 +1279,15 @@ def _format_arg(self, name, spec, value): return cmd return super(FitMSParams, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if not isdefined(self.inputs.out_dir): out_dir = self._gen_filename("out_dir") else: out_dir = self.inputs.out_dir - outputs["t1_image"] = os.path.join(out_dir, "T1.mgz") - outputs["pd_image"] = os.path.join(out_dir, "PD.mgz") - outputs["t2star_image"] = os.path.join(out_dir, "T2star.mgz") - return outputs + self.outputs.t1_image = os.path.join(out_dir, "T1.mgz") + self.outputs.pd_image = os.path.join(out_dir, "PD.mgz") + self.outputs.t2star_image = os.path.join(out_dir, "T2star.mgz") def _gen_filename(self, name): if name == "out_dir": @@ -1345,16 +1335,15 @@ class SynthesizeFLASH(FSCommand): input_spec = SynthesizeFLASHInputSpec output_spec = SynthesizeFLASHOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.out_file): - outputs["out_file"] = self.inputs.out_file + self.outputs.out_file = self.inputs.out_file else: - outputs["out_file"] = self._gen_fname("synth-flash_%02d.mgz" % self.inputs.flip_angle, + self.outputs.out_file = self._gen_fname("synth-flash_%02d.mgz" % self.inputs.flip_angle, suffix="") - return outputs def _gen_filename(self, name): if name == "out_file": - return self._list_outputs()["out_file"] + return self.outputs.out_file return None diff --git a/nipype/interfaces/freesurfer/tests/test_auto_Binarize.py b/nipype/interfaces/freesurfer/tests/test_auto_Binarize.py index b22d6a98e6..6fd29cf435 100644 --- a/nipype/interfaces/freesurfer/tests/test_auto_Binarize.py +++ b/nipype/interfaces/freesurfer/tests/test_auto_Binarize.py @@ -15,7 +15,9 @@ def test_Binarize_inputs(): bin_val_not=dict(argstr='--binvalnot %d', ), binary_file=dict(argstr='--o %s', - genfile=True, + keep_extension=True, + name_source='in_file', + name_template='_bin', ), count_file=dict(argstr='--count %s', ), diff --git a/nipype/interfaces/freesurfer/tests/test_auto_DICOMConvert.py b/nipype/interfaces/freesurfer/tests/test_auto_DICOMConvert.py index 1551f3e44c..102c4e6dd9 100644 --- a/nipype/interfaces/freesurfer/tests/test_auto_DICOMConvert.py +++ b/nipype/interfaces/freesurfer/tests/test_auto_DICOMConvert.py @@ -37,3 +37,11 @@ def test_DICOMConvert_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_DICOMConvert_outputs(): + output_map = dict() + outputs = DICOMConvert.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/freesurfer/tests/test_auto_FSCommand.py b/nipype/interfaces/freesurfer/tests/test_auto_FSCommand.py index f463310c33..77f1b3b5ad 100644 --- a/nipype/interfaces/freesurfer/tests/test_auto_FSCommand.py +++ b/nipype/interfaces/freesurfer/tests/test_auto_FSCommand.py @@ -22,3 +22,11 @@ def test_FSCommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_FSCommand_outputs(): + output_map = dict() + outputs = FSCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/freesurfer/tests/test_auto_MRIsConvert.py b/nipype/interfaces/freesurfer/tests/test_auto_MRIsConvert.py index 86c949f645..c7ac846849 100644 --- a/nipype/interfaces/freesurfer/tests/test_auto_MRIsConvert.py +++ b/nipype/interfaces/freesurfer/tests/test_auto_MRIsConvert.py @@ -30,10 +30,12 @@ def test_MRIsConvert_inputs(): ), origname=dict(argstr='-o %s', ), - out_datatype=dict(xor=['out_file'], + out_datatype=dict(mandatory=True, + xor=['out_file'], ), out_file=dict(argstr='%s', genfile=True, + mandatory=True, position=-1, xor=['out_datatype'], ), diff --git a/nipype/interfaces/freesurfer/tests/test_auto_UnpackSDICOMDir.py b/nipype/interfaces/freesurfer/tests/test_auto_UnpackSDICOMDir.py index 40e1c65378..366af91387 100644 --- a/nipype/interfaces/freesurfer/tests/test_auto_UnpackSDICOMDir.py +++ b/nipype/interfaces/freesurfer/tests/test_auto_UnpackSDICOMDir.py @@ -51,3 +51,11 @@ def test_UnpackSDICOMDir_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_UnpackSDICOMDir_outputs(): + output_map = dict() + outputs = UnpackSDICOMDir.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/freesurfer/utils.py b/nipype/interfaces/freesurfer/utils.py index 49ae5f72e8..1d94211e0e 100644 --- a/nipype/interfaces/freesurfer/utils.py +++ b/nipype/interfaces/freesurfer/utils.py @@ -48,9 +48,8 @@ class SampleToSurfaceInputSpec(FSTraitedSpec): reg_header = traits.Bool(argstr="--regheader %s", requires=["subject_id"], mandatory=True, xor=reg_xors, desc="register based on header geometry") - mni152reg = traits.Bool(argstr="--mni152reg", - mandatory=True, xor=reg_xors, - desc="source volume is in MNI152 space") + mni152reg = traits.Bool(False, argstr="--mni152reg", mandatory=True, + xor=reg_xors, desc="source volume is in MNI152 space") apply_rot = traits.Tuple(traits.Float, traits.Float, traits.Float, argstr="--rot %.3f %.3f %.3f", @@ -199,12 +198,11 @@ def _get_outfilename(self, opt="out_file"): use_ext=False) return outfile - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = os.path.abspath(self._get_outfilename()) + def _post_run(self): + self.outputs.out_file = os.path.abspath(self._get_outfilename()) hitsfile = self.inputs.hits_file if isdefined(hitsfile): - outputs["hits_file"] = hitsfile + self.outputs.hits_file = hitsfile if isinstance(hitsfile, bool): hitsfile = self._get_outfilename("hits_file") voxfile = self.inputs.vox_file @@ -215,12 +213,11 @@ def _list_outputs(self): prefix=self.inputs.hemi + ".", suffix="_vox.txt", use_ext=False) - outputs["vox_file"] = voxfile - return outputs + self.outputs.vox_file = voxfile def _gen_filename(self, name): if name == "out_file": - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -274,23 +271,21 @@ class SurfaceSmooth(FSCommand): input_spec = SurfaceSmoothInputSpec output_spec = SurfaceSmoothOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.inputs.out_file - if not isdefined(outputs["out_file"]): + def _post_run(self): + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): in_file = self.inputs.in_file if isdefined(self.inputs.fwhm): kernel = self.inputs.fwhm else: kernel = self.inputs.smooth_iters - outputs["out_file"] = fname_presuffix(in_file, + self.outputs.out_file = fname_presuffix(in_file, suffix="_smooth%d" % kernel, newpath=os.getcwd()) - return outputs def _gen_filename(self, name): if name == "out_file": - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -352,10 +347,9 @@ class SurfaceTransform(FSCommand): input_spec = SurfaceTransformInputSpec output_spec = SurfaceTransformOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.inputs.out_file - if not isdefined(outputs["out_file"]): + def _post_run(self): + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): if isdefined(self.inputs.source_file): source = self.inputs.source_file else: @@ -374,17 +368,16 @@ def _list_outputs(self): if isdefined(self.inputs.target_type): ext = "." + filemap[self.inputs.target_type] use_ext = False - outputs["out_file"] = fname_presuffix(source, + self.outputs.out_file = fname_presuffix(source, suffix=".%s%s" % (self.inputs.target_subject, ext), newpath=os.getcwd(), use_ext=use_ext) else: - outputs["out_file"] = os.path.abspath(self.inputs.out_file) - return outputs + self.outputs.out_file = os.path.abspath(self.inputs.out_file) def _gen_filename(self, name): if name == "out_file": - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -486,21 +479,19 @@ class ApplyMask(FSCommand): input_spec = ApplyMaskInputSpec output_spec = ApplyMaskOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.inputs.out_file - if not isdefined(outputs["out_file"]): - outputs["out_file"] = fname_presuffix(self.inputs.in_file, + def _post_run(self): + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): + self.outputs.out_file = fname_presuffix(self.inputs.in_file, suffix="_masked", newpath=os.getcwd(), use_ext=True) else: - outputs["out_file"] = os.path.abspath(outputs["out_file"]) - return outputs + self.outputs.out_file = os.path.abspath(self.outputs.out_file) def _gen_filename(self, name): if name == "out_file": - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -692,8 +683,7 @@ def _write_tcl_script(self): fid.write("\n".join(script)) fid.close() - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if not isdefined(self.inputs.screenshot_stem): stem = "%s_%s_%s" % (self.inputs.subject_id, self.inputs.hemi, self.inputs.surface) else: @@ -706,8 +696,7 @@ def _list_outputs(self): if self.inputs.six_images: snapshots.extend(["%s-pos.tif", "%s-ant.tif"]) snapshots = [self._gen_fname(f % stem, suffix="") for f in snapshots] - outputs["snapshots"] = snapshots - return outputs + self.outputs.snapshots = snapshots def _gen_filename(self, name): if name == "tcl_script": @@ -776,7 +765,6 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): outputs.file_format = ftype outputs.data_type = dtype - return outputs class MRIsConvertInputSpec(FSTraitedSpec): @@ -852,11 +840,10 @@ def _format_arg(self, name, spec, value): if name == "out_file" and not os.path.isabs(value): value = os.path.abspath(value) return super(MRIsConvert, self)._format_arg(name, spec, value) - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["converted"] = os.path.abspath(self._gen_outfilename()) - return outputs + + def _post_run(self): + + self.outputs.converted = os.path.abspath(self._gen_outfilename()) def _gen_filename(self, name): if name is 'out_file': @@ -921,10 +908,9 @@ class MRITessellate(FSCommand): input_spec = MRITessellateInputSpec output_spec = MRITessellateOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['surface'] = os.path.abspath(self._gen_outfilename()) - return outputs + def _post_run(self): + + self.outputs.surface = os.path.abspath(self._gen_outfilename()) def _gen_filename(self, name): if name is 'out_file': @@ -991,10 +977,9 @@ class MRIPretess(FSCommand): input_spec = MRIPretessInputSpec output_spec = MRIPretessOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = os.path.abspath(self._gen_outfilename()) - return outputs + def _post_run(self): + + self.outputs.out_file = os.path.abspath(self._gen_outfilename()) def _gen_filename(self, name): if name is 'out_file': @@ -1048,10 +1033,9 @@ class MRIMarchingCubes(FSCommand): input_spec = MRIMarchingCubesInputSpec output_spec = MRIMarchingCubesOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['surface'] = self._gen_outfilename() - return outputs + def _post_run(self): + + self.outputs.surface = self._gen_outfilename() def _gen_filename(self, name): if name is 'out_file': @@ -1119,10 +1103,9 @@ class SmoothTessellation(FSCommand): input_spec = SmoothTessellationInputSpec output_spec = SmoothTessellationOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['surface'] = self._gen_outfilename() - return outputs + def _post_run(self): + + self.outputs.surface = self._gen_outfilename() def _gen_filename(self, name): if name is 'out_file': @@ -1176,10 +1159,9 @@ class MakeAverageSubject(FSCommand): input_spec = MakeAverageSubjectInputSpec output_spec = MakeAverageSubjectOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['average_subject_name'] = self.inputs.out_name - return outputs + def _post_run(self): + + self.outputs.average_subject_name = self.inputs.out_name class ExtractMainComponentInputSpec(CommandLineInputSpec): @@ -1286,12 +1268,10 @@ class Tkregister2(FSCommand): input_spec = Tkregister2InputSpec output_spec = Tkregister2OutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs['reg_file'] = os.path.abspath(self.inputs.reg_file) + def _post_run(self): + self.outputs.reg_file = os.path.abspath(self.inputs.reg_file) if isdefined(self.inputs.fsl_out): - outputs['fsl_file'] = os.path.abspath(self.inputs.fsl_out) - return outputs + self.outputs.fsl_file = os.path.abspath(self.inputs.fsl_out) def _gen_outfilename(self): if isdefined(self.inputs.out_file): diff --git a/nipype/interfaces/fsl/base.py b/nipype/interfaces/fsl/base.py index b4fedddc7d..59281cc081 100644 --- a/nipype/interfaces/fsl/base.py +++ b/nipype/interfaces/fsl/base.py @@ -25,19 +25,22 @@ """ -from builtins import object - -from glob import glob import os -import warnings +from glob import glob +from builtins import object +from ..base import traits, CommandLine, CommandLineInputSpec +from ... import logging -from ...utils.filemanip import fname_presuffix, split_filename, copyfile -from ..base import (traits, isdefined, - CommandLine, CommandLineInputSpec, TraitedSpec, - File, Directory, InputMultiPath, OutputMultiPath) +IFLOGGER = logging.getLogger('interface') -warn = warnings.warn +FSLDIR = os.getenv('FSLDIR') +if FSLDIR is None: + IFLOGGER.warn('FSLDIR environment variable is not set') +FSLOUTPUTTYPE = os.getenv('FSLOUTPUTTYPE') +if FSLOUTPUTTYPE is None: + IFLOGGER.warn('FSLOUTPUTTYPE environment variable is not set, using NIFTI') + FSLOUTPUTTYPE = 'NIFTI' class Info(object): """Handle fsl output type and version information. @@ -70,60 +73,19 @@ def version(): """ # find which fsl being used....and get version from # /path/to/fsl/etc/fslversion - try: - basedir = os.environ['FSLDIR'] - except KeyError: - return None - out = open('%s/etc/fslversion' % (basedir)).read() - return out.strip('\n') - - @classmethod - def output_type_to_ext(cls, output_type): - """Get the file extension for the given output type. - - Parameters - ---------- - output_type : {'NIFTI', 'NIFTI_GZ', 'NIFTI_PAIR', 'NIFTI_PAIR_GZ'} - String specifying the output type. - - Returns - ------- - extension : str - The file extension for the output type. - """ - - try: - return cls.ftypes[output_type] - except KeyError: - msg = 'Invalid FSLOUTPUTTYPE: ', output_type - raise KeyError(msg) - - @classmethod - def output_type(cls): - """Get the global FSL output file type FSLOUTPUTTYPE. - - This returns the value of the environment variable - FSLOUTPUTTYPE. An exception is raised if it is not defined. - - Returns - ------- - fsl_ftype : string - Represents the current environment setting of FSLOUTPUTTYPE - """ - try: - return os.environ['FSLOUTPUTTYPE'] - except KeyError: - warnings.warn(('FSL environment variables not set. setting output ' - 'type to NIFTI')) - return 'NIFTI' + out = None + if FSLDIR is not None: + with open('%s/etc/fslversion' % FSLDIR, 'r') as vfile: + out = vfile.read().strip('\n') + return out @staticmethod def standard_image(img_name=None): - '''Grab an image from the standard location. + """Grab an image from the standard location. Returns a list of standard images if called without arguments. - Could be made more fancy to allow for more relocatability''' + Could be made more fancy to allow for more relocatability""" try: fsldir = os.environ['FSLDIR'] except KeyError: @@ -142,126 +104,46 @@ class FSLCommandInputSpec(CommandLineInputSpec): All command support specifying FSLOUTPUTTYPE dynamically via output_type. - Example - ------- - fsl.ExtractRoi(tmin=42, tsize=1, output_type='NIFTI') - """ - output_type = traits.Enum('NIFTI', list(Info.ftypes.keys()), - desc='FSL output type') + Example:: + fsl.ExtractRoi(tmin=42, tsize=1, output_type='NIFTI') -class FSLCommand(CommandLine): - """Base support for FSL commands. """ + output_type = traits.Trait(FSLOUTPUTTYPE, Info.ftypes, usedefault=True, + desc='FSL output type') + + +class FSLCommand(CommandLine): # pylint: disable=W0223 + """Base support for FSL commands.""" input_spec = FSLCommandInputSpec - _output_type = None def __init__(self, **inputs): super(FSLCommand, self).__init__(**inputs) self.inputs.on_trait_change(self._output_update, 'output_type') - - if self._output_type is None: - self._output_type = Info.output_type() - - if not isdefined(self.inputs.output_type): - self.inputs.output_type = self._output_type - else: - self._output_update() + self.inputs.output_type = FSLOUTPUTTYPE def _output_update(self): - self._output_type = self.inputs.output_type self.inputs.environ.update({'FSLOUTPUTTYPE': self.inputs.output_type}) - @classmethod - def set_default_output_type(cls, output_type): - """Set the default output type for FSL classes. - - This method is used to set the default output type for all fSL - subclasses. However, setting this will not update the output - type for any existing instances. For these, assign the - .inputs.output_type. - """ - - if output_type in Info.ftypes: - cls._output_type = output_type - else: - raise AttributeError('Invalid FSL output_type: %s' % output_type) - @property def version(self): return Info.version() - def _gen_fname(self, basename, cwd=None, suffix=None, change_ext=True, - ext=None): - """Generate a filename based on the given parameters. - - The filename will take the form: cwd/basename. - If change_ext is True, it will use the extentions specified in - intputs.output_type. - - Parameters - ---------- - basename : str - Filename to base the new filename on. - cwd : str - Path to prefix to the new filename. (default is os.getcwd()) - suffix : str - Suffix to add to the `basename`. (defaults is '' ) - change_ext : bool - Flag to change the filename extension to the FSL output type. - (default True) - - Returns - ------- - fname : str - New filename based on given parameters. - - """ - - if basename == '': - msg = 'Unable to generate filename for command %s. ' % self.cmd - msg += 'basename is not set!' - raise ValueError(msg) - if cwd is None: - cwd = os.getcwd() - if ext is None: - ext = Info.output_type_to_ext(self.inputs.output_type) - if change_ext: - if suffix: - suffix = ''.join((suffix, ext)) - else: - suffix = ext - if suffix is None: - suffix = '' - fname = fname_presuffix(basename, suffix=suffix, - use_ext=False, newpath=cwd) - return fname - - def _overload_extension(self, value, name=None): - return value + Info.output_type_to_ext(self.inputs.output_type) - - -def check_fsl(): - ver = Info.version() - if ver: - return 0 - else: - return 1 - def no_fsl(): """Checks if FSL is NOT installed used with skipif to skip tests that will fail if FSL is not installed""" + return Info.version() is None - if Info.version() is None: - return True - else: - return False - +def check_fsl(): + """Same as the previous. One of these should disappear """ + return Info.version() is not None def no_fsl_course_data(): """check if fsl_course data is present""" - return not ('FSL_COURSE_DATA' in os.environ and os.path.isdir(os.path.abspath(os.environ['FSL_COURSE_DATA']))) + if os.getenv('FSL_COURSE_DATA') is None: + return False + return os.path.isdir(os.path.abspath(os.getenv('FSL_COURSE_DATA'))) diff --git a/nipype/interfaces/fsl/dti.py b/nipype/interfaces/fsl/dti.py index 84e44e4f7a..26031122d9 100644 --- a/nipype/interfaces/fsl/dti.py +++ b/nipype/interfaces/fsl/dti.py @@ -6,8 +6,8 @@ Change directory to provide relative paths for doctests >>> import os - >>> filepath = os.path.dirname( os.path.realpath( __file__ ) ) - >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data')) + >>> filepath = op.dirname( op.realpath( __file__ ) ) + >>> datadir = op.realpath(op.join(filepath, '../../testing/data')) >>> os.chdir(datadir) """ @@ -15,22 +15,23 @@ from builtins import range import os +import os.path as op import shutil -import warnings from ... import LooseVersion -from ..base import (TraitedSpec, isdefined, File, Directory, - InputMultiPath, OutputMultiPath, traits) +from ..base import (TraitedSpec, isdefined, File, GenFile, GenMultiFile, Directory, + InputMultiPath, OutputMultiPath, traits, Undefined) from ..fsl.base import (FSLCommand, FSLCommandInputSpec, Info) from ...utils.filemanip import fname_presuffix, split_filename, copyfile -warn = warnings.warn +from ... import logging +IFLOGGER = logging.getLogger('interface') class DTIFitInputSpec(FSLCommandInputSpec): dwi = File(exists=True, desc='diffusion weighted image data file', argstr='-k %s', position=0, mandatory=True) - base_name = traits.Str("dtifit_", desc='base_name that all output files will start with', + base_name = traits.Str('dtifit_', desc='base_name that all output files will start with', argstr='-o %s', position=1, usedefault=True) mask = File(exists=True, desc='bet binary mask file', argstr='-m %s', position=2, mandatory=True) @@ -44,7 +45,7 @@ class DTIFitInputSpec(FSLCommandInputSpec): max_y = traits.Int(argstr='-Y %d', desc='max y') min_x = traits.Int(argstr='-x %d', desc='min x') max_x = traits.Int(argstr='-X %d', desc='max x') - save_tensor = traits.Bool(desc='save the elements of the tensor', + save_tensor = traits.Bool(False, usedefault=True, desc='save the elements of the tensor', argstr='--save_tensor') sse = traits.Bool(desc='output sum of squared errors', argstr='--sse') cni = File(exists=True, desc='input counfound regressors', argstr='--cni=%s') @@ -53,20 +54,43 @@ class DTIFitInputSpec(FSLCommandInputSpec): gradnonlin = File(exists=True, argstr='--gradnonlin=%s', desc='gradient non linearities') + # Auto-generated outputs + out_v1 = GenFile(template='{base_name}V1{output_type_}', + keep_extension=False, desc='1st eigenvector') + out_v2 = GenFile(template='{base_name}V2{output_type_}', + keep_extension=False, desc='2nd eigenvector') + out_v3 = GenFile(template='{base_name}V3{output_type_}', + keep_extension=False, desc='3rd eigenvector') + out_l1 = GenFile(template='{base_name}L1{output_type_}', + keep_extension=False, desc='1st eigenvalue') + out_l2 = GenFile(template='{base_name}L2{output_type_}', + keep_extension=False, desc='2nd eigenvalue') + out_l3 = GenFile(template='{base_name}L3{output_type_}', + keep_extension=False, desc='3rd eigenvalue') + out_md = GenFile(template='{base_name}MD{output_type_}', + keep_extension=False, desc='mean diffusivity') + out_fa = GenFile(template='{base_name}FA{output_type_}', + keep_extension=False, desc='fractional anisotropy') + out_mo = GenFile(template='{base_name}MO{output_type_}', + keep_extension=False, desc='mode of anisotropy') + out_s0 = GenFile(template='{base_name}S0{output_type_}', + keep_extension=False, desc='raw T2 signal with no diffusion weighting') + tensor = GenFile(template='{base_name}tensor{output_type_}', + keep_extension=False, desc='path/name of file with the 4D tensor volume') + class DTIFitOutputSpec(TraitedSpec): - V1 = File(exists=True, desc='path/name of file with the 1st eigenvector') - V2 = File(exists=True, desc='path/name of file with the 2nd eigenvector') - V3 = File(exists=True, desc='path/name of file with the 3rd eigenvector') - L1 = File(exists=True, desc='path/name of file with the 1st eigenvalue') - L2 = File(exists=True, desc='path/name of file with the 2nd eigenvalue') - L3 = File(exists=True, desc='path/name of file with the 3rd eigenvalue') - MD = File(exists=True, desc='path/name of file with the mean diffusivity') - FA = File(exists=True, desc='path/name of file with the fractional anisotropy') - MO = File(exists=True, desc='path/name of file with the mode of anisotropy') - S0 = File(exists=True, desc='path/name of file with the raw T2 signal with no ' + - 'diffusion weighting') - tensor = File(exists=True, desc='path/name of file with the 4D tensor volume') + out_v1 = File(exists=True, desc='1st eigenvector') + out_v2 = File(exists=True, desc='2nd eigenvector') + out_v3 = File(exists=True, desc='3rd eigenvector') + out_l1 = File(exists=True, desc='1st eigenvalue') + out_l2 = File(exists=True, desc='2nd eigenvalue') + out_l3 = File(exists=True, desc='3rd eigenvalue') + out_md = File(exists=True, desc='mean diffusivity') + out_fa = File(exists=True, desc='fractional anisotropy') + out_mo = File(exists=True, desc='mode of anisotropy') + out_s0 = File(exists=True, desc='raw T2 signal with no diffusion weighting') + tensor = File(desc='path/name of file with the 4D tensor volume') class DTIFit(FSLCommand): @@ -92,14 +116,9 @@ class DTIFit(FSLCommand): input_spec = DTIFitInputSpec output_spec = DTIFitOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - for k in list(outputs.keys()): - if k not in ('outputtype', 'environ', 'args'): - if k != 'tensor' or (isdefined(self.inputs.save_tensor) and - self.inputs.save_tensor): - outputs[k] = self._gen_fname(self.inputs.base_name, suffix='_' + k) - return outputs + def _post_run(self): + if not self.inputs.save_tensor: + self.outputs.tensor = Undefined class FSLXCommandInputSpec(FSLCommandInputSpec): @@ -114,7 +133,7 @@ class FSLXCommandInputSpec(FSLCommandInputSpec): logdir = Directory('.', argstr='--logdir=%s', usedefault=True) n_fibres = traits.Range( - usedefault=True, low=1, default=2, argstr='--nfibres=%d', + usedefault=True, low=1, value=2, argstr='--nfibres=%d', desc=('Maximum number of fibres to fit in each voxel'), mandatory=True) model = traits.Enum(1, 2, 3, argstr='--model=%d', desc=('use monoexponential (1, default, required for ' @@ -154,7 +173,7 @@ class FSLXCommandInputSpec(FSLCommandInputSpec): cnlinear = traits.Bool(argstr='--cnonlinear', xor=_xor_inputs2, desc=('Initialise with constrained nonlinear ' 'fitting')) - rician = traits.Bool(argstr='--rician', desc=('use Rician noise modeling')) + rician = traits.Bool(False, usedefault=True, argstr='--rician', desc='use Rician noise modeling') _xor_inputs3 = ['f0_noard', 'f0_ard'] f0_noard = traits.Bool(argstr='--f0', xor=_xor_inputs3, @@ -167,21 +186,37 @@ class FSLXCommandInputSpec(FSLCommandInputSpec): desc=('use the actual directory name given ' '(do not add + to make a new directory)')) + mean_dsamples = GenFile(template='mean_dsamples{output_type_}', keep_extension=False, + desc='Mean of distribution on diffusivity d') + mean_S0samples = GenFile(template='mean_S0samples{output_type_}', keep_extension=False, + desc='Mean of distribution on T2w baseline signal intensity S0') + mean_tausamples = GenFile(template='mean_tausamples{output_type_}', keep_extension=False, + desc='Mean of distribution on tau samples (only with rician noise)') + dyads = GenMultiFile(template='dyads{n_fibres:d}{output_type_}', range_source='n_fibres+1', + keep_extension=False, desc='Mean of PDD distribution in vector form.') + fsamples = GenMultiFile(template='f{n_fibres:d}samples{output_type_}', range_source='n_fibres+1', + keep_extension=False, desc='Samples from the distribution on f anisotropy') + mean_fsamples = GenMultiFile(template='mean_f{n_fibres:d}samples{output_type_}', range_source='n_fibres+1', + keep_extension=False, desc='Mean of distribution on f anisotropy') + phsamples = GenMultiFile(template='ph{n_fibres:d}samples{output_type_}', range_source='n_fibres+1', + keep_extension=False, desc='phi samples, per fiber') + thsamples = GenMultiFile(template='th{n_fibres:d}samples{output_type_}', range_source='n_fibres+1', + keep_extension=False, desc='theta samples, per fiber') + class FSLXCommandOutputSpec(TraitedSpec): - dyads = OutputMultiPath(File(exists=True), desc=('Mean of PDD distribution' - ' in vector form.')) - fsamples = OutputMultiPath(File(exists=True), desc=('Samples from the ' - 'distribution on f anisotropy')) mean_dsamples = File(exists=True, desc='Mean of distribution on diffusivity d') - mean_fsamples = OutputMultiPath(File(exists=True), desc=('Mean of ' - 'distribution on f anisotropy')) - mean_S0samples = File(exists=True, desc=('Mean of distribution on T2w' - 'baseline signal intensity S0')) - mean_tausamples = File(exists=True, desc=('Mean of distribution on ' - 'tau samples (only with rician noise)')) - phsamples = OutputMultiPath(File(exists=True), desc=('phi samples, per fiber')) - thsamples = OutputMultiPath(File(exists=True), desc=('theta samples, per fiber')) + mean_S0samples = File( + exists=True, desc='Mean of distribution on T2w baseline signal intensity S0') + mean_tausamples = File( + exists=True, desc='Mean of distribution on tau samples (only with rician noise)') + dyads = OutputMultiPath(File(exists=True), desc='Mean of PDD distribution in vector form.') + fsamples = OutputMultiPath(File(exists=True), + desc='Samples from the distribution on f anisotropy') + mean_fsamples = OutputMultiPath(File(exists=True), + desc='Mean of distribution on f anisotropy') + phsamples = OutputMultiPath(File(exists=True), desc='phi samples, per fiber') + thsamples = OutputMultiPath(File(exists=True), desc='theta samples, per fiber') class FSLXCommand(FSLCommand): @@ -192,50 +227,13 @@ class FSLXCommand(FSLCommand): output_spec = FSLXCommandOutputSpec def _run_interface(self, runtime): - self._out_dir = os.getcwd() runtime = super(FSLXCommand, self)._run_interface(runtime) if runtime.stderr: self.raise_exception(runtime) - return runtime - - def _list_outputs(self, out_dir=None): - outputs = self.output_spec().get() - n_fibres = self.inputs.n_fibres - if not out_dir: - if isdefined(self.inputs.logdir): - out_dir = os.path.abspath(self.inputs.logdir) - else: - out_dir = os.path.abspath('logdir') - - multi_out = ['dyads', 'fsamples', 'mean_fsamples', - 'phsamples', 'thsamples'] - single_out = ['mean_dsamples', 'mean_S0samples'] - - for k in single_out: - outputs[k] = self._gen_fname(k, cwd=out_dir) - - if isdefined(self.inputs.rician) and self.inputs.rician: - outputs['mean_tausamples'] = self._gen_fname('mean_tausamples', - cwd=out_dir) - - for k in multi_out: - outputs[k] = [] - for i in range(1, n_fibres + 1): - outputs['fsamples'].append(self._gen_fname('f%dsamples' % i, - cwd=out_dir)) - outputs['mean_fsamples'].append(self._gen_fname(('mean_f%d' - 'samples') % i, cwd=out_dir)) - - for i in range(1, n_fibres + 1): - outputs['dyads'].append(self._gen_fname('dyads%d' % i, - cwd=out_dir)) - outputs['phsamples'].append(self._gen_fname('ph%dsamples' % i, - cwd=out_dir)) - outputs['thsamples'].append(self._gen_fname('th%dsamples' % i, - cwd=out_dir)) - - return outputs + if not self.inputs.rician: + self.outputs.mean_tausamples = Undefined + return runtime class BEDPOSTX5InputSpec(FSLXCommandInputSpec): @@ -267,27 +265,13 @@ class BEDPOSTX5InputSpec(FSLXCommandInputSpec): 'nonlinearities, default off')) use_gpu = traits.Bool(False, desc='Use the GPU version of bedpostx') + # Add dyads dispersion + dyads_dispersion = GenMultiFile( + template='{n_fibres:d}{output_type_}', keep_extension=False, range_source='n_fibres+1', + desc='Dispersion') -class BEDPOSTX5OutputSpec(TraitedSpec): - mean_dsamples = File(exists=True, desc='Mean of distribution on diffusivity d') - mean_fsamples = OutputMultiPath(File(exists=True), desc=('Mean of ' - 'distribution on f anisotropy')) - mean_S0samples = File(exists=True, desc=('Mean of distribution on T2w' - 'baseline signal intensity S0')) - mean_phsamples = OutputMultiPath(File(exists=True), desc=('Mean of ' - 'distribution on phi')) - mean_thsamples = OutputMultiPath(File(exists=True), desc=('Mean of ' - 'distribution on theta')) - merged_thsamples = OutputMultiPath(File(exists=True), desc=('Samples from ' - 'the distribution on theta')) - merged_phsamples = OutputMultiPath(File(exists=True), desc=('Samples from ' - 'the distribution on phi')) - merged_fsamples = OutputMultiPath(File(exists=True), - desc=('Samples from the distribution on ' - 'anisotropic volume fraction')) - dyads = OutputMultiPath(File(exists=True), desc=('Mean of PDD distribution' - ' in vector form.')) - dyads_dispersion = OutputMultiPath(File(exists=True), desc=('Dispersion')) +class BEDPOSTX5OutputSpec(FSLXCommandOutputSpec): + dyads_dispersion = OutputMultiPath(File(exists=True), desc='Dispersion') class BEDPOSTX5(FSLXCommand): @@ -310,7 +294,7 @@ class BEDPOSTX5(FSLXCommand): >>> from nipype.interfaces import fsl >>> bedp = fsl.BEDPOSTX5(bvecs='bvecs', bvals='bvals', dwi='diffusion.nii', - ... mask='mask.nii', n_fibres=1) + ... mask='mask.nii', n_fibres=1) >>> bedp.cmdline 'bedpostx bedpostx --forcedir -n 1' @@ -334,63 +318,26 @@ def _cuda_update(self): def _run_interface(self, runtime): - subjectdir = os.path.abspath(self.inputs.out_dir) - if not os.path.exists(subjectdir): + subjectdir = op.abspath(self.inputs.out_dir) + if not op.exists(subjectdir): os.makedirs(subjectdir) _, _, ext = split_filename(self.inputs.mask) copyfile(self.inputs.mask, - os.path.join(subjectdir, + op.join(subjectdir, 'nodif_brain_mask' + ext)) _, _, ext = split_filename(self.inputs.dwi) copyfile(self.inputs.dwi, - os.path.join(subjectdir, 'data' + ext)) + op.join(subjectdir, 'data' + ext)) copyfile(self.inputs.bvals, - os.path.join(subjectdir, 'bvals')) + op.join(subjectdir, 'bvals')) copyfile(self.inputs.bvecs, - os.path.join(subjectdir, 'bvecs')) + op.join(subjectdir, 'bvecs')) retval = super(BEDPOSTX5, self)._run_interface(runtime) self._out_dir = subjectdir + '.bedpostX' return retval - def _list_outputs(self): - outputs = self.output_spec().get() - n_fibres = self.inputs.n_fibres - - multi_out = ['merged_thsamples', 'merged_fsamples', - 'merged_phsamples', 'mean_phsamples', - 'mean_thsamples', 'mean_fsamples', - 'dyads_dispersion', 'dyads'] - - single_out = ['mean_dsamples', 'mean_S0samples'] - - for k in single_out: - outputs[k] = self._gen_fname(k, cwd=self._out_dir) - - for k in multi_out: - outputs[k] = [] - - for i in range(1, n_fibres + 1): - outputs['merged_thsamples'].append(self._gen_fname('merged_th%dsamples' % i, - cwd=self._out_dir)) - outputs['merged_fsamples'].append(self._gen_fname('merged_f%dsamples' % i, - cwd=self._out_dir)) - outputs['merged_phsamples'].append(self._gen_fname('merged_ph%dsamples' % i, - cwd=self._out_dir)) - - outputs['mean_thsamples'].append(self._gen_fname('mean_th%dsamples' % i, - cwd=self._out_dir)) - outputs['mean_phsamples'].append(self._gen_fname('mean_ph%dsamples' % i, - cwd=self._out_dir)) - outputs['mean_fsamples'].append(self._gen_fname('mean_f%dsamples' % i, - cwd=self._out_dir)) - outputs['dyads'].append(self._gen_fname('dyads%d' % i, - cwd=self._out_dir)) - outputs['dyads_dispersion'].append(self._gen_fname('dyads%d_dispersion' % i, - cwd=self._out_dir)) - return outputs - class XFibres5InputSpec(FSLXCommandInputSpec): gradnonlin = File(exists=True, argstr='--gradnonlin=%s', @@ -413,7 +360,7 @@ class XFibres4InputSpec(FSLCommandInputSpec): gradnonlin = File(exists=True, argstr="--gradnonlin=%s") bvecs = File(exists=True, argstr="--bvecs=%s", mandatory=True) bvals = File(exists=True, argstr="--bvals=%s", mandatory=True) - logdir = Directory("logdir", argstr="--logdir=%s", usedefault=True) + logdir = Directory('logdir', argstr="--logdir=%s", usedefault=True) n_fibres = traits.Range(low=1, argstr="--nfibres=%d", desc="Maximum nukmber of fibres to fit in each voxel") fudge = traits.Int(argstr="--fudge=%d", @@ -466,40 +413,36 @@ class XFibres4(FSLCommand): """ - _cmd = "xfibres" + _cmd = 'xfibres' input_spec = XFibres4InputSpec output_spec = XFibres4OutputSpec def __init__(self, **inputs): - warnings.warn(('Deprecated: Please use XFIBERS5 instead. This ' - 'interface will be removed in version 0.11.'), - DeprecationWarning) + IFLOGGER.warn('Deprecated: Please use XFIBERS5 instead. This ' + 'interface will be removed in version 0.11.') super(XFibres4, self).__init__(**inputs) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["mean_dsamples"] = self._gen_fname("mean_dsamples", - cwd=self.inputs.logdir) - outputs["mean_S0samples"] = self._gen_fname("mean_S0samples", - cwd=self.inputs.logdir) - outputs["dyads"] = [] - outputs["fsamples"] = [] - outputs["mean_fsamples"] = [] - outputs["phsamples"] = [] - outputs["thsamples"] = [] + def _post_run(self): + self.outputs.mean_dsamples = self._gen_fname('mean_dsamples', + self.inputs.logdir) + self.outputs.mean_S0samples = self._gen_fname('mean_S0samples', + self.inputs.logdir) + self.outputs.dyads = [] + self.outputs.fsamples = [] + self.outputs.mean_fsamples = [] + self.outputs.phsamples = [] + self.outputs.thsamples = [] for i in range(1, self.inputs.n_fibres + 1): - outputs["dyads"].append(self._gen_fname("dyads%d" % i, - cwd=self.inputs.logdir)) - outputs["fsamples"].append(self._gen_fname("f%dsamples" % i, - cwd=self.inputs.logdir)) - outputs["mean_fsamples"].append(self._gen_fname("mean_f%dsamples" % i, - cwd=self.inputs.logdir)) - outputs["phsamples"].append(self._gen_fname("ph%dsamples" % i, - cwd=self.inputs.logdir)) - outputs["thsamples"].append(self._gen_fname("th%dsamples" % i, - cwd=self.inputs.logdir)) - - return outputs + self.outputs.dyads.append(self._gen_fname('dyads%d' % i, + self.inputs.logdir)) + self.outputs.fsamples.append(self._gen_fname('f%dsamples' % i, + self.inputs.logdir)) + self.outputs.mean_fsamples.append(self._gen_fname('mean_f%dsamples' % i, + self.inputs.logdir)) + self.outputs.phsamples.append(self._gen_fname('ph%dsamples' % i, + self.inputs.logdir)) + self.outputs.thsamples.append(self._gen_fname('th%dsamples' % i, + self.inputs.logdir)) class BEDPOSTX4InputSpec(XFibres4InputSpec): @@ -577,32 +520,32 @@ class BEDPOSTX4(FSLCommand): _can_resume = True def __init__(self, **inputs): - warnings.warn(('Deprecated: Please use BEDPOSTX5 or ' - 'create_bedpostx_pipeline instead. This interface will ' - 'be removed in version 0.11.'), DeprecationWarning) + IFLOGGER.warn('Deprecated: Please use BEDPOSTX5 or ' + 'create_bedpostx_pipeline instead. This interface will ' + 'be removed in version 0.11.') super(BEDPOSTX4, self).__init__(**inputs) def _get_bedpostx_dir(self): - return os.path.join(os.getcwd(), self.inputs.bpx_directory) + return op.join(os.getcwd(), self.inputs.bpx_directory) def _run_interface(self, runtime, correct_return_codes=[0]): # create the subject specific bpx_directory bpx_directory = self._get_bedpostx_dir() - if not os.path.exists(bpx_directory): + if not op.exists(bpx_directory): os.makedirs(bpx_directory) _, _, ext = split_filename(self.inputs.mask) shutil.copyfile(self.inputs.mask, - os.path.join(self.inputs.bpx_directory, + op.join(self.inputs.bpx_directory, 'nodif_brain_mask' + ext)) _, _, ext = split_filename(self.inputs.dwi) shutil.copyfile(self.inputs.dwi, - os.path.join(self.inputs.bpx_directory, 'data' + ext)) + op.join(self.inputs.bpx_directory, 'data' + ext)) shutil.copyfile(self.inputs.bvals, - os.path.join(self.inputs.bpx_directory, 'bvals')) + op.join(self.inputs.bpx_directory, 'bvals')) shutil.copyfile(self.inputs.bvecs, - os.path.join(self.inputs.bpx_directory, 'bvecs')) + op.join(self.inputs.bpx_directory, 'bvecs')) runtime = super(BEDPOSTX4, self)._run_interface(runtime, correct_return_codes) @@ -610,44 +553,35 @@ def _run_interface(self, runtime, correct_return_codes=[0]): self.raise_exception(runtime) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - bpx_directory = self._get_bedpostx_dir() - outputs['bpx_out_directory'] = os.path.join(bpx_directory + '.bedpostX') - outputs['xfms_directory'] = os.path.join(bpx_directory + '.bedpostX', - 'xfms') + def _post_run(self): + bpx_out_dir = op.abspath(self._get_bedpostx_dir() + '.bedpostX') + self.outputs.bpx_out_directory = bpx_out_dir + self.outputs.xfms_directory = op.join(bpx_out_dir, 'xfms') - for k in list(outputs.keys()): - if k not in ('outputtype', 'environ', 'args', 'bpx_out_directory', + for k, _ in list(self.outputs.items()): + if k in ('outputtype', 'environ', 'args', 'bpx_out_directory', 'xfms_directory'): - outputs[k] = [] - - for n in range(self.inputs.fibres): - outputs['merged_thsamples'].append(self._gen_fname( - 'merged_th' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['merged_phsamples'].append(self._gen_fname( - 'merged_ph' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['merged_fsamples'].append(self._gen_fname( - 'merged_f' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['mean_thsamples'].append(self._gen_fname( - 'mean_th' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['mean_phsamples'].append(self._gen_fname( - 'mean_ph' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['mean_fsamples'].append(self._gen_fname( - 'mean_f' + repr(n + 1) + 'samples', - suffix='', cwd=outputs['bpx_out_directory'])) - outputs['dyads'].append(self._gen_fname( - 'dyads' + repr(n + 1), - suffix='', cwd=outputs['bpx_out_directory'])) - return outputs - - -if (Info.version() and LooseVersion(Info.version()) >= LooseVersion('5.0.0')): + continue + setattr(self.outputs, k, []) + + for i in [repr(n + 1) for n in range(self.inputs.fibres)]: + self.outputs.merged_thsamples.append( + self._gen_fname('merged_th' + repr(i + 1) + 'samples', bpx_out_dir)) + + self.outputs.merged_phsamples.append(self._gen_fname( + 'merged_ph' + repr(i + 1) + 'samples', bpx_out_dir), suffix='') + self.outputs.merged_fsamples.append(self._gen_fname( + 'merged_f' + repr(i + 1) + 'samples', bpx_out_dir), suffix='') + self.outputs.mean_thsamples.append(self._gen_fname( + 'mean_th' + repr(i + 1) + 'samples', bpx_out_dir), suffix='') + self.outputs.mean_phsamples.append(self._gen_fname( + 'mean_ph' + repr(i + 1) + 'samples', bpx_out_dir), suffix='') + self.outputs.mean_fsamples.append(self._gen_fname( + 'mean_f' + repr(i + 1) + 'samples', bpx_out_dir), suffix='') + self.outputs.dyads.append(self._gen_fname( + 'dyads' + repr(i + 1), bpx_out_dir), suffix='') + +if Info.version() and (LooseVersion(Info.version()) >= LooseVersion('5.0.0')): CurrentXFibres = XFibres5 CurrentBEDPOST = BEDPOSTX5 else: @@ -667,7 +601,7 @@ class ProbTrackXBaseInputSpec(FSLCommandInputSpec): thsamples = InputMultiPath(File(exists=True), mandatory=True) phsamples = InputMultiPath(File(exists=True), mandatory=True) fsamples = InputMultiPath(File(exists=True), mandatory=True) - samples_base_name = traits.Str("merged", desc='the rootname/base_name for samples files', + samples_base_name = traits.Str('merged', desc='the rootname/base_name for samples files', argstr='--samples=%s', usedefault=True) mask = File(exists=True, desc='bet binary mask file in diffusion space', argstr='-m %s', mandatory=True) @@ -732,9 +666,26 @@ class ProbTrackXBaseInputSpec(FSLCommandInputSpec): "Level 2 is required to output particle files.", argstr="--verbose=%d") + def _format_arg(self, name, spec=None, value=None): + if spec is None: + spec = self.traits()[name] + + if value is None: + value = getattr(self, name) + + if name == 'target_masks' and isdefined(value): + fname = 'targets.txt' + return super(ProbTrackXBaseInputSpec, self)._format_arg(name, spec, [fname]) + elif name == 'seed' and isinstance(value, list): + fname = 'seeds.txt' + return super(ProbTrackXBaseInputSpec, self)._format_arg(name, spec, fname) + else: + return super(ProbTrackXBaseInputSpec, self)._format_arg(name, spec, value) + + class ProbTrackXInputSpec(ProbTrackXBaseInputSpec): - mode = traits.Enum("simple", "two_mask_symm", "seedmask", + mode = traits.Enum('simple', 'two_mask_symm', 'seedmask', desc='options: simple (single seed voxel), seedmask (mask of seed voxels), ' + 'twomask_symm (two bet binary masks) ', argstr='--mode=%s', genfile=True) @@ -780,31 +731,31 @@ class ProbTrackX(FSLCommand): output_spec = ProbTrackXOutputSpec def __init__(self, **inputs): - warnings.warn("Deprecated: Please use create_bedpostx_pipeline instead", DeprecationWarning) - return super(ProbTrackX, self).__init__(**inputs) + IFLOGGER.warn('Deprecated: Please use create_bedpostx_pipeline instead') + super(ProbTrackX, self).__init__(**inputs) def _run_interface(self, runtime): for i in range(1, len(self.inputs.thsamples) + 1): _, _, ext = split_filename(self.inputs.thsamples[i - 1]) copyfile(self.inputs.thsamples[i - 1], - self.inputs.samples_base_name + "_th%dsamples" % i + ext, + self.inputs.samples_base_name + '_th%dsamples' % i + ext, copy=False) _, _, ext = split_filename(self.inputs.thsamples[i - 1]) copyfile(self.inputs.phsamples[i - 1], - self.inputs.samples_base_name + "_ph%dsamples" % i + ext, + self.inputs.samples_base_name + '_ph%dsamples' % i + ext, copy=False) _, _, ext = split_filename(self.inputs.thsamples[i - 1]) copyfile(self.inputs.fsamples[i - 1], - self.inputs.samples_base_name + "_f%dsamples" % i + ext, + self.inputs.samples_base_name + '_f%dsamples' % i + ext, copy=False) if isdefined(self.inputs.target_masks): - f = open("targets.txt", "w") + f = open('targets.txt', 'w') for target in self.inputs.target_masks: f.write("%s\n" % target) f.close() if isinstance(self.inputs.seed, list): - f = open("seeds.txt", "w") + f = open('seeds.txt', 'w') for seed in self.inputs.seed: if isinstance(seed, list): f.write("%s\n" % (" ".join([str(s) for s in seed]))) @@ -817,59 +768,48 @@ def _run_interface(self, runtime): self.raise_exception(runtime) return runtime - def _format_arg(self, name, spec, value): - if name == 'target_masks' and isdefined(value): - fname = "targets.txt" - return super(ProbTrackX, self)._format_arg(name, spec, [fname]) - elif name == 'seed' and isinstance(value, list): - fname = "seeds.txt" - return super(ProbTrackX, self)._format_arg(name, spec, fname) - else: - return super(ProbTrackX, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): if not isdefined(self.inputs.out_dir): - out_dir = self._gen_filename("out_dir") + out_dir = self._gen_filename('out_dir') else: out_dir = self.inputs.out_dir - outputs['log'] = os.path.abspath(os.path.join(out_dir, 'probtrackx.log')) - # utputs['way_total'] = os.path.abspath(os.path.join(out_dir, 'waytotal')) + self.outputs.log = op.abspath(op.join(out_dir, 'probtrackx.log')) + # utputs['way_total'] = op.abspath(op.join(out_dir, 'waytotal')) if isdefined(self.inputs.opd is True): if isinstance(self.inputs.seed, list) and isinstance(self.inputs.seed[0], list): - outputs['fdt_paths'] = [] + self.outputs.fdt_paths = [] for seed in self.inputs.seed: - outputs['fdt_paths'].append( - os.path.abspath( - self._gen_fname("fdt_paths_%s" % ("_".join([str(s) for s in seed])), - cwd=out_dir, suffix=''))) + self.outputs.fdt_paths.append( + op.abspath( + self._gen_fname('fdt_paths_%s' % ('_'.join([str(s) for s in seed])), + out_dir, suffix=''))) else: - outputs['fdt_paths'] = os.path.abspath(self._gen_fname("fdt_paths", - cwd=out_dir, suffix='')) + self.outputs.fdt_paths = op.abspath(self._gen_fname('fdt_paths', + out_dir, suffix='')) # handle seeds-to-target output files if isdefined(self.inputs.target_masks): - outputs['targets'] = [] + self.outputs.targets = [] for target in self.inputs.target_masks: - outputs['targets'].append(os.path.abspath( - self._gen_fname('seeds_to_' + os.path.split(target)[1], - cwd=out_dir, + self.outputs.targets.append(op.abspath( + self._gen_fname('seeds_to_' + op.split(target)[1], + out_dir, suffix=''))) if isdefined(self.inputs.verbose) and self.inputs.verbose == 2: - outputs['particle_files'] = [os.path.abspath( - os.path.join(out_dir, 'particle%d' % i)) + self.outputs.particle_files = [op.abspath( + op.join(out_dir, 'particle%d' % i)) for i in range(self.inputs.n_samples)] - return outputs def _gen_filename(self, name): - if name == "out_dir": + if name == 'out_dir': return os.getcwd() - elif name == "mode": + elif name == 'mode': if isinstance(self.inputs.seed, list) and isinstance(self.inputs.seed[0], list): - return "simple" + return 'simple' else: - return "seedmask" + return 'seedmask' class ProbTrackX2InputSpec(ProbTrackXBaseInputSpec): @@ -877,8 +817,8 @@ class ProbTrackX2InputSpec(ProbTrackXBaseInputSpec): usedefault=False, argstr='--simple') fopd = File(exists=True, desc='Other mask for binning tract distribution', argstr='--fopd=%s') - waycond = traits.Enum("OR", "AND", argstr='--waycond=%s', - desc='Waypoint condition. Either "AND" (default) or "OR"') + waycond = traits.Enum('OR', 'AND', argstr='--waycond=%s', + desc='Waypoint condition. Either \'AND\' (default) or \'OR\'') wayorder = traits.Bool(desc='Reject streamlines that do not hit waypoints in given order. ' + 'Only valid if waycond=AND', argstr='--wayorder') onewaycondition = traits.Bool(desc='Apply waypoint conditions to each half tract separately', @@ -905,9 +845,9 @@ class ProbTrackX2InputSpec(ProbTrackXBaseInputSpec): colmask4 = File(exists=True, desc='Mask for columns of matrix4 (default=seed mask)', argstr='--colmask4=%s') target4 = File(exists=True, desc='Brain mask in DTI space', argstr='--target4=%s') - meshspace = traits.Enum("caret", "freesurfer", "first", "vox", argstr='--meshspace=%s', - desc='Mesh reference space - either "caret" (default) or ' + - '"freesurfer" or "first" or "vox"') + meshspace = traits.Enum('caret', 'freesurfer', 'first', 'vox', argstr='--meshspace=%s', + desc='Mesh reference space - either \'caret\' (default) or ' + + '\'freesurfer\' or \'first\' or \'vox\'') class ProbTrackX2OutputSpec(ProbTrackXOutputSpec): @@ -941,8 +881,8 @@ class ProbTrackX2(ProbTrackX): input_spec = ProbTrackX2InputSpec output_spec = ProbTrackX2OutputSpec - def _list_outputs(self): - outputs = super(ProbTrackX2, self)._list_outputs() + def _post_run(self): + outputs = super(ProbTrackX2, self)._post_run() if not isdefined(self.inputs.out_dir): out_dir = os.getcwd() @@ -950,17 +890,16 @@ def _list_outputs(self): out_dir = self.inputs.out_dir if isdefined(self.inputs.omatrix1): - outputs['network_matrix'] = os.path.abspath(os.path.join(out_dir, 'fdt_network_matrix')) - outputs['matrix1_dot'] = os.path.abspath(os.path.join(out_dir, 'fdt_matrix1.dot')) + self.outputs.network_matrix = op.abspath(op.join(out_dir, 'fdt_network_matrix')) + self.outputs.matrix1_dot = op.abspath(op.join(out_dir, 'fdt_matrix1.dot')) if isdefined(self.inputs.omatrix2): - outputs['lookup_tractspace'] = \ - os.path.abspath(os.path.join(out_dir, 'lookup_tractspace_fdt_matrix2.nii.gz')) - outputs['matrix2_dot'] = os.path.abspath(os.path.join(out_dir, 'fdt_matrix2.dot')) + self.outputs.lookup_tractspace = \ + op.abspath(op.join(out_dir, 'lookup_tractspace_fdt_matrix2.nii.gz')) + self.outputs.matrix2_dot = op.abspath(op.join(out_dir, 'fdt_matrix2.dot')) if isdefined(self.inputs.omatrix3): - outputs['matrix3_dot'] = os.path.abspath(os.path.join(out_dir, 'fdt_matrix3.dot')) - return outputs + self.outputs.matrix3_dot = op.abspath(op.join(out_dir, 'fdt_matrix3.dot')) class VecRegInputSpec(FSLCommandInputSpec): @@ -980,7 +919,7 @@ class VecRegInputSpec(FSLCommandInputSpec): rotation_warp = File(exists=True, argstr='--rotwarp=%s', desc='filename for secondary warp field' + 'if set, this will be used for the rotation of the vector/tensor field') - interpolation = traits.Enum("nearestneighbour", "trilinear", "sinc", "spline", + interpolation = traits.Enum('nearestneighbour', 'trilinear', 'sinc', 'spline', argstr='--interp=%s', desc='interpolation method : ' + 'nearestneighbour, trilinear (default), sinc or spline') @@ -1017,24 +956,22 @@ class VecReg(FSLCommand): def _run_interface(self, runtime): if not isdefined(self.inputs.out_file): - pth, base_name = os.path.split(self.inputs.in_file) - self.inputs.out_file = self._gen_fname(base_name, cwd=os.path.abspath(pth), + pth, base_name = op.split(self.inputs.in_file) + self.inputs.out_file = self._gen_fname(base_name, op.abspath(pth), suffix='_vreg') return super(VecReg, self)._run_interface(runtime) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']) and isdefined(self.inputs.in_file): - pth, base_name = os.path.split(self.inputs.in_file) - outputs['out_file'] = self._gen_fname(base_name, cwd=os.path.abspath(pth), + def _post_run(self): + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file) and isdefined(self.inputs.in_file): + pth, base_name = op.split(self.inputs.in_file) + self.outputs.out_file = self._gen_fname(base_name, op.abspath(pth), suffix='_vreg') - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs + self.outputs.out_file = op.abspath(self.outputs.out_file) def _gen_filename(self, name): if name is 'out_file': - return self._list_outputs()[name] + return self._post_run()[name] else: return None @@ -1072,15 +1009,13 @@ class ProjThresh(FSLCommand): input_spec = ProjThreshInputSpec output_spec = ProjThreshOuputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_files'] = [] + def _post_run(self): + self.outputs.out_files = [] for name in self.inputs.in_files: - cwd, base_name = os.path.split(name) - outputs['out_files'].append(self._gen_fname(base_name, cwd=cwd, + cwd, base_name = op.split(name) + self.outputs.out_files.append(self._gen_fname(base_name, cwd, suffix='_proj_seg_thr_' + repr(self.inputs.threshold))) - return outputs class FindTheBiggestInputSpec(FSLCommandInputSpec): @@ -1121,17 +1056,15 @@ def _run_interface(self, runtime): self.inputs.out_file = self._gen_fname('biggestSegmentation', suffix='') return super(FindTheBiggest, self)._run_interface(runtime) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): - outputs['out_file'] = self._gen_fname('biggestSegmentation', suffix='') - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs + def _post_run(self): + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): + self.outputs.out_file = self._gen_fname('biggestSegmentation', suffix='') + self.outputs.out_file = op.abspath(self.outputs.out_file) def _gen_filename(self, name): if name is 'out_file': - return self._list_outputs()[name] + return self._post_run()[name] else: return None @@ -1140,15 +1073,15 @@ class TractSkeletonInputSpec(FSLCommandInputSpec): in_file = File(exists=True, mandatory=True, argstr="-i %s", desc="input image (typcially mean FA volume)") - _proj_inputs = ["threshold", "distance_map", "data_file"] + _proj_inputs = ['threshold', 'distance_map', 'data_file'] project_data = traits.Bool(argstr="-p %.3f %s %s %s %s", requires=_proj_inputs, desc="project data onto skeleton") threshold = traits.Float(desc="skeleton threshold value") distance_map = File(exists=True, desc="distance map image") - search_mask_file = File(exists=True, xor=["use_cingulum_mask"], + search_mask_file = File(exists=True, xor=['use_cingulum_mask'], desc="mask in which to use alternate search rule") use_cingulum_mask = traits.Bool(True, usedefault=True, - xor=["search_mask_file"], + xor=['search_mask_file'], desc="perform alternate search using built-in cingulum mask") data_file = File(exists=True, desc="4D data to project onto skeleton (usually FA)") alt_data_file = File(exists=True, argstr="-a %s", desc="4D non-FA data to project onto skeleton") @@ -1179,58 +1112,56 @@ class TractSkeleton(FSLCommand): >>> import nipype.interfaces.fsl as fsl >>> skeletor = fsl.TractSkeleton() - >>> skeletor.inputs.in_file = "all_FA.nii.gz" + >>> skeletor.inputs.in_file = 'all_FA.nii.gz' >>> skeletor.inputs.skeleton_file = True >>> skeletor.run() # doctest: +SKIP """ - _cmd = "tbss_skeleton" + _cmd = 'tbss_skeleton' input_spec = TractSkeletonInputSpec output_spec = TractSkeletonOutputSpec def _format_arg(self, name, spec, value): - if name == "project_data": + if name == 'project_data': if isdefined(value) and value: _si = self.inputs if isdefined(_si.use_cingulum_mask) and _si.use_cingulum_mask: - mask_file = Info.standard_image("LowerCingulum_1mm.nii.gz") + mask_file = Info.standard_image('LowerCingulum_1mm.nii.gz') else: mask_file = _si.search_mask_file if not isdefined(_si.projected_data): - proj_file = self._list_outputs()["projected_data"] + proj_file = self.outputs.projected_data else: proj_file = _si.projected_data return spec.argstr % (_si.threshold, _si.distance_map, mask_file, _si.data_file, proj_file) - elif name == "skeleton_file": + elif name == 'skeleton_file': if isinstance(value, bool): - return spec.argstr % self._list_outputs()["skeleton_file"] + return spec.argstr % self.outputs.skeleton_file else: return spec.argstr % value return super(TractSkeleton, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): _si = self.inputs if isdefined(_si.project_data) and _si.project_data: proj_data = _si.projected_data - outputs["projected_data"] = proj_data + self.outputs.projected_data = proj_data if not isdefined(proj_data): stem = _si.data_file if isdefined(_si.alt_data_file): stem = _si.alt_data_file - outputs["projected_data"] = fname_presuffix(stem, - suffix="_skeletonised", + self.outputs.projected_data = fname_presuffix(stem, + suffix='_skeletonised', newpath=os.getcwd(), use_ext=True) if isdefined(_si.skeleton_file) and _si.skeleton_file: - outputs["skeleton_file"] = _si.skeleton_file + self.outputs.skeleton_file = _si.skeleton_file if isinstance(_si.skeleton_file, bool): - outputs["skeleton_file"] = fname_presuffix(_si.in_file, - suffix="_skeleton", + self.outputs.skeleton_file = fname_presuffix(_si.in_file, + suffix='_skeleton', newpath=os.getcwd(), use_ext=True) - return outputs class DistanceMapInputSpec(FSLCommandInputSpec): @@ -1259,56 +1190,54 @@ class DistanceMap(FSLCommand): >>> import nipype.interfaces.fsl as fsl >>> mapper = fsl.DistanceMap() - >>> mapper.inputs.in_file = "skeleton_mask.nii.gz" + >>> mapper.inputs.in_file = 'skeleton_mask.nii.gz' >>> mapper.run() # doctest: +SKIP """ - _cmd = "distancemap" + _cmd = 'distancemap' input_spec = DistanceMapInputSpec output_spec = DistanceMapOutputSpec def _format_arg(self, name, spec, value): - if name == "local_max_file": + if name == 'local_max_file': if isinstance(value, bool): - return spec.argstr % self._list_outputs()["local_max_file"] + return spec.argstr % self.outputs.local_max_file return super(DistanceMap, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): _si = self.inputs - outputs["distance_map"] = _si.distance_map + self.outputs.distance_map = _si.distance_map if not isdefined(_si.distance_map): - outputs["distance_map"] = fname_presuffix(_si.in_file, - suffix="_dstmap", + self.outputs.distance_map = fname_presuffix(_si.in_file, + suffix='_dstmap', use_ext=True, newpath=os.getcwd()) - outputs["distance_map"] = os.path.abspath(outputs["distance_map"]) + self.outputs.distance_map = op.abspath(self.outputs.distance_map) if isdefined(_si.local_max_file): - outputs["local_max_file"] = _si.local_max_file + self.outputs.local_max_file = _si.local_max_file if isinstance(_si.local_max_file, bool): - outputs["local_max_file"] = fname_presuffix(_si.in_file, - suffix="_lclmax", + self.outputs.local_max_file = fname_presuffix(_si.in_file, + suffix='_lclmax', use_ext=True, newpath=os.getcwd()) - outputs["local_max_file"] = os.path.abspath(outputs["local_max_file"]) - return outputs + self.outputs.local_max_file = op.abspath(self.outputs.local_max_file) def _gen_filename(self, name): - if name == "distance_map": - return self._list_outputs()["distance_map"] + if name == 'distance_map': + return self.outputs.distance_map return None class MakeDyadicVectorsInputSpec(FSLCommandInputSpec): - theta_vol = File(exists=True, mandatory=True, position=0, argstr="%s") - phi_vol = File(exists=True, mandatory=True, position=1, argstr="%s") - mask = File(exists=True, position=2, argstr="%s") - output = File("dyads", position=3, usedefault=True, argstr="%s", hash_files=False) + theta_vol = File(exists=True, mandatory=True, position=0, argstr='%s') + phi_vol = File(exists=True, mandatory=True, position=1, argstr='%s') + mask = File(exists=True, position=2, argstr='%s') + output = File('dyads', position=3, usedefault=True, argstr='%s', hash_files=False) perc = traits.Float(desc="the {perc}% angle of the output cone of \ uncertainty (output will be in degrees)", position=4, - argstr="%f") + argstr='%f') class MakeDyadicVectorsOutputSpec(TraitedSpec): @@ -1320,14 +1249,12 @@ class MakeDyadicVectors(FSLCommand): """Create vector volume representing mean principal diffusion direction and its uncertainty (dispersion)""" - _cmd = "make_dyadic_vectors" + _cmd = 'make_dyadic_vectors' input_spec = MakeDyadicVectorsInputSpec output_spec = MakeDyadicVectorsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["dyads"] = self._gen_fname(self.inputs.output) - outputs["dispersion"] = self._gen_fname(self.inputs.output, - suffix="_dispersion") + def _post_run(self): + self.outputs.dyads = self._gen_fname(self.inputs.output) + self.outputs.dispersion = self._gen_fname(self.inputs.output, + suffix='_dispersion') - return outputs diff --git a/nipype/interfaces/fsl/epi.py b/nipype/interfaces/fsl/epi.py index 30b1a2c330..cadc2f48b8 100644 --- a/nipype/interfaces/fsl/epi.py +++ b/nipype/interfaces/fsl/epi.py @@ -13,19 +13,18 @@ """ import os -import warnings from glob import glob import numpy as np import nibabel as nib from ..fsl.base import FSLCommand, FSLCommandInputSpec, Info -from ..base import (traits, TraitedSpec, InputMultiPath, File, +from ..base import (traits, TraitedSpec, InputMultiPath, File, GenFile, isdefined, Undefined) from ...utils.filemanip import (load_json, save_json, split_filename, fname_presuffix) - -warn = warnings.warn +from ... import logging +IFLOGGER = logging.getLogger('interface') class PrepareFieldmapInputSpec(FSLCommandInputSpec): @@ -41,12 +40,11 @@ class PrepareFieldmapInputSpec(FSLCommandInputSpec): desc=('echo time difference of the ' 'fieldmap sequence in ms. (usually 2.46ms in' ' Siemens)')) - nocheck = traits.Bool(False, position=-1, argstr='--nocheck', - usedefault=True, + nocheck = traits.Bool(False, position=-1, argstr='--nocheck', usedefault=True, desc=('do not perform sanity checks for image ' 'size/range/dimensions')) - out_fieldmap = File(argstr='%s', position=4, - desc='output name for prepared fieldmap') + out_fieldmap = GenFile(template='{in_phase}_fslprepared{output_type_}', argstr='%s', + position=4, desc='output name for prepared fieldmap') class PrepareFieldmapOutputSpec(TraitedSpec): @@ -72,7 +70,7 @@ class PrepareFieldmap(FSLCommand): >>> prepare.inputs.output_type = "NIFTI_GZ" >>> prepare.cmdline #doctest: +ELLIPSIS 'fsl_prepare_fieldmap SIEMENS phase.nii magnitude.nii \ -.../phase_fslprepared.nii.gz 2.460000' +phase_fslprepared.nii.gz 2.460000' >>> res = prepare.run() # doctest: +SKIP @@ -81,28 +79,12 @@ class PrepareFieldmap(FSLCommand): input_spec = PrepareFieldmapInputSpec output_spec = PrepareFieldmapOutputSpec - def _parse_inputs(self, skip=None): - if skip is None: - skip = [] - - if not isdefined(self.inputs.out_fieldmap): - self.inputs.out_fieldmap = self._gen_fname( - self.inputs.in_phase, suffix='_fslprepared') - - if not isdefined(self.inputs.nocheck) or not self.inputs.nocheck: - skip += ['nocheck'] - - return super(PrepareFieldmap, self)._parse_inputs(skip=skip) - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_fieldmap'] = self.inputs.out_fieldmap - return outputs - def _run_interface(self, runtime): runtime = super(PrepareFieldmap, self)._run_interface(runtime) if runtime.returncode == 0: + # Add an empty volume to the output, since downstream software + # expects two GRE images to compute the difference out_file = self.inputs.out_fieldmap im = nib.load(out_file) dumb_img = nib.Nifti1Image(np.zeros(im.shape), im.affine, @@ -116,35 +98,19 @@ def _run_interface(self, runtime): class TOPUPInputSpec(FSLCommandInputSpec): in_file = File(exists=True, mandatory=True, desc='name of 4D file with images', argstr='--imain=%s') - encoding_file = File(exists=True, mandatory=True, - xor=['encoding_direction'], - desc='name of text file with PE directions/times', - argstr='--datain=%s') - encoding_direction = traits.List(traits.Enum('y', 'x', 'z', 'x-', 'y-', - 'z-'), mandatory=True, - xor=['encoding_file'], - requires=['readout_times'], - argstr='--datain=%s', - desc=('encoding direction for automatic ' - 'generation of encoding_file')) - readout_times = InputMultiPath(traits.Float, - requires=['encoding_direction'], - xor=['encoding_file'], mandatory=True, - desc=('readout times (dwell times by # ' - 'phase-encode steps minus 1)')) - out_base = File(desc=('base-name of output files (spline ' - 'coefficients (Hz) and movement parameters)'), - name_source=['in_file'], name_template='%s_base', - argstr='--out=%s', hash_files=False) - out_field = File(argstr='--fout=%s', hash_files=False, - name_source=['in_file'], name_template='%s_field', - desc='name of image file with field (Hz)') - out_corrected = File(argstr='--iout=%s', hash_files=False, - name_source=['in_file'], name_template='%s_corrected', - desc='name of 4D image file with unwarped images') - out_logfile = File(argstr='--logout=%s', desc='name of log-file', - name_source=['in_file'], name_template='%s_topup.log', - keep_extension=True, hash_files=False) + encoding_file = File( + template='{in_file}_encfile.txt', hash_files=False, + output_name='out_enc_file', mandatory=True, xor=['encoding_direction'], + argstr='--datain=%s', desc='name of text file with PE directions/times') + + encoding_direction = traits.List(traits.Enum( + 'y', 'x', 'z', 'x-', 'y-', 'z-'), mandatory=True, xor=['encoding_file'], + requires=['readout_times'], desc='encoding direction for automatic ' + 'generation of encoding_file') + readout_times = InputMultiPath( + traits.Float, requires=['encoding_direction'], xor=['encoding_file'], + mandatory=True, desc='readout times (dwell times by # phase-encode ' + 'steps minus 1)') # TODO: the following traits admit values separated by commas, one value # per registration level inside topup. @@ -204,6 +170,26 @@ class TOPUPInputSpec(FSLCommandInputSpec): desc=('If set (=1), the calculations are done in a ' 'different grid')) + # Outputs + out_base = GenFile( + template='{in_file}_base', argstr='--out=%s', hash_files=False, + desc='base-name of output files (spline coefficients (Hz) and movement parameters)') + out_field = GenFile( + template='{in_file}_field{output_type_}', argstr='--fout=%s', hash_files=False, + desc='name of image file with field (Hz)') + out_corrected = GenFile( + template='{in_file}_corrected{output_type_}', argstr='--iout=%s', hash_files=False, + desc='name of 4D image file with unwarped images') + out_logfile = GenFile( + template='{in_file}_topup.log', argstr='--logout=%s', hash_files=False, + desc='name of log-file') + + out_fieldcoef = GenFile( + template='{out_base}_fieldcoef{output_type_}', hash_files=False, + desc='file containing the field coefficients') + out_movpar = GenFile( + template='{out_base}_movpar.txt', hash_files=False, + desc='file containing the field coefficients') class TOPUPOutputSpec(TraitedSpec): out_fieldcoef = File(exists=True, @@ -244,67 +230,13 @@ class TOPUP(FSLCommand): input_spec = TOPUPInputSpec output_spec = TOPUPOutputSpec - def _format_arg(self, name, trait_spec, value): - if name == 'encoding_direction': - return trait_spec.argstr % self._generate_encfile() - if name == 'out_base': - path, name, ext = split_filename(value) - if path != '': - if not os.path.exists(path): - raise ValueError('out_base path must exist if provided') - return super(TOPUP, self)._format_arg(name, trait_spec, value) - - def _list_outputs(self): - outputs = super(TOPUP, self)._list_outputs() - del outputs['out_base'] - base_path = None - if isdefined(self.inputs.out_base): - base_path, base, _ = split_filename(self.inputs.out_base) - if base_path == '': - base_path = None - else: - base = split_filename(self.inputs.in_file)[1] + '_base' - outputs['out_fieldcoef'] = self._gen_fname(base, suffix='_fieldcoef', - cwd=base_path) - outputs['out_movpar'] = self._gen_fname(base, suffix='_movpar', - ext='.txt', cwd=base_path) - - if isdefined(self.inputs.encoding_direction): - outputs['out_enc_file'] = self._get_encfilename() - return outputs - - def _get_encfilename(self): - out_file = os.path.join(os.getcwd(), - ('%s_encfile.txt' % - split_filename(self.inputs.in_file)[1])) - return out_file - - def _generate_encfile(self): - """Generate a topup compatible encoding file based on given directions - """ - out_file = self._get_encfilename() - durations = self.inputs.readout_times - if len(self.inputs.encoding_direction) != len(durations): - if len(self.inputs.readout_times) != 1: - raise ValueError(('Readout time must be a float or match the' - 'length of encoding directions')) - durations = durations * len(self.inputs.encoding_direction) - - lines = [] - for idx, encdir in enumerate(self.inputs.encoding_direction): - direction = 1.0 - if encdir.endswith('-'): - direction = -1.0 - line = [float(val[0] == encdir[0]) * direction - for val in ['x', 'y', 'z']] + [durations[idx]] - lines.append(line) - np.savetxt(out_file, np.array(lines), fmt='%d %d %d %.8f') - return out_file - - def _overload_extension(self, value, name=None): - if name == 'out_base': - return value - return super(TOPUP, self)._overload_extension(value, name) + def _run_interface(self, runtime): + if not os.path.isfile(self.inputs.encoding_file): + topup_generate_encfile( + self.inputs.readout_times, + self.inputs.encoding_direction, + self.inputs.encoding_file) + return super(TOPUP, self)._run_interface(runtime) class ApplyTOPUPInputSpec(FSLCommandInputSpec): @@ -325,10 +257,9 @@ class ApplyTOPUPInputSpec(FSLCommandInputSpec): 'coefficients')) in_topup_movpar = File(exists=True, requires=['in_topup_fieldcoef'], copyfile=False, desc='topup movpar.txt file') - out_corrected = File(desc='output (warped) image', - name_source=['in_files'], - name_template='%s_corrected', - argstr='--out=%s') + out_corrected = GenFile( + template='{in_files[0]}_corrected{output_type_}', argstr='--out=%s', + desc='output (warped) image') method = traits.Enum('jac', 'lsr', argstr='--method=%s', desc=('use jacobian modulation (jac) or least-squares' ' resampling (lsr)')) @@ -338,6 +269,11 @@ class ApplyTOPUPInputSpec(FSLCommandInputSpec): argstr='-d=%s', desc='force output data type') + def _format_arg(self, name, spec, value): + if name == 'in_topup_fieldcoef': + return spec.argstr % value.split('_fieldcoef')[0] + return super(ApplyTOPUPInputSpec, self)._format_arg(name, spec, value) + class ApplyTOPUPOutputSpec(TraitedSpec): out_corrected = File(exists=True, desc=('name of 4D image file with ' 'unwarped images')) @@ -374,11 +310,6 @@ class ApplyTOPUP(FSLCommand): input_spec = ApplyTOPUPInputSpec output_spec = ApplyTOPUPOutputSpec - def _format_arg(self, name, spec, value): - if name == 'in_topup_fieldcoef': - return spec.argstr % value.split('_fieldcoef')[0] - return super(ApplyTOPUP, self)._format_arg(name, spec, value) - class EddyInputSpec(FSLCommandInputSpec): in_file = File(exists=True, mandatory=True, argstr='--imain=%s', @@ -458,7 +389,7 @@ class Eddy(FSLCommand): >>> eddy.cmdline #doctest: +ELLIPSIS 'eddy --acqp=epi_acqp.txt --bvals=bvals.scheme --bvecs=bvecs.scheme \ --imain=epi.nii --index=epi_index.txt --mask=epi_mask.nii \ ---out=.../eddy_corrected' +--out=eddy_corrected' >>> res = eddy.run() # doctest: +SKIP """ @@ -492,11 +423,10 @@ def _format_arg(self, name, spec, value): return spec.argstr % os.path.abspath(value) return super(Eddy, self)._format_arg(name, spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_corrected'] = os.path.abspath('%s.nii.gz' % self.inputs.out_base) - outputs['out_parameter'] = os.path.abspath('%s.eddy_parameters' % self.inputs.out_base) - return outputs + def _post_run(self): + + self.outputs.out_corrected = os.path.abspath('%s.nii.gz' % self.inputs.out_base) + self.outputs.out_parameter = os.path.abspath('%s.eddy_parameters' % self.inputs.out_base) class SigLossInputSpec(FSLCommandInputSpec): @@ -504,9 +434,9 @@ class SigLossInputSpec(FSLCommandInputSpec): exists=True, argstr='-i %s', desc='b0 fieldmap file') - out_file = File(argstr='-s %s', - desc='output signal loss estimate file', - genfile=True) + out_file = GenFile( + template='{in_file}_sigloss{output_type_}', argstr='-s %s', + desc='output signal loss estimate file') mask_file = File(exists=True, argstr='-m %s', @@ -536,7 +466,7 @@ class SigLoss(FSLCommand): >>> sigloss.inputs.echo_time = 0.03 >>> sigloss.inputs.output_type = "NIFTI_GZ" >>> sigloss.cmdline #doctest: +ELLIPSIS - 'sigloss --te=0.030000 -i phase.nii -s .../phase_sigloss.nii.gz' + 'sigloss --te=0.030000 -i phase.nii -s phase_sigloss.nii.gz' >>> res = sigloss.run() # doctest: +SKIP @@ -545,18 +475,17 @@ class SigLoss(FSLCommand): output_spec = SigLossOuputSpec _cmd = 'sigloss' - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if ((not isdefined(outputs['out_file'])) and + def _post_run(self): + + self.outputs.out_file = self.inputs.out_file + if ((not isdefined(self.outputs.out_file)) and (isdefined(self.inputs.in_file))): - outputs['out_file'] = self._gen_fname(self.inputs.in_file, + self.outputs.out_file = self._gen_fname(self.inputs.in_file, suffix='_sigloss') - return outputs def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()['out_file'] + return self.outputs.out_file return None @@ -586,12 +515,38 @@ class EpiRegInputSpec(FSLCommandInputSpec): weight_image = File(exists=True, argstr='--weight=%s', desc='weighting image (in T1 space)') - no_fmapreg = traits.Bool(False, argstr='--nofmapreg', - desc='do not perform registration of fmap to T1 \ - (use if fmap already registered)') + no_fmapreg = traits.Bool(False, usedefault=True, argstr='--nofmapreg', + desc='do not perform registration of fmap to T1 ' + '(use if fmap already registered).') no_clean = traits.Bool(True, argstr='--noclean', usedefault=True, desc='do not clean up intermediate files') + out_file = GenFile(template='{out_base}{output_type_}', keep_extension=False, + desc='output file name') + epi2str_mat = GenFile(template='{out_base}.mat', keep_extension=False, + desc='rigid epi-to-structural transform') + wmedge = GenFile(template='{out_base}_fast_wmedge{output_type_}', keep_extension=False, + desc='output file name') + wmseg = GenFile(template='{out_base}_fast_wmseg{output_type_}', keep_extension=False, + desc='output file name') + # Optional outputs + out_1vol = GenFile(template='{out_base}_1vol{output_type_}', keep_extension=False, + desc='output file name') + fmap2str_mat = GenFile(template='{out_base}_fieldmap2str.mat', keep_extension=False, + desc='output file name') + fmap2epi_mat = GenFile(template='{out_base}_fieldmaprads2epi.mat', keep_extension=False, + desc='output file name') + fmap_epi = GenFile(template='{out_base}_fieldmaprads2epi{output_type_}', keep_extension=False, + desc='output file name') + fmap_str = GenFile(template='{out_base}_fieldmaprads2str{output_type_}', keep_extension=False, + desc='output file name') + shiftmap = GenFile(template='{out_base}_fieldmaprads2epi_shift{output_type_}', + keep_extension=False, desc='output file name') + fullwarp = GenFile(template='{out_base}_warp{output_type_}', keep_extension=False, + desc='output file name') + epi2str_inv = GenFile(template='{out_base}_inv.mat', keep_extension=False, + desc='output file name') + class EpiRegOutputSpec(TraitedSpec): out_file = File(exists=True, @@ -617,6 +572,18 @@ class EpiRegOutputSpec(TraitedSpec): wmseg = File(exists=True, desc='white matter segmentation used in flirt bbr') wmedge = File(exists=True, desc='white matter edges for visualization') + def _post_run(self): + if self.inputs.no_fmapreg or not isdefined(self.inputs.fmap): + self.outputs.out_1vol = Undefined + self.outputs.fmap2str_mat = Undefined + self.outputs.fmap2epi_mat = Undefined + self.outputs.fmap_epi = Undefined + self.outputs.fmap_str = Undefined + self.outputs.fmapmag_str = Undefined + self.outputs.shiftmap = Undefined + self.outputs.fullwarp = Undefined + self.outputs.epi2str_inv = Undefined + class EpiReg(FSLCommand): """ @@ -649,87 +616,32 @@ class EpiReg(FSLCommand): input_spec = EpiRegInputSpec output_spec = EpiRegOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = os.path.join(os.getcwd(), - self.inputs.out_base + '.nii.gz') - if not (isdefined(self.inputs.no_fmapreg) and self.inputs.no_fmapreg) and isdefined(self.inputs.fmap): - outputs['out_1vol'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_1vol.nii.gz') - outputs['fmap2str_mat'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmap2str.mat') - outputs['fmap2epi_mat'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmaprads2epi.mat') - outputs['fmap_epi'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmaprads2epi.nii.gz') - outputs['fmap_str'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmaprads2str.nii.gz') - outputs['fmapmag_str'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmap2str.nii.gz') - outputs['shiftmap'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fieldmaprads2epi_shift.nii.gz') - outputs['fullwarp'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_warp.nii.gz') - outputs['epi2str_inv'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_inv.mat') - - outputs['epi2str_mat'] = os.path.join(os.getcwd(), - self.inputs.out_base + '.mat') - outputs['wmedge'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fast_wmedge.nii.gz') - outputs['wmseg'] = os.path.join(os.getcwd(), - self.inputs.out_base + '_fast_wmseg.nii.gz') - - return outputs +# Helper functions ------------------------ +def topup_generate_encfile(durations, encoding_direction, out_file): + """Generate a topup compatible encoding file based on given directions + """ + if len(encoding_direction) != len(durations): + if len(durations) != 1: + raise ValueError('Readout time must be a float or match the ' + 'length of encoding directions') + durations = durations * len(encoding_direction) + + lines = [] + for idx, encdir in enumerate(encoding_direction): + direction = 1.0 + if encdir.endswith('-'): + direction = -1.0 + line = [float(val[0] == encdir[0]) * direction + for val in ['x', 'y', 'z']] + [durations[idx]] + lines.append(line) + np.savetxt(out_file, np.array(lines), fmt='%d %d %d %.8f') ####################################### # deprecated interfaces ####################################### -class EPIDeWarpInputSpec(FSLCommandInputSpec): - mag_file = File(exists=True, - desc='Magnitude file', - argstr='--mag %s', position=0, mandatory=True) - dph_file = File(exists=True, - desc='Phase file assumed to be scaled from 0 to 4095', - argstr='--dph %s', mandatory=True) - exf_file = File(exists=True, - desc='example func volume (or use epi)', - argstr='--exf %s') - epi_file = File(exists=True, - desc='EPI volume to unwarp', - argstr='--epi %s') - tediff = traits.Float(2.46, usedefault=True, - desc='difference in B0 field map TEs', - argstr='--tediff %s') - esp = traits.Float(0.58, desc='EPI echo spacing', - argstr='--esp %s', usedefault=True) - sigma = traits.Int(2, usedefault=True, argstr='--sigma %s', - desc="2D spatial gaussing smoothing \ - stdev (default = 2mm)") - vsm = traits.String(genfile=True, desc='voxel shift map', - argstr='--vsm %s') - exfdw = traits.String(desc='dewarped example func volume', genfile=True, - argstr='--exfdw %s') - epidw = traits.String(desc='dewarped epi volume', genfile=False, - argstr='--epidw %s') - tmpdir = traits.String(genfile=True, desc='tmpdir', - argstr='--tmpdir %s') - nocleanup = traits.Bool(True, usedefault=True, desc='no cleanup', - argstr='--nocleanup') - cleanup = traits.Bool(desc='cleanup', - argstr='--cleanup') - - -class EPIDeWarpOutputSpec(TraitedSpec): - unwarped_file = File(desc="unwarped epi file") - vsm_file = File(desc="voxel shift map") - exfdw = File(desc="dewarped functional volume example") - exf_mask = File(desc="Mask from example functional volume") - - class EPIDeWarp(FSLCommand): """ Wraps the unwarping script `epidewarp.fsl @@ -738,94 +650,18 @@ class EPIDeWarp(FSLCommand): .. warning:: deprecated in FSL, please use :func:`nipype.workflows.dmri.preprocess.epi.sdc_fmb` instead. - Examples - -------- - - >>> from nipype.interfaces.fsl import EPIDeWarp - >>> dewarp = EPIDeWarp() - >>> dewarp.inputs.epi_file = "functional.nii" - >>> dewarp.inputs.mag_file = "magnitude.nii" - >>> dewarp.inputs.dph_file = "phase.nii" - >>> dewarp.inputs.output_type = "NIFTI_GZ" - >>> dewarp.cmdline #doctest: +ELLIPSIS - 'epidewarp.fsl --mag magnitude.nii --dph phase.nii --epi functional.nii \ ---esp 0.58 --exfdw .../exfdw.nii.gz --nocleanup --sigma 2 --tediff 2.46 \ ---tmpdir .../temp --vsm .../vsm.nii.gz' - >>> res = dewarp.run() # doctest: +SKIP - + >>> from nipype.interfaces import fsl + >>> fsl.EPIDeWarp() + Traceback (most recent call last): + ... + NotImplementedError: deprecated, please use nipype.workflows.dmri.preprocess.epi.sdc_fmb instead """ _cmd = 'epidewarp.fsl' - input_spec = EPIDeWarpInputSpec - output_spec = EPIDeWarpOutputSpec def __init__(self, **inputs): - warnings.warn(("Deprecated: Please use " - "nipype.workflows.dmri.preprocess.epi.sdc_fmb instead"), - DeprecationWarning) - return super(EPIDeWarp, self).__init__(**inputs) - - def _run_interface(self, runtime): - runtime = super(EPIDeWarp, self)._run_interface(runtime) - if runtime.stderr: - self.raise_exception(runtime) - return runtime - - def _gen_filename(self, name): - if name == 'exfdw': - if isdefined(self.inputs.exf_file): - return self._gen_fname(self.inputs.exf_file, - suffix="_exfdw") - else: - return self._gen_fname("exfdw") - if name == 'epidw': - if isdefined(self.inputs.epi_file): - return self._gen_fname(self.inputs.epi_file, - suffix="_epidw") - if name == 'vsm': - return self._gen_fname('vsm') - if name == 'tmpdir': - return os.path.join(os.getcwd(), 'temp') - return None - - def _list_outputs(self): - outputs = self.output_spec().get() - if not isdefined(self.inputs.exfdw): - outputs['exfdw'] = self._gen_filename('exfdw') - else: - outputs['exfdw'] = self.inputs.exfdw - if isdefined(self.inputs.epi_file): - if isdefined(self.inputs.epidw): - outputs['unwarped_file'] = self.inputs.epidw - else: - outputs['unwarped_file'] = self._gen_filename('epidw') - if not isdefined(self.inputs.vsm): - outputs['vsm_file'] = self._gen_filename('vsm') - else: - outputs['vsm_file'] = self._gen_fname(self.inputs.vsm) - if not isdefined(self.inputs.tmpdir): - outputs[ - 'exf_mask'] = self._gen_fname(cwd=self._gen_filename('tmpdir'), - basename='maskexf') - else: - outputs['exf_mask'] = self._gen_fname(cwd=self.inputs.tmpdir, - basename='maskexf') - return outputs - - -class EddyCorrectInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, desc='4D input file', argstr='%s', position=0, - mandatory=True) - out_file = File(desc='4D output file', argstr='%s', position=1, - name_source=['in_file'], name_template='%s_edc', - output_name='eddy_corrected') - ref_num = traits.Int(0, argstr='%d', position=2, desc='reference number', - mandatory=True, usedefault=True) - - -class EddyCorrectOutputSpec(TraitedSpec): - eddy_corrected = File(exists=True, - desc='path/name of 4D eddy corrected output file') + raise NotImplementedError( + 'deprecated, please use nipype.workflows.dmri.preprocess.epi.sdc_fmb instead') class EddyCorrect(FSLCommand): @@ -834,27 +670,16 @@ class EddyCorrect(FSLCommand): .. warning:: Deprecated in FSL. Please use :class:`nipype.interfaces.fsl.epi.Eddy` instead - Example - ------- - - >>> from nipype.interfaces.fsl import EddyCorrect - >>> eddyc = EddyCorrect(in_file='diffusion.nii', - ... out_file="diffusion_edc.nii", ref_num=0) - >>> eddyc.cmdline - 'eddy_correct diffusion.nii diffusion_edc.nii 0' + >>> from nipype.interfaces import fsl + >>> fsl.EddyCorrect() + Traceback (most recent call last): + ... + NotImplementedError: deprecated, please use nipype.interfaces.fsl.epi.Eddy instead """ _cmd = 'eddy_correct' - input_spec = EddyCorrectInputSpec - output_spec = EddyCorrectOutputSpec def __init__(self, **inputs): - warnings.warn(("Deprecated: Please use nipype.interfaces.fsl.epi.Eddy " - "instead"), DeprecationWarning) - return super(EddyCorrect, self).__init__(**inputs) + raise NotImplementedError( + 'deprecated, please use nipype.interfaces.fsl.epi.Eddy instead') - def _run_interface(self, runtime): - runtime = super(EddyCorrect, self)._run_interface(runtime) - if runtime.stderr: - self.raise_exception(runtime) - return runtime diff --git a/nipype/interfaces/fsl/maths.py b/nipype/interfaces/fsl/maths.py index c22874d13f..a7cbfb2f6b 100644 --- a/nipype/interfaces/fsl/maths.py +++ b/nipype/interfaces/fsl/maths.py @@ -12,56 +12,42 @@ """ from __future__ import division -import os import numpy as np -from ..base import (TraitedSpec, File, traits, InputMultiPath, isdefined) +from ..base import (TraitedSpec, File, GenFile, traits, InputMultiPath, isdefined) from ..fsl.base import FSLCommand, FSLCommandInputSpec class MathsInput(FSLCommandInputSpec): - in_file = File(position=2, argstr="%s", exists=True, mandatory=True, desc="image to operate on") - out_file = File(genfile=True, position=-2, argstr="%s", desc="image to write", hash_files=False) + out_file = GenFile( + template='{in_file}_maths{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") _dtypes = ["float", "char", "int", "short", "double", "input"] internal_datatype = traits.Enum(*_dtypes, position=1, argstr="-dt %s", desc="datatype to use for calculations (default is float)") output_datatype = traits.Enum(*_dtypes, position=-1, argstr="-odt %s", desc="datatype to use for output (default uses input type)") - - nan2zeros = traits.Bool(position=3, argstr='-nan', + nan2zeros = traits.Bool(False, usedefault=True, position=3, argstr='-nan', desc='change NaNs to zeros before doing anything') class MathsOutput(TraitedSpec): - out_file = File(exists=True, desc="image written after calculations") class MathsCommand(FSLCommand): - _cmd = "fslmaths" input_spec = MathsInput output_spec = MathsOutput - _suffix = "_maths" - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs["out_file"] = self.inputs.out_file - if not isdefined(self.inputs.out_file): - outputs["out_file"] = self._gen_fname(self.inputs.in_file, suffix=self._suffix) - outputs["out_file"] = os.path.abspath(outputs["out_file"]) - return outputs - - def _gen_filename(self, name): - if name == "out_file": - return self._list_outputs()["out_file"] - return None class ChangeDataTypeInput(MathsInput): + out_file = GenFile( + template='{in_file}_chdt{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") _dtypes = ["float", "char", "int", "short", "double", "input"] output_datatype = traits.Enum(*_dtypes, @@ -70,15 +56,14 @@ class ChangeDataTypeInput(MathsInput): class ChangeDataType(MathsCommand): - """Use fslmaths to change the datatype of an image. - - """ + """Use fslmaths to change the datatype of an image.""" input_spec = ChangeDataTypeInput - _suffix = "_chdt" class ThresholdInputSpec(MathsInput): - + out_file = GenFile( + template='{in_file}_thresh{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") thresh = traits.Float(mandatory=True, position=4, argstr="%s", desc="threshold value") direction = traits.Enum("below", "above", usedefault=True, @@ -87,53 +72,50 @@ class ThresholdInputSpec(MathsInput): use_nonzero_voxels = traits.Bool(desc="use nonzero voxels to calculate robust range", requires=["use_robust_range"]) - -class Threshold(MathsCommand): - """Use fslmaths to apply a threshold to an image in a variety of ways. - - """ - input_spec = ThresholdInputSpec - _suffix = "_thresh" - def _format_arg(self, name, spec, value): if name == "thresh": arg = "-" - _si = self.inputs - if self.inputs.direction == "above": + if self.direction == "above": arg += "u" arg += "thr" - if isdefined(_si.use_robust_range) and _si.use_robust_range: - if isdefined(_si.use_nonzero_voxels) and _si.use_nonzero_voxels: + if isdefined(self.use_robust_range) and self.use_robust_range: + if isdefined(self.use_nonzero_voxels) and self.use_nonzero_voxels: arg += "P" else: arg += "p" arg += " %.10f" % value return arg - return super(Threshold, self)._format_arg(name, spec, value) + return super(ThresholdInputSpec, self)._format_arg(name, spec, value) +class Threshold(MathsCommand): + """Use fslmaths to apply a threshold to an image in a variety of ways.""" + input_spec = ThresholdInputSpec -class MeanImageInput(MathsInput): +class MeanImageInput(MathsInput): + out_file = GenFile( + template='{in_file}_mean{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") dimension = traits.Enum("T", "X", "Y", "Z", usedefault=True, argstr="-%smean", position=4, desc="dimension to mean across") class MeanImage(MathsCommand): - """Use fslmaths to generate a mean image across a given dimension. - - """ + """Use fslmaths to generate a mean image across a given dimension.""" input_spec = MeanImageInput - _suffix = "_mean" class MaxImageInput(MathsInput): - + out_file = GenFile( + template='{in_file}_max{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") dimension = traits.Enum("T", "X", "Y", "Z", usedefault=True, argstr="-%smax", position=4, desc="dimension to max across") class MaxImage(MathsCommand): - """Use fslmaths to generate a max image across a given dimension. + """ + Use fslmaths to generate a max image across a given dimension. Examples -------- @@ -146,47 +128,42 @@ class MaxImage(MathsCommand): """ input_spec = MaxImageInput - _suffix = "_max" class IsotropicSmoothInput(MathsInput): - + out_file = GenFile( + template='{in_file}_smooth{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") fwhm = traits.Float(mandatory=True, xor=["sigma"], position=4, argstr="-s %.5f", desc="fwhm of smoothing kernel [mm]") sigma = traits.Float(mandatory=True, xor=["fwhm"], position=4, argstr="-s %.5f", desc="sigma of smoothing kernel [mm]") - -class IsotropicSmooth(MathsCommand): - """Use fslmaths to spatially smooth an image with a gaussian kernel. - - """ - input_spec = IsotropicSmoothInput - _suffix = "_smooth" - def _format_arg(self, name, spec, value): if name == "fwhm": sigma = float(value) / np.sqrt(8 * np.log(2)) return spec.argstr % sigma - return super(IsotropicSmooth, self)._format_arg(name, spec, value) + return super(IsotropicSmoothInput, self)._format_arg(name, spec, value) +class IsotropicSmooth(MathsCommand): + """Use fslmaths to spatially smooth an image with a gaussian kernel.""" + input_spec = IsotropicSmoothInput -class ApplyMaskInput(MathsInput): +class ApplyMaskInput(MathsInput): + out_file = GenFile( + template='{in_file}_masked{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") mask_file = File(exists=True, mandatory=True, argstr="-mas %s", position=4, desc="binary image defining mask space") class ApplyMask(MathsCommand): - """Use fslmaths to apply a binary mask to another image. - - """ + """Use fslmaths to apply a binary mask to another image.""" input_spec = ApplyMaskInput - _suffix = "_masked" class KernelInput(MathsInput): - kernel_shape = traits.Enum("3D", "2D", "box", "boxv", "gauss", "sphere", "file", argstr="-kernel %s", position=4, desc="kernel shape to use") kernel_size = traits.Float(argstr="%.4f", position=5, xor=["kernel_file"], @@ -196,61 +173,59 @@ class KernelInput(MathsInput): class DilateInput(KernelInput): - + out_file = GenFile( + template='{in_file}_dil{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") operation = traits.Enum("mean", "modal", "max", argstr="-dil%s", position=6, mandatory=True, desc="filtering operation to perfoem in dilation") - -class DilateImage(MathsCommand): - """Use fslmaths to perform a spatial dilation of an image. - - """ - input_spec = DilateInput - _suffix = "_dil" - def _format_arg(self, name, spec, value): if name == "operation": return spec.argstr % dict(mean="M", modal="D", max="F")[value] - return super(DilateImage, self)._format_arg(name, spec, value) - + return super(DilateInput, self)._format_arg(name, spec, value) -class ErodeInput(KernelInput): - - minimum_filter = traits.Bool(argstr="%s", position=6, usedefault=True, default_value=False, - desc="if true, minimum filter rather than erosion by zeroing-out") +class DilateImage(MathsCommand): + """Use fslmaths to perform a spatial dilation of an image.""" + input_spec = DilateInput -class ErodeImage(MathsCommand): - """Use fslmaths to perform a spatial erosion of an image. - """ - input_spec = ErodeInput - _suffix = "_ero" +class ErodeInput(KernelInput): + out_file = GenFile( + template='{in_file}_ero{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") + minimum_filter = traits.Bool( + False, argstr="-eroF", position=6, usedefault=True, + desc="if true, minimum filter rather than erosion by zeroing-out") def _format_arg(self, name, spec, value): if name == "minimum_filter": - if value: - return "-eroF" - return "-ero" - return super(ErodeImage, self)._format_arg(name, spec, value) + if not value: + return "-ero" + return super(ErodeInput, self)._format_arg(name, spec, value) +class ErodeImage(MathsCommand): + """Use fslmaths to perform a spatial erosion of an image.""" + input_spec = ErodeInput -class SpatialFilterInput(KernelInput): +class SpatialFilterInput(KernelInput): + out_file = GenFile( + template='{in_file}_{operation}{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") operation = traits.Enum("mean", "median", "meanu", argstr="-f%s", position=6, mandatory=True, desc="operation to filter with") class SpatialFilter(MathsCommand): - """Use fslmaths to spatially filter an image. - - """ + """Use fslmaths to spatially filter an image.""" input_spec = SpatialFilterInput - _suffix = "_filt" class UnaryMathsInput(MathsInput): - + out_file = GenFile( + template='{in_file}_{operation}{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") operation = traits.Enum("exp", "log", "sin", "cos", "tan", "asin", "acos", "atan", "sqr", "sqrt", "recip", "abs", "bin", "binv", "fillh", "fillh26", "index", "edge", "nan", "nanm", "rand", "randn", "range", @@ -259,18 +234,11 @@ class UnaryMathsInput(MathsInput): class UnaryMaths(MathsCommand): - """Use fslmaths to perorm a variety of mathematical operations on an image. - - """ + """Use fslmaths to perorm a variety of mathematical operations on an image.""" input_spec = UnaryMathsInput - def _list_outputs(self): - self._suffix = "_" + self.inputs.operation - return super(UnaryMaths, self)._list_outputs() - class BinaryMathsInput(MathsInput): - operation = traits.Enum("add", "sub", "mul", "div", "rem", "max", "min", mandatory=True, argstr="-%s", position=4, desc="operation to perform") @@ -281,19 +249,23 @@ class BinaryMathsInput(MathsInput): class BinaryMaths(MathsCommand): - """Use fslmaths to perform mathematical operations using a second image or a numeric value. - + """ + Use fslmaths to perform mathematical operations using a second + image or a numeric value. """ input_spec = BinaryMathsInput class MultiImageMathsInput(MathsInput): - op_string = traits.String(position=4, argstr="%s", mandatory=True, desc="python formatted string of operations to perform") operand_files = InputMultiPath(File(exists=True), mandatory=True, desc="list of file names to plug into op string") + def _format_arg(self, name, spec, value): + if name == "op_string": + return value % tuple(self.operand_files) + return super(MultiImageMathsInput, self)._format_arg(name, spec, value) class MultiImageMaths(MathsCommand): """Use fslmaths to perform a sequence of mathematical operations. @@ -305,21 +277,17 @@ class MultiImageMaths(MathsCommand): >>> maths.inputs.in_file = "functional.nii" >>> maths.inputs.op_string = "-add %s -mul -1 -div %s" >>> maths.inputs.operand_files = ["functional2.nii", "functional3.nii"] - >>> maths.inputs.out_file = "functional4.nii" >>> maths.cmdline - 'fslmaths functional.nii -add functional2.nii -mul -1 -div functional3.nii functional4.nii' + 'fslmaths functional.nii -add functional2.nii -mul -1 -div functional3.nii functional_maths.nii.gz' """ input_spec = MultiImageMathsInput - def _format_arg(self, name, spec, value): - if name == "op_string": - return value % tuple(self.inputs.operand_files) - return super(MultiImageMaths, self)._format_arg(name, spec, value) - class TemporalFilterInput(MathsInput): - + out_file = GenFile( + template='{in_file}_filt{output_type_}', position=-2, argstr="%s", hash_files=False, + desc="image to write") lowpass_sigma = traits.Float(-1, argstr="%.6f", position=5, usedefault=True, desc="lowpass filter sigma (in volumes)") highpass_sigma = traits.Float(-1, argstr="-bptf %.6f", position=4, usedefault=True, @@ -327,8 +295,5 @@ class TemporalFilterInput(MathsInput): class TemporalFilter(MathsCommand): - """Use fslmaths to apply a low, high, or bandpass temporal filter to a timeseries. - - """ + """Use fslmaths to apply a low, high, or bandpass temporal filter to a timeseries. """ input_spec = TemporalFilterInput - _suffix = "_filt" diff --git a/nipype/interfaces/fsl/model.py b/nipype/interfaces/fsl/model.py index 3d07fa21de..d953ca8b40 100644 --- a/nipype/interfaces/fsl/model.py +++ b/nipype/interfaces/fsl/model.py @@ -25,14 +25,15 @@ from ... import LooseVersion from .base import (FSLCommand, FSLCommandInputSpec, Info) -from ..base import (load_template, File, traits, isdefined, +from ..base import (load_template, File, GenFile, traits, isdefined, TraitedSpec, BaseInterface, Directory, InputMultiPath, OutputMultiPath, BaseInterfaceInputSpec) from ...utils.filemanip import (list_to_filename, filename_to_list) from ...utils.misc import human_order_sorted -warn = warnings.warn +from ... import logging +IFLOGGER = logging.getLogger('interface') class Level1DesignInputSpec(BaseInterfaceInputSpec): @@ -343,18 +344,18 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + cwd = os.getcwd() - outputs['fsf_files'] = [] - outputs['ev_files'] = [] + self.outputs.fsf_files = [] + self.outputs.ev_files = [] usetd = 0 basis_key = list(self.inputs.bases.keys())[0] if basis_key in ['dgamma', 'gamma']: usetd = int(self.inputs.bases[basis_key]['derivs']) for runno, runinfo in enumerate(self._format_session_info(self.inputs.session_info)): - outputs['fsf_files'].append(os.path.join(cwd, 'run%d.fsf' % runno)) - outputs['ev_files'].insert(runno, []) + self.outputs.fsf_files.append(os.path.join(cwd, 'run%d.fsf' % runno)) + self.outputs.ev_files.insert(runno, []) evname = [] for field in ['cond', 'regress']: for i, cond in enumerate(runinfo[field]): @@ -366,9 +367,8 @@ def _list_outputs(self): if field == 'cond': if usetd: evname.append(name + 'TD') - outputs['ev_files'][runno].append( + self.outputs.ev_files[runno].append( os.path.join(cwd, evfname)) - return outputs class FEATInputSpec(FSLCommandInputSpec): @@ -387,10 +387,9 @@ class FEAT(FSLCommand): input_spec = FEATInputSpec output_spec = FEATOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): is_ica = False - outputs['feat_dir'] = None + self.outputs.feat_dir = None with open(self.inputs.fsf_file, 'rt') as fp: text = fp.read() if "set fmri(inmelodic) 1" in text: @@ -400,17 +399,16 @@ def _list_outputs(self): try: outputdir_spec = line.split('"')[-2] if os.path.exists(outputdir_spec): - outputs['feat_dir'] = outputdir_spec + self.outputs.feat_dir = outputdir_spec except: pass - if not outputs['feat_dir']: + if not self.outputs.feat_dir: if is_ica: - outputs['feat_dir'] = glob(os.path.join(os.getcwd(), '*ica'))[0] + self.outputs.feat_dir = glob(os.path.join(os.getcwd(), '*ica'))[0] else: - outputs['feat_dir'] = glob(os.path.join(os.getcwd(), '*feat'))[0] - print('Outputs from FEATmodel:', outputs) - return outputs + self.outputs.feat_dir = glob(os.path.join(os.getcwd(), '*feat'))[0] + IFLOGGER.info('Outputs from FEATmodel: %s', self.outputs) class FEATModelInputSpec(FSLCommandInputSpec): @@ -422,8 +420,17 @@ class FEATModelInputSpec(FSLCommandInputSpec): desc="Event spec files generated by level1design", position=1, copyfile=False) + def _format_arg(self, name, trait_spec, value): + if name == 'fsf_file': + return super(FEATModelInputSpec, self)._format_arg( + name, trait_spec, self._get_design_root(value)) + elif name == 'ev_files': + return '' + else: + return super(FEATModelInputSpec, self)._format_arg(name, trait_spec, value) + -class FEATModelOutpuSpec(TraitedSpec): +class FEATModelOutputSpec(TraitedSpec): design_file = File( exists=True, desc='Mat file containing ascii matrix for design') design_image = File( @@ -440,43 +447,33 @@ class FEATModel(FSLCommand): """ _cmd = 'feat_model' input_spec = FEATModelInputSpec - output_spec = FEATModelOutpuSpec - - def _format_arg(self, name, trait_spec, value): - if name == 'fsf_file': - return super(FEATModel, self)._format_arg(name, trait_spec, self._get_design_root(value)) - elif name == 'ev_files': - return '' - else: - return super(FEATModel, self)._format_arg(name, trait_spec, value) + output_spec = FEATModelOutputSpec def _get_design_root(self, infile): _, fname = os.path.split(infile) return fname.split('.')[0] - def _list_outputs(self): + def _post_run(self): # TODO: figure out file names and get rid off the globs - outputs = self._outputs().get() root = self._get_design_root(list_to_filename(self.inputs.fsf_file)) design_file = glob(os.path.join(os.getcwd(), '%s*.mat' % root)) assert len(design_file) == 1, 'No mat file generated by FEAT Model' - outputs['design_file'] = design_file[0] + self.outputs.design_file = design_file[0] design_image = glob(os.path.join(os.getcwd(), '%s.png' % root)) assert len( design_image) == 1, 'No design image generated by FEAT Model' - outputs['design_image'] = design_image[0] + self.outputs.design_image = design_image[0] design_cov = glob(os.path.join(os.getcwd(), '%s_cov.png' % root)) assert len( design_cov) == 1, 'No covariance image generated by FEAT Model' - outputs['design_cov'] = design_cov[0] + self.outputs.design_cov = design_cov[0] con_file = glob(os.path.join(os.getcwd(), '%s*.con' % root)) assert len(con_file) == 1, 'No con file generated by FEAT Model' - outputs['con_file'] = con_file[0] + self.outputs.con_file = con_file[0] fcon_file = glob(os.path.join(os.getcwd(), '%s*.fts' % root)) if fcon_file: assert len(fcon_file) == 1, 'No fts file generated by FEAT Model' - outputs['fcon_file'] = fcon_file[0] - return outputs + self.outputs.fcon_file = fcon_file[0] class FILMGLSInputSpec(FSLCommandInputSpec): @@ -693,24 +690,23 @@ def _get_numcons(self): fp.close() return numtcons, numfcons - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): cwd = os.getcwd() results_dir = os.path.join(cwd, self.inputs.results_dir) - outputs['results_dir'] = results_dir + self.outputs.results_dir = results_dir pe_files = self._get_pe_files(results_dir) if pe_files: - outputs['param_estimates'] = pe_files - outputs['residual4d'] = self._gen_fname('res4d.nii', cwd=results_dir) - outputs['dof_file'] = os.path.join(results_dir, 'dof') - outputs['sigmasquareds'] = self._gen_fname('sigmasquareds.nii', + self.outputs.param_estimates = pe_files + self.outputs.residual4d = self._gen_fname('res4d.nii', cwd=results_dir) + self.outputs.dof_file = os.path.join(results_dir, 'dof') + self.outputs.sigmasquareds = self._gen_fname('sigmasquareds.nii', cwd=results_dir) - outputs['thresholdac'] = self._gen_fname('threshac1.nii', + self.outputs.thresholdac = self._gen_fname('threshac1.nii', cwd=results_dir) if Info.version() and LooseVersion(Info.version()) < LooseVersion('5.0.7'): - outputs['corrections'] = self._gen_fname('corrections.nii', + self.outputs.corrections = self._gen_fname('corrections.nii', cwd=results_dir) - outputs['logfile'] = self._gen_fname('logfile', + self.outputs.logfile = self._gen_fname('logfile', change_ext=False, cwd=results_dir) @@ -734,10 +730,10 @@ def _list_outputs(self): tstats.append(self._gen_fname('tstat%d.nii' % (base_contrast + i), cwd=pth)) if copes: - outputs['copes'] = copes - outputs['varcopes'] = varcopes - outputs['zstats'] = zstats - outputs['tstats'] = tstats + self.outputs.copes = copes + self.outputs.varcopes = varcopes + self.outputs.zstats = zstats + self.outputs.tstats = tstats fstats = [] zfstats = [] for i in range(numfcons): @@ -747,9 +743,8 @@ def _list_outputs(self): self._gen_fname('zfstat%d.nii' % (base_contrast + i), cwd=pth)) if fstats: - outputs['fstats'] = fstats - outputs['zfstats'] = zfstats - return outputs + self.outputs.fstats = fstats + self.outputs.zfstats = zfstats class FEATRegisterInputSpec(BaseInterfaceInputSpec): @@ -793,11 +788,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['fsf_file'] = os.path.abspath( + def _post_run(self): + self.outputs.fsf_file = os.path.abspath( os.path.join(os.getcwd(), 'register.fsf')) - return outputs class FLAMEOInputSpec(FSLCommandInputSpec): @@ -908,67 +901,65 @@ def _run_interface(self, runtime): # ohinds: 2010-04-06 # made these compatible with flameo - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): pth = os.path.join(os.getcwd(), self.inputs.log_dir) pes = human_order_sorted(glob(os.path.join(pth, 'pe[0-9]*.*'))) assert len(pes) >= 1, 'No pe volumes generated by FSL Estimate' - outputs['pes'] = pes + self.outputs.pes = pes res4d = human_order_sorted(glob(os.path.join(pth, 'res4d.*'))) assert len(res4d) == 1, 'No residual volume generated by FSL Estimate' - outputs['res4d'] = res4d[0] + self.outputs.res4d = res4d[0] copes = human_order_sorted(glob(os.path.join(pth, 'cope[0-9]*.*'))) assert len(copes) >= 1, 'No cope volumes generated by FSL CEstimate' - outputs['copes'] = copes + self.outputs.copes = copes var_copes = human_order_sorted( glob(os.path.join(pth, 'varcope[0-9]*.*'))) assert len( var_copes) >= 1, 'No varcope volumes generated by FSL CEstimate' - outputs['var_copes'] = var_copes + self.outputs.var_copes = var_copes zstats = human_order_sorted(glob(os.path.join(pth, 'zstat[0-9]*.*'))) assert len(zstats) >= 1, 'No zstat volumes generated by FSL CEstimate' - outputs['zstats'] = zstats + self.outputs.zstats = zstats if isdefined(self.inputs.f_con_file): zfstats = human_order_sorted( glob(os.path.join(pth, 'zfstat[0-9]*.*'))) assert len( zfstats) >= 1, 'No zfstat volumes generated by FSL CEstimate' - outputs['zfstats'] = zfstats + self.outputs.zfstats = zfstats fstats = human_order_sorted( glob(os.path.join(pth, 'fstat[0-9]*.*'))) assert len( fstats) >= 1, 'No fstat volumes generated by FSL CEstimate' - outputs['fstats'] = fstats + self.outputs.fstats = fstats tstats = human_order_sorted(glob(os.path.join(pth, 'tstat[0-9]*.*'))) assert len(tstats) >= 1, 'No tstat volumes generated by FSL CEstimate' - outputs['tstats'] = tstats + self.outputs.tstats = tstats mrefs = human_order_sorted( glob(os.path.join(pth, 'mean_random_effects_var[0-9]*.*'))) assert len( mrefs) >= 1, 'No mean random effects volumes generated by FLAMEO' - outputs['mrefvars'] = mrefs + self.outputs.mrefvars = mrefs tdof = human_order_sorted(glob(os.path.join(pth, 'tdof_t[0-9]*.*'))) assert len(tdof) >= 1, 'No T dof volumes generated by FLAMEO' - outputs['tdof'] = tdof + self.outputs.tdof = tdof weights = human_order_sorted( glob(os.path.join(pth, 'weights[0-9]*.*'))) assert len(weights) >= 1, 'No weight volumes generated by FLAMEO' - outputs['weights'] = weights + self.outputs.weights = weights - outputs['stats_dir'] = pth + self.outputs.stats_dir = pth - return outputs class ContrastMgrInputSpec(FSLCommandInputSpec): @@ -1063,8 +1054,7 @@ def _get_numcons(self): fp.close() return numtcons, numfcons - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): pth, _ = os.path.split(self.inputs.sigmasquareds) numtcons, numfcons = self._get_numcons() base_contrast = 1 @@ -1088,11 +1078,11 @@ def _list_outputs(self): neffs.append(self._gen_fname('neff%d.nii' % (base_contrast + i), cwd=pth)) if copes: - outputs['copes'] = copes - outputs['varcopes'] = varcopes - outputs['zstats'] = zstats - outputs['tstats'] = tstats - outputs['neffs'] = neffs + self.outputs.copes = copes + self.outputs.varcopes = varcopes + self.outputs.zstats = zstats + self.outputs.tstats = tstats + self.outputs.neffs = neffs fstats = [] zfstats = [] for i in range(numfcons): @@ -1102,9 +1092,8 @@ def _list_outputs(self): self._gen_fname('zfstat%d.nii' % (base_contrast + i), cwd=pth)) if fstats: - outputs['fstats'] = fstats - outputs['zfstats'] = zfstats - return outputs + self.outputs.fstats = fstats + self.outputs.zfstats = zfstats class L2ModelInputSpec(BaseInterfaceInputSpec): @@ -1174,12 +1163,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): for field in list(outputs.keys()): - outputs[field] = os.path.join(os.getcwd(), - field.replace('_', '.')) - return outputs + setattr(self.outputs, field, os.path.join(os.getcwd(), field.replace('_', '.'))) class MultipleRegressDesignInputSpec(BaseInterfaceInputSpec): @@ -1323,25 +1309,22 @@ def _run_interface(self, runtime): 'design.grp': grp_txt} # write design files - for key, val in list(txt.items()): - if ('fts' in key) and (nfcons == 0): - continue - filename = key.replace('_', '.') - f = open(os.path.join(cwd, filename), 'wt') - f.write(val) - f.close() + with open(os.path.join(cwd, filename), 'wt') as out_file: + for key, val in list(txt.items()): + if ('fts' in key) and (nfcons == 0): + continue + filename = key.replace('_', '.') + + out_file.write(val) return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): nfcons = sum([1 for con in self.inputs.contrasts if con[1] == 'F']) - for field in list(outputs.keys()): + for field, _ in list(self.outputs.items()): if ('fts' in field) and (nfcons == 0): continue - outputs[field] = os.path.join(os.getcwd(), - field.replace('_', '.')) - return outputs + setattr(self.outputs, field, os.path.join(os.getcwd(), field.replace('_', '.'))) class SMMInputSpec(FSLCommandInputSpec): @@ -1361,26 +1344,24 @@ class SMMOutputSpec(TraitedSpec): class SMM(FSLCommand): - ''' + """ Spatial Mixture Modelling. For more detail on the spatial mixture modelling see Mixture Models with Adaptive Spatial Regularisation for Segmentation with an Application to FMRI Data; Woolrich, M., Behrens, T., Beckmann, C., and Smith, S.; IEEE Trans. Medical Imaging, 24(1):1-11, 2005. - ''' + """ _cmd = 'mm --ld=logdir' input_spec = SMMInputSpec output_spec = SMMOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): # TODO get the true logdir from the stdout - outputs['null_p_map'] = self._gen_fname(basename="w1_mean", + self.outputs.null_p_map = self._gen_fname(basename="w1_mean", cwd="logdir") - outputs['activation_p_map'] = self._gen_fname( + self.outputs.activation_p_map = self._gen_fname( basename="w2_mean", cwd="logdir") if not isdefined(self.inputs.no_deactivation_class) or not self.inputs.no_deactivation_class: - outputs['deactivation_p_map'] = self._gen_fname( + self.outputs.deactivation_p_map = self._gen_fname( basename="w3_mean", cwd="logdir") - return outputs class MELODICInputSpec(FSLCommandInputSpec): @@ -1500,15 +1481,14 @@ class MELODIC(FSLCommand): output_spec = MELODICOutputSpec _cmd = 'melodic' - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_dir'] = self.inputs.out_dir - if not isdefined(outputs['out_dir']): - outputs['out_dir'] = self._gen_filename("out_dir") + def _post_run(self): + + self.outputs.out_dir = self.inputs.out_dir + if not isdefined(self.outputs.out_dir): + self.outputs.out_dir = self._gen_filename("out_dir") if isdefined(self.inputs.report) and self.inputs.report: - outputs['report_dir'] = os.path.join( + self.outputs.report_dir = os.path.join( self._gen_filename("out_dir"), "report") - return outputs def _gen_filename(self, name): if name == "out_dir": @@ -1560,7 +1540,6 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): outputs.dlh = float(stdout[0].split()[1]) outputs.volume = int(stdout[1].split()[1]) outputs.resels = float(stdout[2].split()[1]) - return outputs class ClusterInputSpec(FSLCommandInputSpec): @@ -1569,30 +1548,6 @@ class ClusterInputSpec(FSLCommandInputSpec): threshold = traits.Float(argstr='--thresh=%.10f', mandatory=True, desc='threshold for input volume') - out_index_file = traits.Either(traits.Bool, File, - argstr='--oindex=%s', - desc='output of cluster index (in size order)', hash_files=False) - out_threshold_file = traits.Either(traits.Bool, File, - argstr='--othresh=%s', - desc='thresholded image', hash_files=False) - out_localmax_txt_file = traits.Either(traits.Bool, File, - argstr='--olmax=%s', - desc='local maxima text file', hash_files=False) - out_localmax_vol_file = traits.Either(traits.Bool, File, - argstr='--olmaxim=%s', - desc='output of local maxima volume', hash_files=False) - out_size_file = traits.Either(traits.Bool, File, - argstr='--osize=%s', - desc='filename for output of size image', hash_files=False) - out_max_file = traits.Either(traits.Bool, File, - argstr='--omax=%s', - desc='filename for output of max image', hash_files=False) - out_mean_file = traits.Either(traits.Bool, File, - argstr='--omean=%s', - desc='filename for output of mean image', hash_files=False) - out_pval_file = traits.Either(traits.Bool, File, - argstr='--opvals=%s', - desc='filename for image output of log pvals', hash_files=False) pthreshold = traits.Float(argstr='--pthresh=%.10f', requires=['dlh', 'volume'], desc='p-threshold for clusters') @@ -1624,15 +1579,55 @@ class ClusterInputSpec(FSLCommandInputSpec): desc='file contining warpfield') + out_index_file = GenFile( + template='{in_file}_index{output_type_}', argstr='--oindex=%s', hash_files=False, + desc='output of cluster index (in size order)', output_name='index_file') + out_threshold_file = GenFile( + template='{in_file}_threshold{output_type_}', argstr='--othresh=%s', hash_files=False, + desc='thresholded image') + out_localmax_txt_file = GenFile(template='{in_file}_localmax.txt', argstr='--olmax=%s', + hash_files=False, desc='local maxima text file') + out_localmax_vol_file = GenFile( + template='{in_file}_localmax{output_type_}', argstr='--olmaxim=%s', hash_files=False, + desc='output of local maxima volume') + out_size_file = GenFile(template='{in_file}_size{output_type_}', argstr='--osize=%s', + hash_files=False, desc='filename for output of size image') + out_max_file = GenFile(template='{in_file}_max{output_type_}', argstr='--omax=%s', + hash_files=False, desc='filename for output of max image') + out_mean_file = GenFile(template='{in_file}_mean{output_type_}', argstr='--omean=%s', + hash_files=False, desc='filename for output of mean image') + out_pval_file = GenFile(template='{in_file}_pval{output_type_}', argstr='--opvals=%s', + hash_files=False, desc='filename for image output of log pvals') + + save_index_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_threshold_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_localmax_txt_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_localmax_vol_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_size_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_max_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_mean_file = traits.Bool(False, usedefault=True, desc='enable this output') + save_pval_file = traits.Bool(False, usedefault=True, desc='enable this output') + + def parse_args(self, skip=None): + if skip is None: + skip = [] + + for name, _ in list(self.items()): + if not name.startswith('save_'): + continue + if not getattr(self, name): + skip += ['out_' + name[5:]] + return super(ClusterInputSpec, self).parse_args(skip) + class ClusterOutputSpec(TraitedSpec): index_file = File(desc='output of cluster index (in size order)') - threshold_file = File(desc='thresholded image') - localmax_txt_file = File(desc='local maxima text file') - localmax_vol_file = File(desc='output of local maxima volume') - size_file = File(desc='filename for output of size image') - max_file = File(desc='filename for output of max image') - mean_file = File(desc='filename for output of mean image') - pval_file = File(desc='filename for image output of log pvals') + out_threshold_file = File(desc='thresholded image') + out_localmax_txt_file = File(desc='local maxima text file') + out_localmax_vol_file = File(desc='output of local maxima volume') + out_size_file = File(desc='filename for output of size image') + out_max_file = File(desc='filename for output of max image') + out_mean_file = File(desc='filename for output of mean image') + out_pval_file = File(desc='filename for image output of log pvals') class Cluster(FSLCommand): @@ -1653,14 +1648,8 @@ class Cluster(FSLCommand): output_spec = ClusterOutputSpec _cmd = 'cluster' - filemap = {'out_index_file': 'index', 'out_threshold_file': 'threshold', - 'out_localmax_txt_file': 'localmax.txt', - 'out_localmax_vol_file': 'localmax', - 'out_size_file': 'size', 'out_max_file': 'max', - 'out_mean_file': 'mean', 'out_pval_file': 'pval'} + def _post_run(self): - def _list_outputs(self): - outputs = self.output_spec().get() for key, suffix in list(self.filemap.items()): outkey = key[4:] inval = getattr(self.inputs, key) @@ -1670,21 +1659,11 @@ def _list_outputs(self): change_ext = True if suffix.endswith('.txt'): change_ext = False - outputs[outkey] = self._gen_fname(self.inputs.in_file, + setattr(self.outputs, outkey, self._gen_fname(self.inputs.in_file, suffix='_' + suffix, - change_ext=change_ext) + change_ext=change_ext)) else: - outputs[outkey] = os.path.abspath(inval) - return outputs - - def _format_arg(self, name, spec, value): - if name in list(self.filemap.keys()): - if isinstance(value, bool): - fname = self._list_outputs()[name[4:]] - else: - fname = value - return spec.argstr % fname - return super(Cluster, self)._format_arg(name, spec, value) + setattr(self.outputs, outkey, os.path.abspath(inval)) class RandomiseInputSpec(FSLCommandInputSpec): @@ -1789,11 +1768,11 @@ class Randomise(FSLCommand): input_spec = RandomiseInputSpec output_spec = RandomiseOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['tstat_files'] = glob(self._gen_fname( + def _post_run(self): + + self.outputs.tstat_files = glob(self._gen_fname( '%s_tstat*.nii' % self.inputs.base_name)) - outputs['fstat_files'] = glob(self._gen_fname( + self.outputs.fstat_files = glob(self._gen_fname( '%s_fstat*.nii' % self.inputs.base_name)) prefix = False if self.inputs.tfce or self.inputs.tfce2D: @@ -1805,16 +1784,15 @@ def _list_outputs(self): elif self.inputs.cm_thresh or self.inputs.f_cm_thresh: prefix = 'clusterm' if prefix: - outputs['t_p_files'] = glob(self._gen_fname( + self.outputs.t_p_files = glob(self._gen_fname( '%s_%s_p_tstat*' % (self.inputs.base_name, prefix))) - outputs['t_corrected_p_files'] = glob(self._gen_fname( + self.outputs.t_corrected_p_files = glob(self._gen_fname( '%s_%s_corrp_tstat*.nii' % (self.inputs.base_name, prefix))) - outputs['f_p_files'] = glob(self._gen_fname( + self.outputs.f_p_files = glob(self._gen_fname( '%s_%s_p_fstat*.nii' % (self.inputs.base_name, prefix))) - outputs['f_corrected_p_files'] = glob(self._gen_fname( + self.outputs.f_corrected_p_files = glob(self._gen_fname( '%s_%s_corrp_fstat*.nii' % (self.inputs.base_name, prefix))) - return outputs class GLMInputSpec(FSLCommandInputSpec): @@ -1925,41 +1903,41 @@ class GLM(FSLCommand): input_spec = GLMInputSpec output_spec = GLMOutputSpec - def _list_outputs(self): + def _post_run(self): outputs = super(GLM, self)._list_outputs() if isdefined(self.inputs.out_cope): - outputs['out_cope'] = os.path.abspath(self.inputs.out_cope) + self.outputs.out_cope = os.path.abspath(self.inputs.out_cope) if isdefined(self.inputs.out_z_name): - outputs['out_z'] = os.path.abspath(self.inputs.out_z_name) + self.outputs.out_z = os.path.abspath(self.inputs.out_z_name) if isdefined(self.inputs.out_t_name): - outputs['out_t'] = os.path.abspath(self.inputs.out_t_name) + self.outputs.out_t = os.path.abspath(self.inputs.out_t_name) if isdefined(self.inputs.out_p_name): - outputs['out_p'] = os.path.abspath(self.inputs.out_p_name) + self.outputs.out_p = os.path.abspath(self.inputs.out_p_name) if isdefined(self.inputs.out_f_name): - outputs['out_f'] = os.path.abspath(self.inputs.out_f_name) + self.outputs.out_f = os.path.abspath(self.inputs.out_f_name) if isdefined(self.inputs.out_pf_name): - outputs['out_pf'] = os.path.abspath(self.inputs.out_pf_name) + self.outputs.out_pf = os.path.abspath(self.inputs.out_pf_name) if isdefined(self.inputs.out_res_name): - outputs['out_res'] = os.path.abspath(self.inputs.out_res_name) + self.outputs.out_res = os.path.abspath(self.inputs.out_res_name) if isdefined(self.inputs.out_varcb_name): - outputs['out_varcb'] = os.path.abspath(self.inputs.out_varcb_name) + self.outputs.out_varcb = os.path.abspath(self.inputs.out_varcb_name) if isdefined(self.inputs.out_sigsq_name): - outputs['out_sigsq'] = os.path.abspath(self.inputs.out_sigsq_name) + self.outputs.out_sigsq = os.path.abspath(self.inputs.out_sigsq_name) if isdefined(self.inputs.out_data_name): - outputs['out_data'] = os.path.abspath(self.inputs.out_data_name) + self.outputs.out_data = os.path.abspath(self.inputs.out_data_name) if isdefined(self.inputs.out_vnscales_name): - outputs['out_vnscales'] = os.path.abspath( + self.outputs.out_vnscales = os.path.abspath( self.inputs.out_vnscales_name) - return outputs + diff --git a/nipype/interfaces/fsl/preprocess.py b/nipype/interfaces/fsl/preprocess.py index 1f3137085e..46f73f74b9 100644 --- a/nipype/interfaces/fsl/preprocess.py +++ b/nipype/interfaces/fsl/preprocess.py @@ -13,53 +13,46 @@ from __future__ import print_function from __future__ import division -from builtins import range import os import os.path as op -import warnings - import numpy as np from nibabel import load +from builtins import range + +from ..base import (TraitedSpec, File, GenFile, GenMultiFile, + InputMultiPath, OutputMultiPath, traits, isdefined) from ..fsl.base import FSLCommand, FSLCommandInputSpec -from ..base import (TraitedSpec, File, InputMultiPath, - OutputMultiPath, Undefined, traits, - isdefined, OutputMultiPath) from ...utils.filemanip import split_filename -warn = warnings.warn +from ... import logging +IFLOGGER = logging.getLogger('interface') class BETInputSpec(FSLCommandInputSpec): # We use position args here as list indices - so a negative number # will put something on the end - in_file = File(exists=True, - desc='input file to skull strip', - argstr='%s', position=0, mandatory=True) - out_file = File(desc='name of output skull stripped image', - argstr='%s', position=1, genfile=True, hash_files=False) - outline = traits.Bool(desc='create surface outline image', - argstr='-o') - mask = traits.Bool(desc='create binary mask image', - argstr='-m') - skull = traits.Bool(desc='create skull image', - argstr='-s') - no_output = traits.Bool(argstr='-n', + in_file = File(exists=True, argstr='%s', position=0, mandatory=True, + desc='input file to skull strip') + outline = traits.Bool(False, usedefault=True, argstr='-o', + desc='create surface outline image') + mask = traits.Bool(False, usedefault=True, argstr='-m', + desc='create binary mask image') + skull = traits.Bool(False, usedefault=True, argstr='-s', + desc='create skull image') + no_output = traits.Bool(False, usedefault=True, argstr='-n', desc="Don't generate segmented output") - frac = traits.Float(desc='fractional intensity threshold', - argstr='-f %.2f') - vertical_gradient = traits.Float(argstr='-g %.2f', - desc='vertical gradient in fractional intensity ' - 'threshold (-1, 1)') - radius = traits.Int(argstr='-r %d', units='mm', - desc="head radius") - center = traits.List(traits.Int, desc='center of gravity in voxels', - argstr='-c %s', minlen=0, maxlen=3, - units='voxels') - threshold = traits.Bool(argstr='-t', + frac = traits.Float(desc='fractional intensity threshold', argstr='-f %.2f') + vertical_gradient = traits.Float( + argstr='-g %.2f', desc='vertical gradient in fractional intensity ' + 'threshold (-1, 1)') + radius = traits.Int(argstr='-r %d', units='mm', desc="head radius") + center = traits.List(traits.Int, argstr='-c %s', minlen=0, maxlen=3, units='voxels', + desc='center of gravity in voxels') + threshold = traits.Bool(False, usedefault=True, argstr='-t', desc="apply thresholding to segmented brain image and mask") - mesh = traits.Bool(argstr='-e', + mesh = traits.Bool(False, usedefault=True, argstr='-e', desc="generate a vtk mesh brain surface") # the remaining 'options' are more like modes (mutually exclusive) that # FSL actually implements in a shell script wrapper around the bet binary. @@ -68,9 +61,9 @@ class BETInputSpec(FSLCommandInputSpec): # supported _xor_inputs = ('functional', 'reduce_bias', 'robust', 'padding', 'remove_eyes', 'surfaces', 't2_guided') - robust = traits.Bool(desc='robust brain centre estimation ' - '(iterates BET several times)', - argstr='-R', xor=_xor_inputs) + robust = traits.Bool(False, usedefault=True, argstr='-R', xor=_xor_inputs, + desc='robust brain centre estimation ' + '(iterates BET several times)') padding = traits.Bool(desc='improve BET if FOV is very small in Z ' '(by temporarily padding end slices)', argstr='-Z', xor=_xor_inputs) @@ -89,30 +82,47 @@ class BETInputSpec(FSLCommandInputSpec): reduce_bias = traits.Bool(argstr='-B', xor=_xor_inputs, desc="bias field and neck cleanup") + # Automatically generated input names + out_file = GenFile(argstr='%s', position=1, template='{in_file}_brain{output_type_}', + hash_files=False, desc='name of output skull stripped image') + mask_file = GenFile(template='{in_file}_mask{output_type_}', + desc="path/name of binary brain mask") + meshfile = GenFile( + template='{in_file}_mesh.vtk', keep_extension=False, + desc="path/name of vtk mesh file") + outline_file = GenFile(template='{in_file}_overlay{output_type_}', + desc="path/name of outline file") + inskull_mask_file = GenFile(template='{in_file}_inskull_mask{output_type_}', + desc="path/name of inskull mask") + inskull_mesh_file = GenFile( + template='{in_file}_inskull_mesh.vtk', keep_extension=False, + desc="path/name of inskull mesh outline") + outskull_mask_file = GenFile(template='{in_file}_outskull_mask{output_type_}', + desc="path/name of outskull mask") + outskull_mesh_file = GenFile( + template='{in_file}_outskull_mesh.vtk', keep_extension=False, + desc="path/name of outskull mesh outline") + outskin_mask_file = GenFile(template='{in_file}_outskin_mask{output_type_}', + desc="path/name of outskin mask") + outskin_mesh_file = GenFile( + template='{in_file}_outskin_mesh.vtk', keep_extension=False, + desc="path/name of outskin mesh outline") + skull_mask_file = GenFile(template='{in_file}_skull_mask{output_type_}', + desc="path/name of skull mask") + class BETOutputSpec(TraitedSpec): - out_file = File( - desc="path/name of skullstripped file (if generated)") - mask_file = File( - desc="path/name of binary brain mask (if generated)") - outline_file = File( - desc="path/name of outline file (if generated)") - meshfile = File( - desc="path/name of vtk mesh file (if generated)") - inskull_mask_file = File( - desc="path/name of inskull mask (if generated)") - inskull_mesh_file = File( - desc="path/name of inskull mesh outline (if generated)") - outskull_mask_file = File( - desc="path/name of outskull mask (if generated)") - outskull_mesh_file = File( - desc="path/name of outskull mesh outline (if generated)") - outskin_mask_file = File( - desc="path/name of outskin mask (if generated)") - outskin_mesh_file = File( - desc="path/name of outskin mesh outline (if generated)") - skull_mask_file = File( - desc="path/name of skull mask (if generated)") + out_file = File(desc="path/name of skullstripped file") + mask_file = File(desc="path/name of binary brain mask") + meshfile = File(desc="path/name of vtk mesh file") + outline_file = File(desc="path/name of outline file") + inskull_mask_file = File(desc="path/name of inskull mask") + inskull_mesh_file = File(desc="path/name of inskull mesh outline") + outskull_mask_file = File(desc="path/name of outskull mask") + outskull_mesh_file = File(desc="path/name of outskull mesh outline") + outskin_mask_file = File(desc="path/name of outskin mask") + outskin_mesh_file = File(desc="path/name of outskin mesh outline") + skull_mask_file = File(desc="path/name of skull mask") class BET(FSLCommand): @@ -145,65 +155,13 @@ def _run_interface(self, runtime): self.raise_exception(runtime) return runtime - def _gen_outfilename(self): - out_file = self.inputs.out_file - if not isdefined(out_file) and isdefined(self.inputs.in_file): - out_file = self._gen_fname(self.inputs.in_file, - suffix='_brain') - return os.path.abspath(out_file) - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self._gen_outfilename() - if ((isdefined(self.inputs.mesh) and self.inputs.mesh) or - (isdefined(self.inputs.surfaces) and self.inputs.surfaces)): - outputs['meshfile'] = self._gen_fname(outputs['out_file'], - suffix='_mesh.vtk', - change_ext=False) - if (isdefined(self.inputs.mask) and self.inputs.mask) or \ - (isdefined(self.inputs.reduce_bias) and - self.inputs.reduce_bias): - outputs['mask_file'] = self._gen_fname(outputs['out_file'], - suffix='_mask') - if isdefined(self.inputs.outline) and self.inputs.outline: - outputs['outline_file'] = self._gen_fname(outputs['out_file'], - suffix='_overlay') - if isdefined(self.inputs.surfaces) and self.inputs.surfaces: - outputs['inskull_mask_file'] = self._gen_fname(outputs['out_file'], - suffix='_inskull_mask') - outputs['inskull_mesh_file'] = self._gen_fname(outputs['out_file'], - suffix='_inskull_mesh') - outputs[ - 'outskull_mask_file'] = self._gen_fname(outputs['out_file'], - suffix='_outskull_mask') - outputs[ - 'outskull_mesh_file'] = self._gen_fname(outputs['out_file'], - suffix='_outskull_mesh') - outputs['outskin_mask_file'] = self._gen_fname(outputs['out_file'], - suffix='_outskin_mask') - outputs['outskin_mesh_file'] = self._gen_fname(outputs['out_file'], - suffix='_outskin_mesh') - outputs['skull_mask_file'] = self._gen_fname(outputs['out_file'], - suffix='_skull_mask') - if isdefined(self.inputs.no_output) and self.inputs.no_output: - outputs['out_file'] = Undefined - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._gen_outfilename() - return None - class FASTInputSpec(FSLCommandInputSpec): """ Defines inputs (trait classes) for FAST """ - in_files = InputMultiPath(File(exists=True), copyfile=False, - desc='image, or multi-channel set of images, ' - 'to be segmented', - argstr='%s', position=-1, mandatory=True) - out_basename = File(desc='base name of output files', - argstr='-o %s') # uses in_file name as basename if none given - number_classes = traits.Range(low=1, high=10, argstr='-n %d', + in_files = InputMultiPath( + File(exists=True), copyfile=False, argstr='%s', position=-1, mandatory=True, + desc='image, or multi-channel set of images, to be segmented') + number_classes = traits.Range(low=1, high=10, argstr='-n %d', usedefault=True, default=3, desc='number of tissue-type classes') output_biasfield = traits.Bool(desc='output estimated bias field', argstr='-b') @@ -266,24 +224,61 @@ class FASTInputSpec(FSLCommandInputSpec): probability_maps = traits.Bool(desc='outputs individual probability maps', argstr='-p') + # Automatically generated names + out_basename = GenFile(template='{in_files[0]}', argstr='-o %s', keep_extension=False, + desc='base name of output files') + tissue_class_map = GenFile(template='{out_basename}_seg{output_type_}', + desc='binary segmented volume file one val for each class') + partial_volume_map = GenFile(template='{out_basename}_pveseg{output_type_}', + desc="segmentation corresponding to the partial volume files") + mixeltype = GenFile(template='{out_basename}_mixeltype{output_type_}', + desc="path/name of mixeltype volume file ") + + # Automatically generated lists of names, one element per in_files + restored_image = GenMultiFile( + template='{in_files}_restore{output_type_}', + desc='restored images (one for each input image) named according to the input images') + bias_field = GenMultiFile( + template='{in_files}_bias{output_type_}', desc='Estimated bias field') + + # Automatically generated lists of names, using range + tissue_class_files = GenMultiFile( + template='{out_basename}_seg_{number_classes:d}{output_type_}', range_source='number_classes', + desc='path/name of binary segmented volumes one file for each class _seg_x') + partial_volume_files = GenMultiFile( + template='{out_basename}_pve_{number_classes:d}{output_type_}', range_source='number_classes', + desc='path/name of partial volumes files one for each class, _pve_x') + probability_maps_files = GenMultiFile( + template='{out_basename}_prob_{number_classes:d}{output_type_}', range_source='number_classes', + desc='filenames, one for each class, for each input, prob_x', output_name='probability_maps') + + + def _format_arg(self, name, spec, value): + # first do what should be done in general + formatted = super(FASTInputSpec, self)._format_arg(name, spec, value) + if name == 'in_files': + # FAST needs the -S parameter value to correspond to the number + # of input images, otherwise it will ignore all but the first + formatted = "-S %d %s" % (len(value), formatted) + return formatted + class FASTOutputSpec(TraitedSpec): """Specify possible outputs from FAST""" - tissue_class_map = File(exists=True, - desc='path/name of binary segmented volume file' - ' one val for each class _seg') - tissue_class_files = OutputMultiPath(File(desc='path/name of binary segmented volumes ' - 'one file for each class _seg_x')) + tissue_class_map = File( + desc='path/name of binary segmented volume file one val for each class _seg') + partial_volume_map = File(desc="path/name of partial volume file _pveseg") + mixeltype = File(desc="path/name of mixeltype volume file _mixeltype") + restored_image = OutputMultiPath(File(desc='restored images (one for each input image) ' 'named according to the input images _restore')) + bias_field = OutputMultiPath(File(desc='Estimated bias field _bias')) - mixeltype = File(desc="path/name of mixeltype volume file _mixeltype") - partial_volume_map = File(desc="path/name of partial volume file _pveseg") + tissue_class_files = OutputMultiPath(File(desc='path/name of binary segmented volumes ' + 'one file for each class _seg_x')) partial_volume_files = OutputMultiPath(File(desc='path/name of partial volumes files ' 'one for each class, _pve_x')) - - bias_field = OutputMultiPath(File(desc='Estimated bias field _bias')) probability_maps = OutputMultiPath(File(desc='filenames, one for each class, for each ' 'input, prob_x')) @@ -310,17 +305,9 @@ class FAST(FSLCommand): input_spec = FASTInputSpec output_spec = FASTOutputSpec - def _format_arg(self, name, spec, value): - # first do what should be done in general - formated = super(FAST, self)._format_arg(name, spec, value) - if name == 'in_files': - # FAST needs the -S parameter value to correspond to the number - # of input images, otherwise it will ignore all but the first - formated = "-S %d %s" % (len(value), formated) - return formated + def _post_run(self): + - def _list_outputs(self): - outputs = self.output_spec().get() if not isdefined(self.inputs.number_classes): nclasses = 3 else: @@ -332,56 +319,52 @@ def _list_outputs(self): else: basefile = self.inputs.in_files[-1] - outputs['tissue_class_map'] = self._gen_fname(basefile, - suffix='_seg') if self.inputs.segments: - outputs['tissue_class_files'] = [] + self.outputs.tissue_class_files = [] for i in range(nclasses): - outputs['tissue_class_files'].append( + self.outputs.tissue_class_files.append( self._gen_fname(basefile, suffix='_seg_%d' % i)) if isdefined(self.inputs.output_biascorrected): - outputs['restored_image'] = [] + self.outputs.restored_image = [] if len(self.inputs.in_files) > 1: # for multi-image segmentation there is one corrected image # per input for val, f in enumerate(self.inputs.in_files): # image numbering is 1-based - outputs['restored_image'].append( + self.outputs.restored_image.append( self._gen_fname(basefile, suffix='_restore_%d' % (val + 1))) else: # single image segmentation has unnumbered output image - outputs['restored_image'].append( + self.outputs.restored_image.append( self._gen_fname(basefile, suffix='_restore')) - outputs['mixeltype'] = self._gen_fname(basefile, suffix='_mixeltype') if not self.inputs.no_pve: - outputs['partial_volume_map'] = self._gen_fname( + self.outputs.partial_volume_map = self._gen_fname( basefile, suffix='_pveseg') - outputs['partial_volume_files'] = [] + self.outputs.partial_volume_files = [] for i in range(nclasses): outputs[ 'partial_volume_files'].append(self._gen_fname(basefile, suffix='_pve_%d' % i)) if self.inputs.output_biasfield: - outputs['bias_field'] = [] + self.outputs.bias_field = [] if len(self.inputs.in_files) > 1: # for multi-image segmentation there is one bias field image # per input for val, f in enumerate(self.inputs.in_files): # image numbering is 1-based - outputs['bias_field'].append( + self.outputs.bias_field.append( self._gen_fname(basefile, suffix='_bias_%d' % (val + 1))) else: # single image segmentation has unnumbered output image - outputs['bias_field'].append( + self.outputs.bias_field.append( self._gen_fname(basefile, suffix='_bias')) if self.inputs.probability_maps: - outputs['probability_maps'] = [] + self.outputs.probability_maps = [] for i in range(nclasses): - outputs['probability_maps'].append( + self.outputs.probability_maps.append( self._gen_fname(basefile, suffix='_prob_%d' % i)) - return outputs class FLIRTInputSpec(FSLCommandInputSpec): @@ -509,6 +492,14 @@ class FLIRTInputSpec(FSLCommandInputSpec): argstr='-bbrslope %f', min_ver='5.0.0', desc='value of bbr slope') + def parse_args(self, skip=None): + skip = [] + if isdefined(self.save_log) and self.save_log: + if not isdefined(self.verbose) or self.verbose == 0: + self.verbose = 1 + skip.append('save_log') + return super(FLIRTInputSpec, self).parse_args(skip=skip) + class FLIRTOutputSpec(TraitedSpec): out_file = File(exists=True, @@ -545,21 +536,15 @@ class FLIRT(FSLCommand): input_spec = FLIRTInputSpec output_spec = FLIRTOutputSpec - def aggregate_outputs(self, runtime=None, needed_outputs=None): - outputs = super(FLIRT, self).aggregate_outputs( - runtime=runtime, needed_outputs=needed_outputs) + def _run_interface(self, runtime, **kwargs): + runtime = super(FLIRT, self)._run_interface(runtime, **kwargs) + if isdefined(self.inputs.save_log) and self.inputs.save_log: - with open(outputs.out_log, "a") as text_file: + with open(self.inputs.out_log, "a") as text_file: text_file.write(runtime.stdout + '\n') - return outputs - def _parse_inputs(self, skip=None): - skip = [] - if isdefined(self.inputs.save_log) and self.inputs.save_log: - if not isdefined(self.inputs.verbose) or self.inputs.verbose == 0: - self.inputs.verbose = 1 - skip.append('save_log') - return super(FLIRT, self)._parse_inputs(skip=skip) + return runtime + class ApplyXfmInputSpec(FLIRTInputSpec): @@ -632,6 +617,13 @@ class MCFLIRTInputSpec(FSLCommandInputSpec): ref_file = File(exists=True, argstr='-reffile %s', desc="target image for motion correction") + def _format_arg(self, name, spec, value): + if name == "interpolation": + if value == "trilinear": + return "" + else: + return spec.argstr % value + return super(MCFLIRTInputSpec, self)._format_arg(name, spec, value) class MCFLIRTOutputSpec(TraitedSpec): out_file = File(exists=True, desc="motion-corrected timeseries") @@ -663,24 +655,15 @@ class MCFLIRT(FSLCommand): input_spec = MCFLIRTInputSpec output_spec = MCFLIRTOutputSpec - def _format_arg(self, name, spec, value): - if name == "interpolation": - if value == "trilinear": - return "" - else: - return spec.argstr % value - return super(MCFLIRT, self)._format_arg(name, spec, value) + def _post_run(self): - def _list_outputs(self): cwd = os.getcwd() - outputs = self._outputs().get() - - outputs['out_file'] = self._gen_outfilename() + self.outputs.out_file = self._gen_outfilename() if isdefined(self.inputs.stats_imgs) and self.inputs.stats_imgs: - outputs['variance_img'] = self._gen_fname(outputs['out_file'] + + self.outputs.variance_img = self._gen_fname(self.outputs.out_file + '_variance.ext', cwd=cwd) - outputs['std_img'] = self._gen_fname(outputs['out_file'] + + self.outputs.std_img = self._gen_fname(self.outputs.out_file + '_sigma.ext', cwd=cwd) # The mean image created if -stats option is specified ('meanvol') @@ -690,25 +673,24 @@ def _list_outputs(self): # Note that the same problem holds for the std and variance image. if isdefined(self.inputs.mean_vol) and self.inputs.mean_vol: - outputs['mean_img'] = self._gen_fname(outputs['out_file'] + + self.outputs.mean_img = self._gen_fname(self.outputs.out_file + '_mean_reg.ext', cwd=cwd) if isdefined(self.inputs.save_mats) and self.inputs.save_mats: - _, filename = os.path.split(outputs['out_file']) + _, filename = os.path.split(self.outputs.out_file) matpathname = os.path.join(cwd, filename + '.mat') _, _, _, timepoints = load(self.inputs.in_file).shape - outputs['mat_file'] = [] + self.outputs.mat_file = [] for t in range(timepoints): - outputs['mat_file'].append(os.path.join(matpathname, + self.outputs.mat_file.append(os.path.join(matpathname, 'MAT_%04d' % t)) if isdefined(self.inputs.save_plots) and self.inputs.save_plots: # Note - if e.g. out_file has .nii.gz, you get .nii.gz.par, # which is what mcflirt does! - outputs['par_file'] = outputs['out_file'] + '.par' + self.outputs.par_file = self.outputs.out_file + '.par' if isdefined(self.inputs.save_rms) and self.inputs.save_rms: - outfile = outputs['out_file'] - outputs['rms_files'] = [outfile + '_abs.rms', outfile + '_rel.rms'] - return outputs + outfile = self.outputs.out_file + self.outputs.rms_files = [outfile + '_abs.rms', outfile + '_rel.rms'] def _gen_filename(self, name): if name == 'out_file': @@ -838,6 +820,12 @@ class FNIRTInputSpec(FSLCommandInputSpec): desc='Precision for representing Hessian, double or float. Default double') + def _format_arg(self, name, spec, value): + if name in list(self.filemap.keys()): + return spec.argstr % getattr(self.outputs, name) + return super(FSLCommandInputSpec, self)._format_arg(name, spec, value) + + class FNIRTOutputSpec(TraitedSpec): fieldcoeff_file = File(exists=True, desc='file with field coefficients') warped_file = File(exists=True, desc='warped image') @@ -890,8 +878,7 @@ class FNIRT(FSLCommand): 'log_file': 'log.txt', 'fieldcoeff_file': 'fieldwarp'} - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): for key, suffix in list(self.filemap.items()): inval = getattr(self.inputs, key) change_ext = True @@ -899,29 +886,23 @@ def _list_outputs(self): if suffix.endswith('.txt'): change_ext = False if isdefined(inval): - outputs[key] = inval + setattr(self.outputs, key, inval) else: - outputs[key] = self._gen_fname(self.inputs.in_file, + setattr(self.outputs, key, self._gen_fname(self.inputs.in_file, suffix='_' + suffix, - change_ext=change_ext) + change_ext=change_ext)) elif isdefined(inval): if isinstance(inval, bool): if inval: - outputs[key] = self._gen_fname(self.inputs.in_file, + setattr(self.outputs, key, self._gen_fname(self.inputs.in_file, suffix='_' + suffix, - change_ext=change_ext) + change_ext=change_ext)) else: - outputs[key] = os.path.abspath(inval) - return outputs - - def _format_arg(self, name, spec, value): - if name in list(self.filemap.keys()): - return spec.argstr % self._list_outputs()[name] - return super(FNIRT, self)._format_arg(name, spec, value) + setattr(self.outputs, key, os.path.abspath(inval)) def _gen_filename(self, name): if name in ['warped_file', 'log_file']: - return self._list_outputs()[name] + return getattr(self.outputs, name) return None def write_config(self, configfile): @@ -977,6 +958,12 @@ class ApplyWarpInputSpec(FSLCommandInputSpec): desc='interpolation method') + def _format_arg(self, name, spec, value): + if name == 'superlevel': + return spec.argstr % str(value) + return super(ApplyWarpInputSpec, self)._format_arg(name, spec, value) + + class ApplyWarpOutputSpec(TraitedSpec): out_file = File(exists=True, desc='Warped output file') @@ -1001,23 +988,17 @@ class ApplyWarp(FSLCommand): input_spec = ApplyWarpInputSpec output_spec = ApplyWarpOutputSpec - def _format_arg(self, name, spec, value): - if name == 'superlevel': - return spec.argstr % str(value) - return super(ApplyWarp, self)._format_arg(name, spec, value) + def _post_run(self): - def _list_outputs(self): - outputs = self._outputs().get() if not isdefined(self.inputs.out_file): - outputs['out_file'] = self._gen_fname(self.inputs.in_file, + self.outputs.out_file = self._gen_fname(self.inputs.in_file, suffix='_warp') else: - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs + self.outputs.out_file = os.path.abspath(self.inputs.out_file) def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()[name] + return getattr(self.outputs, name) return None @@ -1066,18 +1047,16 @@ class SliceTimer(FSLCommand): input_spec = SliceTimerInputSpec output_spec = SliceTimerOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): out_file = self.inputs.out_file if not isdefined(out_file): out_file = self._gen_fname(self.inputs.in_file, suffix='_st') - outputs['slice_time_corrected_file'] = os.path.abspath(out_file) - return outputs + self.outputs.slice_time_corrected_file = os.path.abspath(out_file) def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()['slice_time_corrected_file'] + return self.outputs.slice_time_corrected_file return None @@ -1108,6 +1087,19 @@ class SUSANInputSpec(FSLCommandInputSpec): desc='output file name', hash_files=False) + def _format_arg(self, name, spec, value): + if name == 'fwhm': + return spec.argstr % (float(value) / np.sqrt(8 * np.log(2))) + if name == 'usans': + if not value: + return '0' + arglist = [str(len(value))] + for filename, thresh in value: + arglist.extend([filename, '%.10f' % thresh]) + return ' '.join(arglist) + return super(SUSANInputSpec, self)._format_arg(name, spec, value) + + class SUSANOutputSpec(TraitedSpec): smoothed_file = File(exists=True, desc='smoothed output file') @@ -1133,30 +1125,17 @@ class SUSAN(FSLCommand): input_spec = SUSANInputSpec output_spec = SUSANOutputSpec - def _format_arg(self, name, spec, value): - if name == 'fwhm': - return spec.argstr % (float(value) / np.sqrt(8 * np.log(2))) - if name == 'usans': - if not value: - return '0' - arglist = [str(len(value))] - for filename, thresh in value: - arglist.extend([filename, '%.10f' % thresh]) - return ' '.join(arglist) - return super(SUSAN, self)._format_arg(name, spec, value) + def _post_run(self): - def _list_outputs(self): - outputs = self._outputs().get() out_file = self.inputs.out_file if not isdefined(out_file): out_file = self._gen_fname(self.inputs.in_file, suffix='_smooth') - outputs['smoothed_file'] = os.path.abspath(out_file) - return outputs + self.outputs.smoothed_file = os.path.abspath(out_file) def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()['smoothed_file'] + return self.outputs.smoothed_file return None @@ -1169,10 +1148,12 @@ class FUGUEInputSpec(FSLCommandInputSpec): desc='filename for input phase image') fmap_in_file = File(exists=True, argstr='--loadfmap=%s', desc='filename for loading fieldmap (rad/s)') - unwarped_file = File(argstr='--unwarp=%s', desc='apply unwarping and save as filename', - xor=['warped_file'], requires=['in_file']) - warped_file = File(argstr='--warp=%s', desc='apply forward warping and save as filename', - xor=['unwarped_file'], requires=['in_file']) + unwarped_file = GenFile( + template='{in_file}_unwarped{output_type_}', argstr='--unwarp=%s', xor=['warped_file'], + requires=['in_file'], desc='apply unwarping and save as filename') + warped_file = GenFile( + template='{in_file}_warped{output_type_}', argstr='--warp=%s', xor=['unwarped_file'], + requires=['in_file'], desc='apply forward warping and save as filename') forward_warping = traits.Bool(False, usedefault=True, desc='apply forward warping instead of unwarping') @@ -1217,18 +1198,50 @@ class FUGUEInputSpec(FSLCommandInputSpec): nokspace = traits.Bool(False, argstr='--nokspace', desc='do not use k-space forward warping') # Special outputs: shift (voxel shift map, vsm) - save_shift = traits.Bool(False, xor=['save_unmasked_shift'], + save_shift = traits.Bool(False, xor=['save_unmasked_shift'], usedefault=True, desc='write pixel shift volume') - shift_out_file = File(argstr='--saveshift=%s', desc='filename for saving pixel shift volume') save_unmasked_shift = traits.Bool(argstr='--unmaskshift', xor=['save_shift'], desc='saves the unmasked shiftmap when using --saveshift') + shift_out_file = GenFile( + template='{fmap_in_file|phasemap_in_file|shift_in_file}_vsm{output_type_}', + argstr='--saveshift=%s', desc='filename for saving pixel shift volume') # Special outputs: fieldmap (fmap) - save_fmap = traits.Bool(False, xor=['save_unmasked_fmap'], + save_fmap = traits.Bool(False, xor=['save_unmasked_fmap'], usedefault=True, desc='write field map volume') - fmap_out_file = File(argstr='--savefmap=%s', desc='filename for saving fieldmap (rad/s)') save_unmasked_fmap = traits.Bool(False, argstr='--unmaskfmap', xor=['save_fmap'], desc='saves the unmasked fieldmap when using --savefmap') + fmap_out_file = GenFile( + template='{shift_in_file|phasemap_in_file|fmap_in_file}_fieldmap{output_type_}', + argstr='--savefmap=%s', desc='filename for saving fieldmap (rad/s)') + + def parse_args(self, skip=None): + if skip is None: + skip = [] + + input_phase = isdefined(self.phasemap_in_file) + input_vsm = isdefined(self.shift_in_file) + input_fmap = isdefined(self.fmap_in_file) + + if not input_phase and not input_vsm and not input_fmap: + raise RuntimeError('Either phasemap_in_file, shift_in_file or fmap_in_file must be set.') + + if not isdefined(self.in_file): + skip += ['unwarped_file', 'warped_file'] + else: + if self.forward_warping: + skip += ['unwarped_file'] + else: + skip += ['warped_file'] + + # Handle shift output + if not self.save_shift and not self.save_unmasked_shift: + skip += ['save_shift', 'save_unmasked_shift', 'shift_out_file'] + + if not self.save_fmap and not self.save_unmasked_fmap: + skip += ['save_fmap', 'save_unmasked_fmap', 'fmap_out_file'] + + return super(FUGUEInputSpec, self).parse_args(skip=skip) class FUGUEOutputSpec(TraitedSpec): @@ -1299,94 +1312,10 @@ class FUGUE(FSLCommand): """ - _cmd = 'fugue' input_spec = FUGUEInputSpec output_spec = FUGUEOutputSpec - def _parse_inputs(self, skip=None): - if skip is None: - skip = [] - - input_phase = isdefined(self.inputs.phasemap_in_file) - input_vsm = isdefined(self.inputs.shift_in_file) - input_fmap = isdefined(self.inputs.fmap_in_file) - - if not input_phase and not input_vsm and not input_fmap: - raise RuntimeError('Either phasemap_in_file, shift_in_file or fmap_in_file must be set.') - - if not isdefined(self.inputs.in_file): - skip += ['unwarped_file', 'warped_file'] - else: - if self.inputs.forward_warping: - skip += ['unwarped_file'] - trait_spec = self.inputs.trait('warped_file') - trait_spec.name_template = "%s_warped" - trait_spec.name_source = 'in_file' - trait_spec.output_name = 'warped_file' - else: - skip += ['warped_file'] - trait_spec = self.inputs.trait('unwarped_file') - trait_spec.name_template = "%s_unwarped" - trait_spec.name_source = 'in_file' - trait_spec.output_name = 'unwarped_file' - - # Handle shift output - if not isdefined(self.inputs.shift_out_file): - vsm_save_masked = (isdefined(self.inputs.save_shift) and self.inputs.save_shift) - vsm_save_unmasked = (isdefined(self.inputs.save_unmasked_shift) and - self.inputs.save_unmasked_shift) - - if (vsm_save_masked or vsm_save_unmasked): - trait_spec = self.inputs.trait('shift_out_file') - trait_spec.output_name = 'shift_out_file' - - if input_fmap: - trait_spec.name_source = 'fmap_in_file' - elif input_phase: - trait_spec.name_source = 'phasemap_in_file' - elif input_vsm: - trait_spec.name_source = 'shift_in_file' - else: - raise RuntimeError(('Either phasemap_in_file, shift_in_file or ' - 'fmap_in_file must be set.')) - - if vsm_save_unmasked: - trait_spec.name_template = '%s_vsm_unmasked' - else: - trait_spec.name_template = '%s_vsm' - else: - skip += ['save_shift', 'save_unmasked_shift', 'shift_out_file'] - - # Handle fieldmap output - if not isdefined(self.inputs.fmap_out_file): - fmap_save_masked = (isdefined(self.inputs.save_fmap) and self.inputs.save_fmap) - fmap_save_unmasked = (isdefined(self.inputs.save_unmasked_fmap) and - self.inputs.save_unmasked_fmap) - - if (fmap_save_masked or fmap_save_unmasked): - trait_spec = self.inputs.trait('fmap_out_file') - trait_spec.output_name = 'fmap_out_file' - - if input_vsm: - trait_spec.name_source = 'shift_in_file' - elif input_phase: - trait_spec.name_source = 'phasemap_in_file' - elif input_fmap: - trait_spec.name_source = 'fmap_in_file' - else: - raise RuntimeError(('Either phasemap_in_file, shift_in_file or ' - 'fmap_in_file must be set.')) - - if fmap_save_unmasked: - trait_spec.name_template = '%s_fieldmap_unmasked' - else: - trait_spec.name_template = '%s_fieldmap' - else: - skip += ['save_fmap', 'save_unmasked_fmap', 'fmap_out_file'] - - return super(FUGUE, self)._parse_inputs(skip=skip) - class PRELUDEInputSpec(FSLCommandInputSpec): complex_phase_file = File(exists=True, argstr='--complex=%s', @@ -1452,10 +1381,10 @@ class PRELUDE(FSLCommand): def __init__(self, **kwargs): super(PRELUDE, self).__init__(**kwargs) - warn('This has not been fully tested. Please report any failures.') + IFLOGGER.warn('This has not been fully tested. Please report any failures.') + + def _post_run(self): - def _list_outputs(self): - outputs = self._outputs().get() out_file = self.inputs.unwrapped_phase_file if not isdefined(out_file): if isdefined(self.inputs.phase_file): @@ -1464,12 +1393,11 @@ def _list_outputs(self): elif isdefined(self.inputs.complex_phase_file): out_file = self._gen_fname(self.inputs.complex_phase_file, suffix='_phase_unwrapped') - outputs['unwrapped_phase_file'] = os.path.abspath(out_file) - return outputs + self.outputs.unwrapped_phase_file = os.path.abspath(out_file) def _gen_filename(self, name): if name == 'unwrapped_phase_file': - return self._list_outputs()['unwrapped_phase_file'] + return self.outputs.unwrapped_phase_file return None @@ -1545,8 +1473,9 @@ class FIRST(FSLCommand): input_spec = FIRSTInputSpec output_spec = FIRSTOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + + if isdefined(self.inputs.list_of_specific_structures): structures = self.inputs.list_of_specific_structures @@ -1559,13 +1488,12 @@ def _list_outputs(self): 'L_Puta', 'R_Puta', 'L_Thal', 'R_Thal', 'BrStem'] - outputs['original_segmentations'] = \ + self.outputs.original_segmentations = \ self._gen_fname('original_segmentations') - outputs['segmentation_file'] = self._gen_fname('segmentation_file') - outputs['vtk_surfaces'] = self._gen_mesh_names('vtk_surfaces', + self.outputs.segmentation_file = self._gen_fname('segmentation_file') + self.outputs.vtk_surfaces = self._gen_mesh_names('vtk_surfaces', structures) - outputs['bvars'] = self._gen_mesh_names('bvars', structures) - return outputs + self.outputs.bvars = self._gen_mesh_names('bvars', structures) def _gen_fname(self, name): path, outname, ext = split_filename(self.inputs.out_file) diff --git a/nipype/interfaces/fsl/tests/test_auto_ApplyMask.py b/nipype/interfaces/fsl/tests/test_auto_ApplyMask.py index d374567662..3463489616 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ApplyMask.py +++ b/nipype/interfaces/fsl/tests/test_auto_ApplyMask.py @@ -25,16 +25,17 @@ def test_ApplyMask_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_ApplyTOPUP.py b/nipype/interfaces/fsl/tests/test_auto_ApplyTOPUP.py index 77a11e3232..6a9d904f8a 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ApplyTOPUP.py +++ b/nipype/interfaces/fsl/tests/test_auto_ApplyTOPUP.py @@ -37,10 +37,9 @@ def test_ApplyTOPUP_inputs(): method=dict(argstr='--method=%s', ), out_corrected=dict(argstr='--out=%s', - name_source=['in_files'], - name_template='%s_corrected', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_ApplyWarp.py b/nipype/interfaces/fsl/tests/test_auto_ApplyWarp.py index 47e2703cb6..3a62d1c873 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ApplyWarp.py +++ b/nipype/interfaces/fsl/tests/test_auto_ApplyWarp.py @@ -33,7 +33,8 @@ def test_ApplyWarp_inputs(): hash_files=False, position=2, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), postmat=dict(argstr='--postmat=%s', ), premat=dict(argstr='--premat=%s', diff --git a/nipype/interfaces/fsl/tests/test_auto_ApplyXfm.py b/nipype/interfaces/fsl/tests/test_auto_ApplyXfm.py index 897d6478ed..b5ee6dcf7b 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ApplyXfm.py +++ b/nipype/interfaces/fsl/tests/test_auto_ApplyXfm.py @@ -97,7 +97,8 @@ def test_ApplyXfm_inputs(): name_template='%s_flirt.mat', position=3, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), padding_size=dict(argstr='-paddingsize %d', units='voxels', ), diff --git a/nipype/interfaces/fsl/tests/test_auto_AvScale.py b/nipype/interfaces/fsl/tests/test_auto_AvScale.py index 0d750ddbc0..1ff5de7fe3 100644 --- a/nipype/interfaces/fsl/tests/test_auto_AvScale.py +++ b/nipype/interfaces/fsl/tests/test_auto_AvScale.py @@ -15,7 +15,8 @@ def test_AvScale_inputs(): mat_file=dict(argstr='%s', position=0, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX4.py b/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX4.py index ca71395c20..83fe61490b 100644 --- a/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX4.py +++ b/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX4.py @@ -65,7 +65,8 @@ def test_BEDPOSTX4_inputs(): non_linear=dict(argstr='--nonlinear', xor=('no_spat', 'non_linear'), ), - output_type=dict(), + output_type=dict(usedefault=True, + ), sample_every=dict(argstr='--sampleevery=%d', ), sampling=dict(argstr='-s %d', diff --git a/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX5.py b/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX5.py index 48f229eabd..ffc8e96abb 100644 --- a/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX5.py +++ b/nipype/interfaces/fsl/tests/test_auto_BEDPOSTX5.py @@ -22,6 +22,8 @@ def test_BEDPOSTX5_inputs(): ), dwi=dict(mandatory=True, ), + dyads=dict(), + dyads_dispersion=dict(), environ=dict(nohash=True, usedefault=True, ), @@ -34,6 +36,7 @@ def test_BEDPOSTX5_inputs(): force_dir=dict(argstr='--forcedir', usedefault=True, ), + fsamples=dict(), fudge=dict(argstr='-w %d', ), gradnonlin=dict(argstr='-g', @@ -45,6 +48,10 @@ def test_BEDPOSTX5_inputs(): ), mask=dict(mandatory=True, ), + mean_S0samples=dict(), + mean_dsamples=dict(), + mean_fsamples=dict(), + mean_tausamples=dict(), model=dict(argstr='-model %d', ), n_fibres=dict(argstr='-n %d', @@ -67,8 +74,11 @@ def test_BEDPOSTX5_inputs(): position=1, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), + phsamples=dict(), rician=dict(argstr='--rician', + usedefault=True, ), sample_every=dict(argstr='-s %d', ), @@ -76,6 +86,7 @@ def test_BEDPOSTX5_inputs(): ), terminal_output=dict(nohash=True, ), + thsamples=dict(), update_proposal_every=dict(argstr='--updateproposalevery=%d', ), use_gpu=dict(), @@ -90,14 +101,13 @@ def test_BEDPOSTX5_inputs(): def test_BEDPOSTX5_outputs(): output_map = dict(dyads=dict(), dyads_dispersion=dict(), + fsamples=dict(), mean_S0samples=dict(), mean_dsamples=dict(), mean_fsamples=dict(), - mean_phsamples=dict(), - mean_thsamples=dict(), - merged_fsamples=dict(), - merged_phsamples=dict(), - merged_thsamples=dict(), + mean_tausamples=dict(), + phsamples=dict(), + thsamples=dict(), ) outputs = BEDPOSTX5.output_spec() diff --git a/nipype/interfaces/fsl/tests/test_auto_BET.py b/nipype/interfaces/fsl/tests/test_auto_BET.py index 9f91d76d2f..04e4db99c5 100644 --- a/nipype/interfaces/fsl/tests/test_auto_BET.py +++ b/nipype/interfaces/fsl/tests/test_auto_BET.py @@ -24,20 +24,33 @@ def test_BET_inputs(): mandatory=True, position=0, ), + inskull_mask_file=dict(), + inskull_mesh_file=dict(), mask=dict(argstr='-m', + usedefault=True, ), + mask_file=dict(), mesh=dict(argstr='-e', + usedefault=True, ), + meshfile=dict(), no_output=dict(argstr='-n', + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=1, ), outline=dict(argstr='-o', + usedefault=True, + ), + outline_file=dict(), + output_type=dict(usedefault=True, ), - output_type=dict(), + outskin_mask_file=dict(), + outskin_mesh_file=dict(), + outskull_mask_file=dict(), + outskull_mesh_file=dict(), padding=dict(argstr='-Z', xor=('functional', 'reduce_bias', 'robust', 'padding', 'remove_eyes', 'surfaces', 't2_guided'), ), @@ -51,10 +64,13 @@ def test_BET_inputs(): xor=('functional', 'reduce_bias', 'robust', 'padding', 'remove_eyes', 'surfaces', 't2_guided'), ), robust=dict(argstr='-R', + usedefault=True, xor=('functional', 'reduce_bias', 'robust', 'padding', 'remove_eyes', 'surfaces', 't2_guided'), ), skull=dict(argstr='-s', + usedefault=True, ), + skull_mask_file=dict(), surfaces=dict(argstr='-A', xor=('functional', 'reduce_bias', 'robust', 'padding', 'remove_eyes', 'surfaces', 't2_guided'), ), @@ -64,6 +80,7 @@ def test_BET_inputs(): terminal_output=dict(nohash=True, ), threshold=dict(argstr='-t', + usedefault=True, ), vertical_gradient=dict(argstr='-g %.2f', ), diff --git a/nipype/interfaces/fsl/tests/test_auto_BinaryMaths.py b/nipype/interfaces/fsl/tests/test_auto_BinaryMaths.py index dfc8dcec09..86d19884cd 100644 --- a/nipype/interfaces/fsl/tests/test_auto_BinaryMaths.py +++ b/nipype/interfaces/fsl/tests/test_auto_BinaryMaths.py @@ -21,6 +21,7 @@ def test_BinaryMaths_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), operand_file=dict(argstr='%s', mandatory=True, @@ -37,14 +38,14 @@ def test_BinaryMaths_inputs(): position=4, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_ChangeDataType.py b/nipype/interfaces/fsl/tests/test_auto_ChangeDataType.py index 4de7103895..cd647821d9 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ChangeDataType.py +++ b/nipype/interfaces/fsl/tests/test_auto_ChangeDataType.py @@ -21,9 +21,9 @@ def test_ChangeDataType_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), @@ -31,7 +31,8 @@ def test_ChangeDataType_inputs(): mandatory=True, position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_Cluster.py b/nipype/interfaces/fsl/tests/test_auto_Cluster.py index 7b460a5fd6..dc374701ef 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Cluster.py +++ b/nipype/interfaces/fsl/tests/test_auto_Cluster.py @@ -30,6 +30,7 @@ def test_Cluster_inputs(): ), out_index_file=dict(argstr='--oindex=%s', hash_files=False, + output_name='index_file', ), out_localmax_txt_file=dict(argstr='--olmax=%s', hash_files=False, @@ -52,12 +53,29 @@ def test_Cluster_inputs(): out_threshold_file=dict(argstr='--othresh=%s', hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), peak_distance=dict(argstr='--peakdist=%.10f', ), pthreshold=dict(argstr='--pthresh=%.10f', requires=['dlh', 'volume'], ), + save_index_file=dict(usedefault=True, + ), + save_localmax_txt_file=dict(usedefault=True, + ), + save_localmax_vol_file=dict(usedefault=True, + ), + save_max_file=dict(usedefault=True, + ), + save_mean_file=dict(usedefault=True, + ), + save_pval_file=dict(usedefault=True, + ), + save_size_file=dict(usedefault=True, + ), + save_threshold_file=dict(usedefault=True, + ), std_space_file=dict(argstr='--stdvol=%s', ), terminal_output=dict(nohash=True, @@ -82,13 +100,13 @@ def test_Cluster_inputs(): def test_Cluster_outputs(): output_map = dict(index_file=dict(), - localmax_txt_file=dict(), - localmax_vol_file=dict(), - max_file=dict(), - mean_file=dict(), - pval_file=dict(), - size_file=dict(), - threshold_file=dict(), + out_localmax_txt_file=dict(), + out_localmax_vol_file=dict(), + out_max_file=dict(), + out_mean_file=dict(), + out_pval_file=dict(), + out_size_file=dict(), + out_threshold_file=dict(), ) outputs = Cluster.output_spec() diff --git a/nipype/interfaces/fsl/tests/test_auto_Complex.py b/nipype/interfaces/fsl/tests/test_auto_Complex.py index eae95be846..67d448de7e 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Complex.py +++ b/nipype/interfaces/fsl/tests/test_auto_Complex.py @@ -58,7 +58,8 @@ def test_Complex_inputs(): position=-4, xor=['complex_out_file', 'real_out_file', 'imaginary_out_file', 'real_cartesian', 'complex_cartesian', 'complex_polar', 'complex_split', 'complex_merge'], ), - output_type=dict(), + output_type=dict(usedefault=True, + ), phase_in_file=dict(argstr='%s', position=3, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_ContrastMgr.py b/nipype/interfaces/fsl/tests/test_auto_ContrastMgr.py index 361f9cd086..add2dc41d9 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ContrastMgr.py +++ b/nipype/interfaces/fsl/tests/test_auto_ContrastMgr.py @@ -23,7 +23,8 @@ def test_ContrastMgr_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), param_estimates=dict(argstr='', copyfile=False, mandatory=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_ConvertWarp.py b/nipype/interfaces/fsl/tests/test_auto_ConvertWarp.py index d140396548..5103834ff6 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ConvertWarp.py +++ b/nipype/interfaces/fsl/tests/test_auto_ConvertWarp.py @@ -27,15 +27,13 @@ def test_ConvertWarp_inputs(): xor=['out_relwarp'], ), out_file=dict(argstr='--out=%s', - name_source=['reference'], - name_template='%s_concatwarp', - output_name='out_file', position=-1, ), out_relwarp=dict(argstr='--relout', xor=['out_abswarp'], ), - output_type=dict(), + output_type=dict(usedefault=True, + ), postmat=dict(argstr='--postmat=%s', ), premat=dict(argstr='--premat=%s', diff --git a/nipype/interfaces/fsl/tests/test_auto_ConvertXFM.py b/nipype/interfaces/fsl/tests/test_auto_ConvertXFM.py index 21bfe5ff1c..65edd5c25c 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ConvertXFM.py +++ b/nipype/interfaces/fsl/tests/test_auto_ConvertXFM.py @@ -33,12 +33,17 @@ def test_ConvertXFM_inputs(): position=-3, xor=['invert_xfm', 'concat_xfm', 'fix_scale_skew'], ), + operation=dict(argstr='-%s', + mandatory=True, + position=-3, + usedefault=True, + ), out_file=dict(argstr='-omat %s', - genfile=True, hash_files=False, position=1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_CopyGeom.py b/nipype/interfaces/fsl/tests/test_auto_CopyGeom.py index 75e58ee331..830af5cfa2 100644 --- a/nipype/interfaces/fsl/tests/test_auto_CopyGeom.py +++ b/nipype/interfaces/fsl/tests/test_auto_CopyGeom.py @@ -9,8 +9,6 @@ def test_CopyGeom_inputs(): dest_file=dict(argstr='%s', copyfile=True, mandatory=True, - name_source='dest_file', - name_template='%s', output_name='out_file', position=1, ), @@ -18,7 +16,8 @@ def test_CopyGeom_inputs(): usedefault=True, ), ignore_dims=dict(argstr='-d', - position='-1', + position=-1, + usedefault=True, ), ignore_exception=dict(nohash=True, usedefault=True, @@ -27,7 +26,8 @@ def test_CopyGeom_inputs(): mandatory=True, position=0, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_DTIFit.py b/nipype/interfaces/fsl/tests/test_auto_DTIFit.py index 803a78b930..83d48e1262 100644 --- a/nipype/interfaces/fsl/tests/test_auto_DTIFit.py +++ b/nipype/interfaces/fsl/tests/test_auto_DTIFit.py @@ -50,11 +50,24 @@ def test_DTIFit_inputs(): ), min_z=dict(argstr='-z %d', ), - output_type=dict(), + out_fa=dict(), + out_l1=dict(), + out_l2=dict(), + out_l3=dict(), + out_md=dict(), + out_mo=dict(), + out_s0=dict(), + out_v1=dict(), + out_v2=dict(), + out_v3=dict(), + output_type=dict(usedefault=True, + ), save_tensor=dict(argstr='--save_tensor', + usedefault=True, ), sse=dict(argstr='--sse', ), + tensor=dict(), terminal_output=dict(nohash=True, ), ) @@ -66,16 +79,16 @@ def test_DTIFit_inputs(): def test_DTIFit_outputs(): - output_map = dict(FA=dict(), - L1=dict(), - L2=dict(), - L3=dict(), - MD=dict(), - MO=dict(), - S0=dict(), - V1=dict(), - V2=dict(), - V3=dict(), + output_map = dict(out_fa=dict(), + out_l1=dict(), + out_l2=dict(), + out_l3=dict(), + out_md=dict(), + out_mo=dict(), + out_s0=dict(), + out_v1=dict(), + out_v2=dict(), + out_v3=dict(), tensor=dict(), ) outputs = DTIFit.output_spec() diff --git a/nipype/interfaces/fsl/tests/test_auto_DilateImage.py b/nipype/interfaces/fsl/tests/test_auto_DilateImage.py index 7c0f3e9823..044ba1c605 100644 --- a/nipype/interfaces/fsl/tests/test_auto_DilateImage.py +++ b/nipype/interfaces/fsl/tests/test_auto_DilateImage.py @@ -32,20 +32,21 @@ def test_DilateImage_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), operation=dict(argstr='-dil%s', mandatory=True, position=6, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_DistanceMap.py b/nipype/interfaces/fsl/tests/test_auto_DistanceMap.py index 083590ed5d..ee72ba05dc 100644 --- a/nipype/interfaces/fsl/tests/test_auto_DistanceMap.py +++ b/nipype/interfaces/fsl/tests/test_auto_DistanceMap.py @@ -26,7 +26,8 @@ def test_DistanceMap_inputs(): ), mask_file=dict(argstr='--mask=%s', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_EPIDeWarp.py b/nipype/interfaces/fsl/tests/test_auto_EPIDeWarp.py index 2f1eaf2522..5f4f574f0f 100644 --- a/nipype/interfaces/fsl/tests/test_auto_EPIDeWarp.py +++ b/nipype/interfaces/fsl/tests/test_auto_EPIDeWarp.py @@ -6,52 +6,16 @@ def test_EPIDeWarp_inputs(): input_map = dict(args=dict(argstr='%s', ), - cleanup=dict(argstr='--cleanup', - ), - dph_file=dict(argstr='--dph %s', - mandatory=True, - ), environ=dict(nohash=True, usedefault=True, ), - epi_file=dict(argstr='--epi %s', - ), - epidw=dict(argstr='--epidw %s', - genfile=False, - ), - esp=dict(argstr='--esp %s', - usedefault=True, - ), - exf_file=dict(argstr='--exf %s', - ), - exfdw=dict(argstr='--exfdw %s', - genfile=True, - ), ignore_exception=dict(nohash=True, usedefault=True, ), - mag_file=dict(argstr='--mag %s', - mandatory=True, - position=0, - ), - nocleanup=dict(argstr='--nocleanup', - usedefault=True, - ), - output_type=dict(), - sigma=dict(argstr='--sigma %s', - usedefault=True, - ), - tediff=dict(argstr='--tediff %s', - usedefault=True, + output_type=dict(usedefault=True, ), terminal_output=dict(nohash=True, ), - tmpdir=dict(argstr='--tmpdir %s', - genfile=True, - ), - vsm=dict(argstr='--vsm %s', - genfile=True, - ), ) inputs = EPIDeWarp.input_spec() @@ -61,11 +25,7 @@ def test_EPIDeWarp_inputs(): def test_EPIDeWarp_outputs(): - output_map = dict(exf_mask=dict(), - exfdw=dict(), - unwarped_file=dict(), - vsm_file=dict(), - ) + output_map = dict() outputs = EPIDeWarp.output_spec() for key, metadata in list(output_map.items()): diff --git a/nipype/interfaces/fsl/tests/test_auto_Eddy.py b/nipype/interfaces/fsl/tests/test_auto_Eddy.py index 07b17244c9..727b6cb8ed 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Eddy.py +++ b/nipype/interfaces/fsl/tests/test_auto_Eddy.py @@ -49,7 +49,8 @@ def test_Eddy_inputs(): out_base=dict(argstr='--out=%s', usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), repol=dict(argstr='--repol', ), session=dict(argstr='--session=%s', diff --git a/nipype/interfaces/fsl/tests/test_auto_EddyCorrect.py b/nipype/interfaces/fsl/tests/test_auto_EddyCorrect.py index b7f93f0b52..1974200634 100644 --- a/nipype/interfaces/fsl/tests/test_auto_EddyCorrect.py +++ b/nipype/interfaces/fsl/tests/test_auto_EddyCorrect.py @@ -12,21 +12,7 @@ def test_EddyCorrect_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - in_file=dict(argstr='%s', - mandatory=True, - position=0, - ), - out_file=dict(argstr='%s', - name_source=['in_file'], - name_template='%s_edc', - output_name='eddy_corrected', - position=1, - ), - output_type=dict(), - ref_num=dict(argstr='%d', - mandatory=True, - position=2, - usedefault=True, + output_type=dict(usedefault=True, ), terminal_output=dict(nohash=True, ), @@ -39,8 +25,7 @@ def test_EddyCorrect_inputs(): def test_EddyCorrect_outputs(): - output_map = dict(eddy_corrected=dict(), - ) + output_map = dict() outputs = EddyCorrect.output_spec() for key, metadata in list(output_map.items()): diff --git a/nipype/interfaces/fsl/tests/test_auto_EpiReg.py b/nipype/interfaces/fsl/tests/test_auto_EpiReg.py index 10014e521a..82440ecebb 100644 --- a/nipype/interfaces/fsl/tests/test_auto_EpiReg.py +++ b/nipype/interfaces/fsl/tests/test_auto_EpiReg.py @@ -15,12 +15,19 @@ def test_EpiReg_inputs(): mandatory=True, position=-4, ), + epi2str_inv=dict(), + epi2str_mat=dict(), fmap=dict(argstr='--fmap=%s', ), + fmap2epi_mat=dict(), + fmap2str_mat=dict(), + fmap_epi=dict(), + fmap_str=dict(), fmapmag=dict(argstr='--fmapmag=%s', ), fmapmagbrain=dict(argstr='--fmapmagbrain=%s', ), + fullwarp=dict(), ignore_exception=dict(nohash=True, usedefault=True, ), @@ -28,14 +35,19 @@ def test_EpiReg_inputs(): usedefault=True, ), no_fmapreg=dict(argstr='--nofmapreg', + usedefault=True, ), + out_1vol=dict(), out_base=dict(argstr='--out=%s', position=-1, usedefault=True, ), - output_type=dict(), + out_file=dict(), + output_type=dict(usedefault=True, + ), pedir=dict(argstr='--pedir=%s', ), + shiftmap=dict(), t1_brain=dict(argstr='--t1brain=%s', mandatory=True, position=-2, @@ -48,8 +60,8 @@ def test_EpiReg_inputs(): ), weight_image=dict(argstr='--weight=%s', ), - wmseg=dict(argstr='--wmseg=%s', - ), + wmedge=dict(), + wmseg=dict(), ) inputs = EpiReg.input_spec() diff --git a/nipype/interfaces/fsl/tests/test_auto_ErodeImage.py b/nipype/interfaces/fsl/tests/test_auto_ErodeImage.py index 3981afc1a5..98fe5f4b0b 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ErodeImage.py +++ b/nipype/interfaces/fsl/tests/test_auto_ErodeImage.py @@ -30,22 +30,23 @@ def test_ErodeImage_inputs(): position=5, xor=['kernel_file'], ), - minimum_filter=dict(argstr='%s', + minimum_filter=dict(argstr='-eroF', position=6, usedefault=True, ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_ExtractROI.py b/nipype/interfaces/fsl/tests/test_auto_ExtractROI.py index 4368a41256..6a6f971128 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ExtractROI.py +++ b/nipype/interfaces/fsl/tests/test_auto_ExtractROI.py @@ -20,9 +20,9 @@ def test_ExtractROI_inputs(): mandatory=True, position=0, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), roi_file=dict(argstr='%s', - genfile=True, hash_files=False, position=1, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_FAST.py b/nipype/interfaces/fsl/tests/test_auto_FAST.py index 3dc8ca73f2..0249ee415c 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FAST.py +++ b/nipype/interfaces/fsl/tests/test_auto_FAST.py @@ -6,6 +6,7 @@ def test_FAST_inputs(): input_map = dict(args=dict(argstr='%s', ), + bias_field=dict(), bias_iters=dict(argstr='-I %d', ), bias_lowpass=dict(argstr='-l %d', @@ -36,11 +37,13 @@ def test_FAST_inputs(): ), mixel_smooth=dict(argstr='-R %.2f', ), + mixeltype=dict(), no_bias=dict(argstr='-N', ), no_pve=dict(argstr='--nopve', ), number_classes=dict(argstr='-n %d', + usedefault=True, ), other_priors=dict(argstr='-A %s', ), @@ -50,15 +53,23 @@ def test_FAST_inputs(): ), output_biasfield=dict(argstr='-b', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), + partial_volume_files=dict(), + partial_volume_map=dict(), probability_maps=dict(argstr='-p', ), + probability_maps_files=dict(output_name='probability_maps', + ), + restored_image=dict(), segment_iters=dict(argstr='-W %d', ), segments=dict(argstr='-g', ), terminal_output=dict(nohash=True, ), + tissue_class_files=dict(), + tissue_class_map=dict(), use_priors=dict(argstr='-P', ), verbose=dict(argstr='-v', diff --git a/nipype/interfaces/fsl/tests/test_auto_FEAT.py b/nipype/interfaces/fsl/tests/test_auto_FEAT.py index 8500302502..a6ebeb9d08 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FEAT.py +++ b/nipype/interfaces/fsl/tests/test_auto_FEAT.py @@ -16,7 +16,8 @@ def test_FEAT_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_FEATModel.py b/nipype/interfaces/fsl/tests/test_auto_FEATModel.py index 06cbe57d84..ca076465d6 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FEATModel.py +++ b/nipype/interfaces/fsl/tests/test_auto_FEATModel.py @@ -22,7 +22,8 @@ def test_FEATModel_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_FIRST.py b/nipype/interfaces/fsl/tests/test_auto_FIRST.py index 876f89f5b6..6ad2da8452 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FIRST.py +++ b/nipype/interfaces/fsl/tests/test_auto_FIRST.py @@ -44,7 +44,8 @@ def test_FIRST_inputs(): position=-1, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), verbose=dict(argstr='-v', diff --git a/nipype/interfaces/fsl/tests/test_auto_FLAMEO.py b/nipype/interfaces/fsl/tests/test_auto_FLAMEO.py index bd4d938ffb..e93e1d08f0 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FLAMEO.py +++ b/nipype/interfaces/fsl/tests/test_auto_FLAMEO.py @@ -43,7 +43,8 @@ def test_FLAMEO_inputs(): ), outlier_iter=dict(argstr='--ioni=%d', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), run_mode=dict(argstr='--runmode=%s', mandatory=True, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_FLIRT.py b/nipype/interfaces/fsl/tests/test_auto_FLIRT.py index 8bba532da8..e5d56fda1d 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FLIRT.py +++ b/nipype/interfaces/fsl/tests/test_auto_FLIRT.py @@ -96,7 +96,8 @@ def test_FLIRT_inputs(): name_template='%s_flirt.mat', position=3, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), padding_size=dict(argstr='-paddingsize %d', units='voxels', ), diff --git a/nipype/interfaces/fsl/tests/test_auto_FNIRT.py b/nipype/interfaces/fsl/tests/test_auto_FNIRT.py index f37e3b7eb2..83bd3b300b 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FNIRT.py +++ b/nipype/interfaces/fsl/tests/test_auto_FNIRT.py @@ -77,7 +77,8 @@ def test_FNIRT_inputs(): out_intensitymap_file=dict(argstr='--intout=%s', hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), ref_file=dict(argstr='--ref=%s', mandatory=True, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_FSLCommand.py b/nipype/interfaces/fsl/tests/test_auto_FSLCommand.py index c5b0bb63a2..df99aef52d 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FSLCommand.py +++ b/nipype/interfaces/fsl/tests/test_auto_FSLCommand.py @@ -12,7 +12,8 @@ def test_FSLCommand_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) @@ -22,3 +23,11 @@ def test_FSLCommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_FSLCommand_outputs(): + output_map = dict() + outputs = FSLCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/fsl/tests/test_auto_FSLXCommand.py b/nipype/interfaces/fsl/tests/test_auto_FSLXCommand.py index 57b06760d5..929e4109a3 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FSLXCommand.py +++ b/nipype/interfaces/fsl/tests/test_auto_FSLXCommand.py @@ -25,6 +25,7 @@ def test_FSLXCommand_inputs(): dwi=dict(argstr='--data=%s', mandatory=True, ), + dyads=dict(), environ=dict(nohash=True, usedefault=True, ), @@ -37,6 +38,7 @@ def test_FSLXCommand_inputs(): force_dir=dict(argstr='--forcedir', usedefault=True, ), + fsamples=dict(), fudge=dict(argstr='--fudge=%d', ), ignore_exception=dict(nohash=True, @@ -48,6 +50,10 @@ def test_FSLXCommand_inputs(): mask=dict(argstr='--mask=%s', mandatory=True, ), + mean_S0samples=dict(), + mean_dsamples=dict(), + mean_fsamples=dict(), + mean_tausamples=dict(), model=dict(argstr='--model=%d', ), n_fibres=dict(argstr='--nfibres=%d', @@ -65,8 +71,11 @@ def test_FSLXCommand_inputs(): non_linear=dict(argstr='--nonlinear', xor=('no_spat', 'non_linear', 'cnlinear'), ), - output_type=dict(), + output_type=dict(usedefault=True, + ), + phsamples=dict(), rician=dict(argstr='--rician', + usedefault=True, ), sample_every=dict(argstr='--sampleevery=%d', ), @@ -74,6 +83,7 @@ def test_FSLXCommand_inputs(): ), terminal_output=dict(nohash=True, ), + thsamples=dict(), update_proposal_every=dict(argstr='--updateproposalevery=%d', ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_FUGUE.py b/nipype/interfaces/fsl/tests/test_auto_FUGUE.py index 84de7126df..657da0bd0c 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FUGUE.py +++ b/nipype/interfaces/fsl/tests/test_auto_FUGUE.py @@ -48,7 +48,8 @@ def test_FUGUE_inputs(): ), nokspace=dict(argstr='--nokspace', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), pava=dict(argstr='--pava', ), phase_conjugate=dict(argstr='--phaseconj', diff --git a/nipype/interfaces/fsl/tests/test_auto_FilterRegressor.py b/nipype/interfaces/fsl/tests/test_auto_FilterRegressor.py index 2904b70798..546ff54ff4 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FilterRegressor.py +++ b/nipype/interfaces/fsl/tests/test_auto_FilterRegressor.py @@ -33,13 +33,14 @@ def test_FilterRegressor_inputs(): mask=dict(argstr='-m %s', ), out_file=dict(argstr='-o %s', - genfile=True, hash_files=False, position=2, + template='{in_file}_regfilt{output_type_}', ), out_vnscales=dict(argstr='--out_vnscales', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), var_norm=dict(argstr='--vn', diff --git a/nipype/interfaces/fsl/tests/test_auto_FindTheBiggest.py b/nipype/interfaces/fsl/tests/test_auto_FindTheBiggest.py index 0fd902dbf0..46d13ddf9e 100644 --- a/nipype/interfaces/fsl/tests/test_auto_FindTheBiggest.py +++ b/nipype/interfaces/fsl/tests/test_auto_FindTheBiggest.py @@ -21,7 +21,8 @@ def test_FindTheBiggest_inputs(): hash_files=False, position=2, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_GLM.py b/nipype/interfaces/fsl/tests/test_auto_GLM.py index 3aeef972c0..fc14205028 100644 --- a/nipype/interfaces/fsl/tests/test_auto_GLM.py +++ b/nipype/interfaces/fsl/tests/test_auto_GLM.py @@ -60,7 +60,8 @@ def test_GLM_inputs(): ), out_z_name=dict(argstr='--out_z=%s', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), var_norm=dict(argstr='--vn', diff --git a/nipype/interfaces/fsl/tests/test_auto_ImageMaths.py b/nipype/interfaces/fsl/tests/test_auto_ImageMaths.py index 008516f571..ae1c0f83cc 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ImageMaths.py +++ b/nipype/interfaces/fsl/tests/test_auto_ImageMaths.py @@ -26,12 +26,13 @@ def test_ImageMaths_inputs(): position=5, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=4, ), - output_type=dict(), - suffix=dict(), + output_type=dict(usedefault=True, + ), + suffix=dict(deprecated=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_ImageMeants.py b/nipype/interfaces/fsl/tests/test_auto_ImageMeants.py index 2a07ee64f0..1441f68c05 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ImageMeants.py +++ b/nipype/interfaces/fsl/tests/test_auto_ImageMeants.py @@ -26,10 +26,10 @@ def test_ImageMeants_inputs(): usedefault=True, ), out_file=dict(argstr='-o %s', - genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), show_all=dict(argstr='--showall', ), spatial_coord=dict(argstr='-c %s', diff --git a/nipype/interfaces/fsl/tests/test_auto_ImageStats.py b/nipype/interfaces/fsl/tests/test_auto_ImageStats.py index 86be9772c4..6f393c07d9 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ImageStats.py +++ b/nipype/interfaces/fsl/tests/test_auto_ImageStats.py @@ -16,13 +16,14 @@ def test_ImageStats_inputs(): mandatory=True, position=2, ), - mask_file=dict(argstr='', + mask_file=dict(argstr='-k %s', ), op_string=dict(argstr='%s', mandatory=True, position=3, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), split_4d=dict(argstr='-t', position=1, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_InvWarp.py b/nipype/interfaces/fsl/tests/test_auto_InvWarp.py index ad367bf904..6d6fd72689 100644 --- a/nipype/interfaces/fsl/tests/test_auto_InvWarp.py +++ b/nipype/interfaces/fsl/tests/test_auto_InvWarp.py @@ -17,8 +17,6 @@ def test_InvWarp_inputs(): ), inverse_warp=dict(argstr='--out=%s', hash_files=False, - name_source=['warp'], - name_template='%s_inverse', ), jacobian_max=dict(argstr='--jmax=%f', ), @@ -28,7 +26,8 @@ def test_InvWarp_inputs(): ), noconstraint=dict(argstr='--noconstraint', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), reference=dict(argstr='--ref=%s', mandatory=True, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_IsotropicSmooth.py b/nipype/interfaces/fsl/tests/test_auto_IsotropicSmooth.py index 2d1023d674..8c05b34ed4 100644 --- a/nipype/interfaces/fsl/tests/test_auto_IsotropicSmooth.py +++ b/nipype/interfaces/fsl/tests/test_auto_IsotropicSmooth.py @@ -26,16 +26,17 @@ def test_IsotropicSmooth_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), sigma=dict(argstr='-s %.5f', mandatory=True, position=4, diff --git a/nipype/interfaces/fsl/tests/test_auto_MCFLIRT.py b/nipype/interfaces/fsl/tests/test_auto_MCFLIRT.py index 355c9ab527..88c7a5e079 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MCFLIRT.py +++ b/nipype/interfaces/fsl/tests/test_auto_MCFLIRT.py @@ -32,7 +32,8 @@ def test_MCFLIRT_inputs(): genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), ref_file=dict(argstr='-reffile %s', ), ref_vol=dict(argstr='-refvol %d', diff --git a/nipype/interfaces/fsl/tests/test_auto_MELODIC.py b/nipype/interfaces/fsl/tests/test_auto_MELODIC.py index 3f4c0047ca..dbfb447911 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MELODIC.py +++ b/nipype/interfaces/fsl/tests/test_auto_MELODIC.py @@ -74,7 +74,8 @@ def test_MELODIC_inputs(): ), out_white=dict(argstr='--Owhite', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), pbsc=dict(argstr='--pbsc', ), rem_cmp=dict(argstr='-f %d', diff --git a/nipype/interfaces/fsl/tests/test_auto_MakeDyadicVectors.py b/nipype/interfaces/fsl/tests/test_auto_MakeDyadicVectors.py index cbc35e34c9..3d42ba2ec2 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MakeDyadicVectors.py +++ b/nipype/interfaces/fsl/tests/test_auto_MakeDyadicVectors.py @@ -20,7 +20,8 @@ def test_MakeDyadicVectors_inputs(): position=3, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), perc=dict(argstr='%f', position=4, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_MathsCommand.py b/nipype/interfaces/fsl/tests/test_auto_MathsCommand.py index 3c3eee3d14..740604db08 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MathsCommand.py +++ b/nipype/interfaces/fsl/tests/test_auto_MathsCommand.py @@ -21,16 +21,17 @@ def test_MathsCommand_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_MaxImage.py b/nipype/interfaces/fsl/tests/test_auto_MaxImage.py index 4edd2cfb13..e71e5018bb 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MaxImage.py +++ b/nipype/interfaces/fsl/tests/test_auto_MaxImage.py @@ -25,16 +25,17 @@ def test_MaxImage_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_MeanImage.py b/nipype/interfaces/fsl/tests/test_auto_MeanImage.py index f6792d368d..dfc34fc27c 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MeanImage.py +++ b/nipype/interfaces/fsl/tests/test_auto_MeanImage.py @@ -25,16 +25,17 @@ def test_MeanImage_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_Merge.py b/nipype/interfaces/fsl/tests/test_auto_Merge.py index 621d43dd65..c08bc799c6 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Merge.py +++ b/nipype/interfaces/fsl/tests/test_auto_Merge.py @@ -22,11 +22,10 @@ def test_Merge_inputs(): ), merged_file=dict(argstr='%s', hash_files=False, - name_source='in_files', - name_template='%s_merged', position=1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), tr=dict(argstr='%.2f', diff --git a/nipype/interfaces/fsl/tests/test_auto_MotionOutliers.py b/nipype/interfaces/fsl/tests/test_auto_MotionOutliers.py index d8d88d809e..7c6565c4ef 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MotionOutliers.py +++ b/nipype/interfaces/fsl/tests/test_auto_MotionOutliers.py @@ -25,23 +25,15 @@ def test_MotionOutliers_inputs(): ), out_file=dict(argstr='-o %s', hash_files=False, - keep_extension=True, - name_source='in_file', - name_template='%s_outliers.txt', ), out_metric_plot=dict(argstr='-p %s', hash_files=False, - keep_extension=True, - name_source='in_file', - name_template='%s_metrics.png', ), out_metric_values=dict(argstr='-s %s', hash_files=False, - keep_extension=True, - name_source='in_file', - name_template='%s_metrics.txt', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), threshold=dict(argstr='--thresh=%g', diff --git a/nipype/interfaces/fsl/tests/test_auto_MultiImageMaths.py b/nipype/interfaces/fsl/tests/test_auto_MultiImageMaths.py index 91b5f03657..bb4247c08d 100644 --- a/nipype/interfaces/fsl/tests/test_auto_MultiImageMaths.py +++ b/nipype/interfaces/fsl/tests/test_auto_MultiImageMaths.py @@ -21,6 +21,7 @@ def test_MultiImageMaths_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), op_string=dict(argstr='%s', mandatory=True, @@ -29,14 +30,14 @@ def test_MultiImageMaths_inputs(): operand_files=dict(mandatory=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_Overlay.py b/nipype/interfaces/fsl/tests/test_auto_Overlay.py index 14257803be..9452550394 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Overlay.py +++ b/nipype/interfaces/fsl/tests/test_auto_Overlay.py @@ -32,7 +32,6 @@ def test_Overlay_inputs(): usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-1, ), @@ -40,7 +39,8 @@ def test_Overlay_inputs(): position=2, usedefault=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), show_negative_stats=dict(argstr='%s', position=8, xor=['stat_image2'], diff --git a/nipype/interfaces/fsl/tests/test_auto_PRELUDE.py b/nipype/interfaces/fsl/tests/test_auto_PRELUDE.py index 434322da60..4e60b70dc8 100644 --- a/nipype/interfaces/fsl/tests/test_auto_PRELUDE.py +++ b/nipype/interfaces/fsl/tests/test_auto_PRELUDE.py @@ -31,7 +31,8 @@ def test_PRELUDE_inputs(): ), num_partitions=dict(argstr='--numphasesplit=%d', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), phase_file=dict(argstr='--phase=%s', mandatory=True, xor=['complex_phase_file'], diff --git a/nipype/interfaces/fsl/tests/test_auto_PlotMotionParams.py b/nipype/interfaces/fsl/tests/test_auto_PlotMotionParams.py index 75d376e32e..6e29b0d067 100644 --- a/nipype/interfaces/fsl/tests/test_auto_PlotMotionParams.py +++ b/nipype/interfaces/fsl/tests/test_auto_PlotMotionParams.py @@ -12,18 +12,20 @@ def test_PlotMotionParams_inputs(): ignore_exception=dict(nohash=True, usedefault=True, ), - in_file=dict(argstr='%s', + in_file=dict(argstr='-i %s', mandatory=True, position=1, + sep=',', ), in_source=dict(mandatory=True, ), out_file=dict(argstr='-o %s', - genfile=True, hash_files=False, + template='{in_file}_{plot_type[:5]}.png', ), - output_type=dict(), - plot_size=dict(argstr='%s', + output_type=dict(usedefault=True, + ), + plot_size=dict(argstr='-h %d -w %d', ), plot_type=dict(argstr='%s', mandatory=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_PlotTimeSeries.py b/nipype/interfaces/fsl/tests/test_auto_PlotTimeSeries.py index e8c28c68de..680f90c272 100644 --- a/nipype/interfaces/fsl/tests/test_auto_PlotTimeSeries.py +++ b/nipype/interfaces/fsl/tests/test_auto_PlotTimeSeries.py @@ -15,32 +15,35 @@ def test_PlotTimeSeries_inputs(): in_file=dict(argstr='%s', mandatory=True, position=1, + sep=',', ), - labels=dict(argstr='%s', + labels=dict(argstr='-a %s', + sep=',', ), legend_file=dict(argstr='--legend=%s', ), out_file=dict(argstr='-o %s', - genfile=True, hash_files=False, + template='{in_file}.png', + ), + output_type=dict(usedefault=True, ), - output_type=dict(), plot_finish=dict(argstr='--finish=%d', - xor=('plot_range',), + xor=['plot_range'], ), - plot_range=dict(argstr='%s', - xor=('plot_start', 'plot_finish'), + plot_range=dict(argstr='--start=%d --finish=%d', + xor=['plot_start', 'plot_finish'], ), - plot_size=dict(argstr='%s', + plot_size=dict(argstr='-h %d -w %d', ), plot_start=dict(argstr='--start=%d', - xor=('plot_range',), + xor=['plot_range'], ), sci_notation=dict(argstr='--sci', ), terminal_output=dict(nohash=True, ), - title=dict(argstr='%s', + title=dict(argstr="-t '%s'", ), x_precision=dict(argstr='--precision=%d', ), @@ -48,13 +51,13 @@ def test_PlotTimeSeries_inputs(): usedefault=True, ), y_max=dict(argstr='--ymax=%.2f', - xor=('y_range',), + xor=['y_range'], ), y_min=dict(argstr='--ymin=%.2f', - xor=('y_range',), + xor=['y_range'], ), - y_range=dict(argstr='%s', - xor=('y_min', 'y_max'), + y_range=dict(argstr='--ymin=%.2f --ymax=%.2f', + xor=['y_min', 'y_max'], ), ) inputs = PlotTimeSeries.input_spec() diff --git a/nipype/interfaces/fsl/tests/test_auto_PowerSpectrum.py b/nipype/interfaces/fsl/tests/test_auto_PowerSpectrum.py index bacda34c21..8df00e2afc 100644 --- a/nipype/interfaces/fsl/tests/test_auto_PowerSpectrum.py +++ b/nipype/interfaces/fsl/tests/test_auto_PowerSpectrum.py @@ -21,7 +21,8 @@ def test_PowerSpectrum_inputs(): hash_files=False, position=1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_PrepareFieldmap.py b/nipype/interfaces/fsl/tests/test_auto_PrepareFieldmap.py index 01aea929dc..42d1c20388 100644 --- a/nipype/interfaces/fsl/tests/test_auto_PrepareFieldmap.py +++ b/nipype/interfaces/fsl/tests/test_auto_PrepareFieldmap.py @@ -32,7 +32,8 @@ def test_PrepareFieldmap_inputs(): out_fieldmap=dict(argstr='%s', position=4, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), scanner=dict(argstr='%s', position=1, usedefault=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_ProbTrackX.py b/nipype/interfaces/fsl/tests/test_auto_ProbTrackX.py index a4b60ff6f6..b890fc5294 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ProbTrackX.py +++ b/nipype/interfaces/fsl/tests/test_auto_ProbTrackX.py @@ -58,7 +58,8 @@ def test_ProbTrackX_inputs(): out_dir=dict(argstr='--dir=%s', genfile=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), phsamples=dict(mandatory=True, ), rand_fib=dict(argstr='--randfib=%d', diff --git a/nipype/interfaces/fsl/tests/test_auto_ProbTrackX2.py b/nipype/interfaces/fsl/tests/test_auto_ProbTrackX2.py index df69f76670..967c90788d 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ProbTrackX2.py +++ b/nipype/interfaces/fsl/tests/test_auto_ProbTrackX2.py @@ -75,7 +75,8 @@ def test_ProbTrackX2_inputs(): out_dir=dict(argstr='--dir=%s', genfile=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), phsamples=dict(mandatory=True, ), rand_fib=dict(argstr='--randfib=%d', diff --git a/nipype/interfaces/fsl/tests/test_auto_ProjThresh.py b/nipype/interfaces/fsl/tests/test_auto_ProjThresh.py index a8fbd352a9..44fda21420 100644 --- a/nipype/interfaces/fsl/tests/test_auto_ProjThresh.py +++ b/nipype/interfaces/fsl/tests/test_auto_ProjThresh.py @@ -16,7 +16,8 @@ def test_ProjThresh_inputs(): mandatory=True, position=0, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), threshold=dict(argstr='%d', diff --git a/nipype/interfaces/fsl/tests/test_auto_Randomise.py b/nipype/interfaces/fsl/tests/test_auto_Randomise.py index 72a38393fd..46dbce71ff 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Randomise.py +++ b/nipype/interfaces/fsl/tests/test_auto_Randomise.py @@ -43,7 +43,8 @@ def test_Randomise_inputs(): ), one_sample_group_mean=dict(argstr='-1', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), p_vec_n_dist_files=dict(argstr='-P', ), raw_stats_imgs=dict(argstr='-R', diff --git a/nipype/interfaces/fsl/tests/test_auto_Reorient2Std.py b/nipype/interfaces/fsl/tests/test_auto_Reorient2Std.py index 0f252d5d61..eb8c62e295 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Reorient2Std.py +++ b/nipype/interfaces/fsl/tests/test_auto_Reorient2Std.py @@ -19,7 +19,8 @@ def test_Reorient2Std_inputs(): genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_RobustFOV.py b/nipype/interfaces/fsl/tests/test_auto_RobustFOV.py index d28c8845dd..6ed9cecdd7 100644 --- a/nipype/interfaces/fsl/tests/test_auto_RobustFOV.py +++ b/nipype/interfaces/fsl/tests/test_auto_RobustFOV.py @@ -18,10 +18,9 @@ def test_RobustFOV_inputs(): ), out_roi=dict(argstr='-r %s', hash_files=False, - name_source=['in_file'], - name_template='%s_ROI', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_SMM.py b/nipype/interfaces/fsl/tests/test_auto_SMM.py index b2440eaa7e..4cef44c665 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SMM.py +++ b/nipype/interfaces/fsl/tests/test_auto_SMM.py @@ -20,7 +20,8 @@ def test_SMM_inputs(): no_deactivation_class=dict(argstr='--zfstatmode', position=2, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), spatial_data_file=dict(argstr='--sdf="%s"', copyfile=False, mandatory=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_SUSAN.py b/nipype/interfaces/fsl/tests/test_auto_SUSAN.py index 0b813fc31e..247a062106 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SUSAN.py +++ b/nipype/interfaces/fsl/tests/test_auto_SUSAN.py @@ -33,7 +33,8 @@ def test_SUSAN_inputs(): hash_files=False, position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), usans=dict(argstr='', diff --git a/nipype/interfaces/fsl/tests/test_auto_SigLoss.py b/nipype/interfaces/fsl/tests/test_auto_SigLoss.py index e42dc4ba88..237246c252 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SigLoss.py +++ b/nipype/interfaces/fsl/tests/test_auto_SigLoss.py @@ -22,7 +22,8 @@ def test_SigLoss_inputs(): out_file=dict(argstr='-s %s', genfile=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), slice_direction=dict(argstr='-d %s', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_SliceTimer.py b/nipype/interfaces/fsl/tests/test_auto_SliceTimer.py index c02b80cf3b..b4c91745ba 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SliceTimer.py +++ b/nipype/interfaces/fsl/tests/test_auto_SliceTimer.py @@ -30,7 +30,8 @@ def test_SliceTimer_inputs(): genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), slice_direction=dict(argstr='--direction=%d', ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_Slicer.py b/nipype/interfaces/fsl/tests/test_auto_Slicer.py index edcaafaa30..230ef4a45f 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Slicer.py +++ b/nipype/interfaces/fsl/tests/test_auto_Slicer.py @@ -20,6 +20,10 @@ def test_Slicer_inputs(): environ=dict(nohash=True, usedefault=True, ), + hide_orientation=dict(argstr='-u', + position=9, + usedefault=True, + ), ignore_exception=dict(nohash=True, usedefault=True, ), @@ -37,6 +41,7 @@ def test_Slicer_inputs(): position=5, ), label_slices=dict(argstr='-L', + mandatory=True, position=3, usedefault=True, ), @@ -48,11 +53,11 @@ def test_Slicer_inputs(): position=8, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), sample_axial=dict(argstr='-S %d', position=10, requires=['image_width'], @@ -61,9 +66,9 @@ def test_Slicer_inputs(): scaling=dict(argstr='-s %f', position=0, ), - show_orientation=dict(argstr='%s', + show_orientation=dict(deprecated=True, + new_name='hide_orientation', position=9, - usedefault=True, ), single_slice=dict(argstr='-%s', position=10, diff --git a/nipype/interfaces/fsl/tests/test_auto_Smooth.py b/nipype/interfaces/fsl/tests/test_auto_Smooth.py index f1cebc39d7..247008db52 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Smooth.py +++ b/nipype/interfaces/fsl/tests/test_auto_Smooth.py @@ -21,7 +21,8 @@ def test_Smooth_inputs(): mandatory=True, position=0, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), sigma=dict(argstr='-kernel gauss %.03f -fmean', mandatory=True, position=1, @@ -29,8 +30,6 @@ def test_Smooth_inputs(): ), smoothed_file=dict(argstr='%s', hash_files=False, - name_source=['in_file'], - name_template='%s_smooth', position=2, ), terminal_output=dict(nohash=True, diff --git a/nipype/interfaces/fsl/tests/test_auto_SmoothEstimate.py b/nipype/interfaces/fsl/tests/test_auto_SmoothEstimate.py index 5c3f8c46b0..d703e308ed 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SmoothEstimate.py +++ b/nipype/interfaces/fsl/tests/test_auto_SmoothEstimate.py @@ -19,7 +19,8 @@ def test_SmoothEstimate_inputs(): mask_file=dict(argstr='--mask=%s', mandatory=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), residual_fit_file=dict(argstr='--res=%s', requires=['dof'], ), diff --git a/nipype/interfaces/fsl/tests/test_auto_SpatialFilter.py b/nipype/interfaces/fsl/tests/test_auto_SpatialFilter.py index ab605fed0b..3c02cef908 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SpatialFilter.py +++ b/nipype/interfaces/fsl/tests/test_auto_SpatialFilter.py @@ -32,20 +32,21 @@ def test_SpatialFilter_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), operation=dict(argstr='-f%s', mandatory=True, position=6, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_Split.py b/nipype/interfaces/fsl/tests/test_auto_Split.py index a7469eca48..d889d8678f 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Split.py +++ b/nipype/interfaces/fsl/tests/test_auto_Split.py @@ -23,7 +23,8 @@ def test_Split_inputs(): out_base_name=dict(argstr='%s', position=1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_SwapDimensions.py b/nipype/interfaces/fsl/tests/test_auto_SwapDimensions.py index 60dd31a304..cbe9b3e0a8 100644 --- a/nipype/interfaces/fsl/tests/test_auto_SwapDimensions.py +++ b/nipype/interfaces/fsl/tests/test_auto_SwapDimensions.py @@ -14,7 +14,7 @@ def test_SwapDimensions_inputs(): ), in_file=dict(argstr='%s', mandatory=True, - position='1', + position=1, ), new_dims=dict(argstr='%s %s %s', mandatory=True, @@ -23,7 +23,8 @@ def test_SwapDimensions_inputs(): genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_TOPUP.py b/nipype/interfaces/fsl/tests/test_auto_TOPUP.py index 3e097b26ab..80520511fe 100644 --- a/nipype/interfaces/fsl/tests/test_auto_TOPUP.py +++ b/nipype/interfaces/fsl/tests/test_auto_TOPUP.py @@ -9,13 +9,15 @@ def test_TOPUP_inputs(): config=dict(argstr='--config=%s', usedefault=True, ), - encoding_direction=dict(argstr='--datain=%s', - mandatory=True, + encoding_direction=dict(mandatory=True, requires=['readout_times'], xor=['encoding_file'], ), encoding_file=dict(argstr='--datain=%s', + hash_files=False, mandatory=True, + output_name='out_enc_file', + template='{in_file}_encfile.txt', xor=['encoding_direction'], ), environ=dict(nohash=True, @@ -41,26 +43,22 @@ def test_TOPUP_inputs(): ), out_base=dict(argstr='--out=%s', hash_files=False, - name_source=['in_file'], - name_template='%s_base', ), out_corrected=dict(argstr='--iout=%s', hash_files=False, - name_source=['in_file'], - name_template='%s_corrected', ), out_field=dict(argstr='--fout=%s', hash_files=False, - name_source=['in_file'], - name_template='%s_field', + ), + out_fieldcoef=dict(hash_files=False, ), out_logfile=dict(argstr='--logout=%s', hash_files=False, - keep_extension=True, - name_source=['in_file'], - name_template='%s_topup.log', ), - output_type=dict(), + out_movpar=dict(hash_files=False, + ), + output_type=dict(usedefault=True, + ), readout_times=dict(mandatory=True, requires=['encoding_direction'], xor=['encoding_file'], diff --git a/nipype/interfaces/fsl/tests/test_auto_TemporalFilter.py b/nipype/interfaces/fsl/tests/test_auto_TemporalFilter.py index 049af8bd52..b53f74ed23 100644 --- a/nipype/interfaces/fsl/tests/test_auto_TemporalFilter.py +++ b/nipype/interfaces/fsl/tests/test_auto_TemporalFilter.py @@ -29,16 +29,17 @@ def test_TemporalFilter_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_Threshold.py b/nipype/interfaces/fsl/tests/test_auto_Threshold.py index dfaa3594bb..866f5a93a7 100644 --- a/nipype/interfaces/fsl/tests/test_auto_Threshold.py +++ b/nipype/interfaces/fsl/tests/test_auto_Threshold.py @@ -23,16 +23,17 @@ def test_Threshold_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), thresh=dict(argstr='%s', diff --git a/nipype/interfaces/fsl/tests/test_auto_TractSkeleton.py b/nipype/interfaces/fsl/tests/test_auto_TractSkeleton.py index 3808504b9d..9fcad4e22c 100644 --- a/nipype/interfaces/fsl/tests/test_auto_TractSkeleton.py +++ b/nipype/interfaces/fsl/tests/test_auto_TractSkeleton.py @@ -21,7 +21,8 @@ def test_TractSkeleton_inputs(): in_file=dict(argstr='-i %s', mandatory=True, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), project_data=dict(argstr='-p %.3f %s %s %s %s', requires=['threshold', 'distance_map', 'data_file'], ), diff --git a/nipype/interfaces/fsl/tests/test_auto_UnaryMaths.py b/nipype/interfaces/fsl/tests/test_auto_UnaryMaths.py index 9bc209e532..7836d0ca3a 100644 --- a/nipype/interfaces/fsl/tests/test_auto_UnaryMaths.py +++ b/nipype/interfaces/fsl/tests/test_auto_UnaryMaths.py @@ -21,20 +21,21 @@ def test_UnaryMaths_inputs(): ), nan2zeros=dict(argstr='-nan', position=3, + usedefault=True, ), operation=dict(argstr='-%s', mandatory=True, position=4, ), out_file=dict(argstr='%s', - genfile=True, hash_files=False, position=-2, ), output_datatype=dict(argstr='-odt %s', position=-1, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), terminal_output=dict(nohash=True, ), ) diff --git a/nipype/interfaces/fsl/tests/test_auto_VecReg.py b/nipype/interfaces/fsl/tests/test_auto_VecReg.py index 55c84c1164..503d6c5ea5 100644 --- a/nipype/interfaces/fsl/tests/test_auto_VecReg.py +++ b/nipype/interfaces/fsl/tests/test_auto_VecReg.py @@ -25,7 +25,8 @@ def test_VecReg_inputs(): genfile=True, hash_files=False, ), - output_type=dict(), + output_type=dict(usedefault=True, + ), ref_mask=dict(argstr='--refmask=%s', ), ref_vol=dict(argstr='-r %s', diff --git a/nipype/interfaces/fsl/tests/test_auto_WarpUtils.py b/nipype/interfaces/fsl/tests/test_auto_WarpUtils.py index 065e2b455b..06b90eddfe 100644 --- a/nipype/interfaces/fsl/tests/test_auto_WarpUtils.py +++ b/nipype/interfaces/fsl/tests/test_auto_WarpUtils.py @@ -18,15 +18,14 @@ def test_WarpUtils_inputs(): knot_space=dict(argstr='--knotspace=%d,%d,%d', ), out_file=dict(argstr='--out=%s', - name_source=['in_file'], - output_name='out_file', position=-1, ), out_format=dict(argstr='--outformat=%s', ), out_jacobian=dict(argstr='--jac=%s', ), - output_type=dict(), + output_type=dict(usedefault=True, + ), reference=dict(argstr='--ref=%s', mandatory=True, ), diff --git a/nipype/interfaces/fsl/tests/test_auto_XFibres4.py b/nipype/interfaces/fsl/tests/test_auto_XFibres4.py index 5fa46bb954..448b30884b 100644 --- a/nipype/interfaces/fsl/tests/test_auto_XFibres4.py +++ b/nipype/interfaces/fsl/tests/test_auto_XFibres4.py @@ -56,7 +56,8 @@ def test_XFibres4_inputs(): non_linear=dict(argstr='--nonlinear', xor=('no_spat', 'non_linear'), ), - output_type=dict(), + output_type=dict(usedefault=True, + ), sample_every=dict(argstr='--sampleevery=%d', ), seed=dict(argstr='--seed=%d', diff --git a/nipype/interfaces/fsl/tests/test_auto_XFibres5.py b/nipype/interfaces/fsl/tests/test_auto_XFibres5.py index f877d894e5..29361e2571 100644 --- a/nipype/interfaces/fsl/tests/test_auto_XFibres5.py +++ b/nipype/interfaces/fsl/tests/test_auto_XFibres5.py @@ -25,6 +25,7 @@ def test_XFibres5_inputs(): dwi=dict(argstr='--data=%s', mandatory=True, ), + dyads=dict(), environ=dict(nohash=True, usedefault=True, ), @@ -37,6 +38,7 @@ def test_XFibres5_inputs(): force_dir=dict(argstr='--forcedir', usedefault=True, ), + fsamples=dict(), fudge=dict(argstr='--fudge=%d', ), gradnonlin=dict(argstr='--gradnonlin=%s', @@ -50,6 +52,10 @@ def test_XFibres5_inputs(): mask=dict(argstr='--mask=%s', mandatory=True, ), + mean_S0samples=dict(), + mean_dsamples=dict(), + mean_fsamples=dict(), + mean_tausamples=dict(), model=dict(argstr='--model=%d', ), n_fibres=dict(argstr='--nfibres=%d', @@ -67,8 +73,11 @@ def test_XFibres5_inputs(): non_linear=dict(argstr='--nonlinear', xor=('no_spat', 'non_linear', 'cnlinear'), ), - output_type=dict(), + output_type=dict(usedefault=True, + ), + phsamples=dict(), rician=dict(argstr='--rician', + usedefault=True, ), sample_every=dict(argstr='--sampleevery=%d', ), @@ -76,6 +85,7 @@ def test_XFibres5_inputs(): ), terminal_output=dict(nohash=True, ), + thsamples=dict(), update_proposal_every=dict(argstr='--updateproposalevery=%d', ), ) diff --git a/nipype/interfaces/fsl/tests/test_base.py b/nipype/interfaces/fsl/tests/test_base.py index a8ec012905..06366a6139 100644 --- a/nipype/interfaces/fsl/tests/test_base.py +++ b/nipype/interfaces/fsl/tests/test_base.py @@ -4,36 +4,22 @@ from nipype.testing import (assert_equal, assert_true, assert_raises, assert_not_equal, skipif) -import nipype.interfaces.fsl as fsl +from ... import fsl +from ..base import FSLCommandInputSpec from nipype.interfaces.base import InterfaceResult from nipype.interfaces.fsl import check_fsl, no_fsl -@skipif(no_fsl) # skip if fsl not installed) def test_fslversion(): ver = fsl.Info.version() - if ver: + if check_fsl(): # If ver is None, fsl is not installed ver = ver.split('.') yield assert_true, ver[0] in ['4', '5'] + else: + yield assert_equal, None, ver -@skipif(no_fsl) # skip if fsl not installed) -def test_fsloutputtype(): - types = list(fsl.Info.ftypes.keys()) - orig_out_type = fsl.Info.output_type() - yield assert_true, orig_out_type in types - - -def test_outputtype_to_ext(): - for ftype, ext in fsl.Info.ftypes.items(): - res = fsl.Info.output_type_to_ext(ftype) - yield assert_equal, res, ext - - yield assert_raises, KeyError, fsl.Info.output_type_to_ext, 'JUNK' - - -@skipif(no_fsl) # skip if fsl not installed) def test_FSLCommand(): # Most methods in FSLCommand are tested in the subclasses. Only # testing the one item that is not. @@ -42,43 +28,14 @@ def test_FSLCommand(): yield assert_equal, type(res), InterfaceResult -@skipif(no_fsl) # skip if fsl not installed) -def test_FSLCommand2(): +def test_FSLCommandInputSpec(): # Check default output type and environ - cmd = fsl.FSLCommand(command='junk') - yield assert_equal, cmd._output_type, fsl.Info.output_type() - yield assert_equal, cmd.inputs.environ['FSLOUTPUTTYPE'], cmd._output_type - yield assert_true, cmd._output_type in fsl.Info.ftypes + fslspec = FSLCommandInputSpec() + yield assert_equal, fslspec.output_type, lambda: os.getenv('FSLOUTPUTTYPE', 'NIFTI') - cmd = fsl.FSLCommand - cmdinst = fsl.FSLCommand(command='junk') +def test_FSLCommand2(): + cmd = fsl.FSLCommand(command='junk') + yield assert_equal, cmd.inputs.environ.get('FSLOUTPUTTYPE'), cmd.inputs.output_type for out_type in fsl.Info.ftypes: - cmd.set_default_output_type(out_type) - yield assert_equal, cmd._output_type, out_type - if out_type != fsl.Info.output_type(): - # Setting class outputtype should not effect existing instances - yield assert_not_equal, cmdinst.inputs.output_type, out_type - - -@skipif(no_fsl) # skip if fsl not installed) -def test_gen_fname(): - # Test _gen_fname method of FSLCommand - cmd = fsl.FSLCommand(command='junk', output_type='NIFTI_GZ') - pth = os.getcwd() - # just the filename - fname = cmd._gen_fname('foo.nii.gz', suffix='_fsl') - desired = os.path.join(pth, 'foo_fsl.nii.gz') - yield assert_equal, fname, desired - # filename with suffix - fname = cmd._gen_fname('foo.nii.gz', suffix='_brain') - desired = os.path.join(pth, 'foo_brain.nii.gz') - yield assert_equal, fname, desired - # filename with suffix and working directory - fname = cmd._gen_fname('foo.nii.gz', suffix='_brain', cwd='/data') - desired = os.path.join('/data', 'foo_brain.nii.gz') - yield assert_equal, fname, desired - # filename with suffix and no file extension change - fname = cmd._gen_fname('foo.nii.gz', suffix='_brain.mat', - change_ext=False) - desired = os.path.join(pth, 'foo_brain.mat') - yield assert_equal, fname, desired + cmd.inputs.output_type = out_type + yield assert_equal, cmd.inputs.output_type_, fsl.Info.ftypes[out_type] diff --git a/nipype/interfaces/fsl/tests/test_epi.py b/nipype/interfaces/fsl/tests/test_epi.py deleted file mode 100644 index 4f2b0ed2c3..0000000000 --- a/nipype/interfaces/fsl/tests/test_epi.py +++ /dev/null @@ -1,63 +0,0 @@ -# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- -# vi: set ft=python sts=4 ts=4 sw=4 et: -import os - -from tempfile import mkdtemp -from shutil import rmtree - -import numpy as np - -import nibabel as nb - -from nipype.testing import (assert_equal, assert_not_equal, - assert_raises, skipif) -import nipype.interfaces.fsl.epi as fsl -from nipype.interfaces.fsl import no_fsl - - -def create_files_in_directory(): - outdir = os.path.realpath(mkdtemp()) - cwd = os.getcwd() - os.chdir(outdir) - filelist = ['a.nii', 'b.nii'] - for f in filelist: - hdr = nb.Nifti1Header() - shape = (3, 3, 3, 4) - hdr.set_data_shape(shape) - img = np.random.random(shape) - nb.save(nb.Nifti1Image(img, np.eye(4), hdr), - os.path.join(outdir, f)) - return filelist, outdir, cwd - - -def clean_directory(outdir, old_wd): - if os.path.exists(outdir): - rmtree(outdir) - os.chdir(old_wd) - - -# test eddy_correct -@skipif(no_fsl) -def test_eddy_correct2(): - filelist, outdir, cwd = create_files_in_directory() - eddy = fsl.EddyCorrect() - - # make sure command gets called - yield assert_equal, eddy.cmd, 'eddy_correct' - - # test raising error with mandatory args absent - yield assert_raises, ValueError, eddy.run - - # .inputs based parameters setting - eddy.inputs.in_file = filelist[0] - eddy.inputs.out_file = 'foo_eddc.nii' - eddy.inputs.ref_num = 100 - yield assert_equal, eddy.cmdline, 'eddy_correct %s foo_eddc.nii 100' % filelist[0] - - # .run based parameter setting - eddy2 = fsl.EddyCorrect(in_file=filelist[0], out_file='foo_ec.nii', ref_num=20) - yield assert_equal, eddy2.cmdline, 'eddy_correct %s foo_ec.nii 20' % filelist[0] - - # test arguments for opt_map - # eddy_correct class doesn't have opt_map{} - clean_directory(outdir, cwd) diff --git a/nipype/interfaces/fsl/tests/test_maths.py b/nipype/interfaces/fsl/tests/test_maths.py index d4003e8d74..e9a245e56b 100644 --- a/nipype/interfaces/fsl/tests/test_maths.py +++ b/nipype/interfaces/fsl/tests/test_maths.py @@ -16,19 +16,6 @@ from nipype.interfaces.fsl.base import FSLCommand -def set_output_type(fsl_output_type): - prev_output_type = os.environ.get('FSLOUTPUTTYPE', None) - - if fsl_output_type is not None: - os.environ['FSLOUTPUTTYPE'] = fsl_output_type - elif 'FSLOUTPUTTYPE' in os.environ: - del os.environ['FSLOUTPUTTYPE'] - - FSLCommand.set_default_output_type(Info.output_type()) - - return prev_output_type - - def create_files_in_directory(): testdir = os.path.realpath(mkdtemp()) origdir = os.getcwd() @@ -43,7 +30,7 @@ def create_files_in_directory(): nb.save(nb.Nifti1Image(img, np.eye(4), hdr), os.path.join(testdir, f)) - out_ext = Info.output_type_to_ext(Info.output_type()) + out_ext = Info.ftypes[Info.output_type()] return filelist, testdir, origdir, out_ext @@ -54,12 +41,11 @@ def clean_directory(testdir, origdir): @skipif(no_fsl) -def test_maths_base(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_maths_base(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get some fslmaths - maths = fsl.MathsCommand() + maths = fsl.MathsCommand(output_type=fsl_output_type) # Test that we got what we wanted yield assert_equal, maths.cmd, "fslmaths" @@ -80,11 +66,11 @@ def test_maths_base(fsl_output_type=None): out_cmdline = "fslmaths a.nii " + os.path.join(testdir, out_file) + " -odt %s" duo_cmdline = "fslmaths -dt %s a.nii " + os.path.join(testdir, out_file) + " -odt %s" for dtype in dtypes: - foo = fsl.MathsCommand(in_file="a.nii", internal_datatype=dtype) + foo = fsl.MathsCommand(in_file="a.nii", internal_datatype=dtype, output_type=fsl_output_type) yield assert_equal, foo.cmdline, int_cmdline % dtype - bar = fsl.MathsCommand(in_file="a.nii", output_datatype=dtype) + bar = fsl.MathsCommand(in_file="a.nii", output_datatype=dtype, output_type=fsl_output_type) yield assert_equal, bar.cmdline, out_cmdline % dtype - foobar = fsl.MathsCommand(in_file="a.nii", internal_datatype=dtype, output_datatype=dtype) + foobar = fsl.MathsCommand(in_file="a.nii", internal_datatype=dtype, output_datatype=dtype, output_type=fsl_output_type) yield assert_equal, foobar.cmdline, duo_cmdline % (dtype, dtype) # Test that we can ask for an outfile name @@ -93,16 +79,14 @@ def test_maths_base(fsl_output_type=None): # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_changedt(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_changedt(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get some fslmaths - cdt = fsl.ChangeDataType() + cdt = fsl.ChangeDataType(output_type=fsl_output_type) # Test that we got what we wanted yield assert_equal, cdt.cmd, "fslmaths" @@ -121,21 +105,19 @@ def test_changedt(fsl_output_type=None): dtypes = ["float", "char", "int", "short", "double", "input"] cmdline = "fslmaths a.nii b.nii -odt %s" for dtype in dtypes: - foo = fsl.MathsCommand(in_file="a.nii", out_file="b.nii", output_datatype=dtype) + foo = fsl.MathsCommand(in_file="a.nii", out_file="b.nii", output_datatype=dtype, output_type=fsl_output_type) yield assert_equal, foo.cmdline, cmdline % dtype # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_threshold(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_threshold(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii") + thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, thresh.cmd, "fslmaths" @@ -150,11 +132,11 @@ def test_threshold(fsl_output_type=None): yield assert_equal, thresh.cmdline, cmdline % "-thr %.10f" % val val = "%.10f" % 42 - thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii", thresh=42, use_robust_range=True) + thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii", thresh=42, use_robust_range=True, output_type=fsl_output_type) yield assert_equal, thresh.cmdline, cmdline % ("-thrp " + val) thresh.inputs.use_nonzero_voxels = True yield assert_equal, thresh.cmdline, cmdline % ("-thrP " + val) - thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii", thresh=42, direction="above") + thresh = fsl.Threshold(in_file="a.nii", out_file="b.nii", thresh=42, direction="above", output_type=fsl_output_type) yield assert_equal, thresh.cmdline, cmdline % ("-uthr " + val) thresh.inputs.use_robust_range = True yield assert_equal, thresh.cmdline, cmdline % ("-uthrp " + val) @@ -163,16 +145,14 @@ def test_threshold(fsl_output_type=None): # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_meanimage(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_meanimage(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - meaner = fsl.MeanImage(in_file="a.nii", out_file="b.nii") + meaner = fsl.MeanImage(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, meaner.cmd, "fslmaths" @@ -187,21 +167,19 @@ def test_meanimage(fsl_output_type=None): yield assert_equal, meaner.cmdline, cmdline % dim # Test the auto naming - meaner = fsl.MeanImage(in_file="a.nii") + meaner = fsl.MeanImage(in_file="a.nii", output_type=fsl_output_type) yield assert_equal, meaner.cmdline, "fslmaths a.nii -Tmean %s" % os.path.join(testdir, "a_mean%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_maximage(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_maximage(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - maxer = fsl.MaxImage(in_file="a.nii", out_file="b.nii") + maxer = fsl.MaxImage(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, maxer.cmd, "fslmaths" @@ -216,21 +194,19 @@ def test_maximage(fsl_output_type=None): yield assert_equal, maxer.cmdline, cmdline % dim # Test the auto naming - maxer = fsl.MaxImage(in_file="a.nii") + maxer = fsl.MaxImage(in_file="a.nii", output_type=fsl_output_type) yield assert_equal, maxer.cmdline, "fslmaths a.nii -Tmax %s" % os.path.join(testdir, "a_max%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_smooth(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_smooth(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - smoother = fsl.IsotropicSmooth(in_file="a.nii", out_file="b.nii") + smoother = fsl.IsotropicSmooth(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, smoother.cmd, "fslmaths" @@ -248,21 +224,19 @@ def test_smooth(fsl_output_type=None): yield assert_equal, smoother.cmdline, cmdline % val # Test automatic naming - smoother = fsl.IsotropicSmooth(in_file="a.nii", sigma=5) + smoother = fsl.IsotropicSmooth(in_file="a.nii", sigma=5, output_type=fsl_output_type) yield assert_equal, smoother.cmdline, "fslmaths a.nii -s %.5f %s" % (5, os.path.join(testdir, "a_smooth%s" % out_ext)) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) -def test_mask(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) +def test_mask(fsl_output_type='NIFTI'): files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - masker = fsl.ApplyMask(in_file="a.nii", out_file="c.nii") + masker = fsl.ApplyMask(in_file="a.nii", out_file="c.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, masker.cmd, "fslmaths" @@ -275,21 +249,19 @@ def test_mask(fsl_output_type=None): yield assert_equal, masker.cmdline, "fslmaths a.nii -mas b.nii c.nii" # Test auto name generation - masker = fsl.ApplyMask(in_file="a.nii", mask_file="b.nii") + masker = fsl.ApplyMask(in_file="a.nii", mask_file="b.nii", output_type=fsl_output_type) yield assert_equal, masker.cmdline, "fslmaths a.nii -mas b.nii " + os.path.join(testdir, "a_masked%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_dilation(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - diller = fsl.DilateImage(in_file="a.nii", out_file="b.nii") + diller = fsl.DilateImage(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, diller.cmd, "fslmaths" @@ -319,21 +291,19 @@ def test_dilation(fsl_output_type=None): yield assert_equal, diller.cmdline, "fslmaths a.nii -kernel file kernel.txt -dilF b.nii" # Test that we don't need to request an out name - dil = fsl.DilateImage(in_file="a.nii", operation="max") + dil = fsl.DilateImage(in_file="a.nii", operation="max", output_type=fsl_output_type) yield assert_equal, dil.cmdline, "fslmaths a.nii -dilF %s" % os.path.join(testdir, "a_dil%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_erosion(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - erode = fsl.ErodeImage(in_file="a.nii", out_file="b.nii") + erode = fsl.ErodeImage(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, erode.cmd, "fslmaths" @@ -346,49 +316,45 @@ def test_erosion(fsl_output_type=None): yield assert_equal, erode.cmdline, "fslmaths a.nii -eroF b.nii" # Test that we don't need to request an out name - erode = fsl.ErodeImage(in_file="a.nii") + erode = fsl.ErodeImage(in_file="a.nii", output_type=fsl_output_type) yield assert_equal, erode.cmdline, "fslmaths a.nii -ero %s" % os.path.join(testdir, "a_ero%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_spatial_filter(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - filter = fsl.SpatialFilter(in_file="a.nii", out_file="b.nii") + spfilt = fsl.SpatialFilter(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command - yield assert_equal, filter.cmd, "fslmaths" + yield assert_equal, spfilt.cmd, "fslmaths" # Test that it fails without an operation - yield assert_raises, ValueError, filter.run + yield assert_raises, ValueError, spfilt.run # Test the different operations for op in ["mean", "meanu", "median"]: - filter.inputs.operation = op - yield assert_equal, filter.cmdline, "fslmaths a.nii -f%s b.nii" % op + spfilt.inputs.operation = op + yield assert_equal, spfilt.cmdline, "fslmaths a.nii -f%s b.nii" % op # Test that we don't need to ask for an out name - filter = fsl.SpatialFilter(in_file="a.nii", operation="mean") - yield assert_equal, filter.cmdline, "fslmaths a.nii -fmean %s" % os.path.join(testdir, "a_filt%s" % out_ext) + spfilt = fsl.SpatialFilter(in_file="a.nii", operation="mean", output_type=fsl_output_type) + yield assert_equal, spfilt.cmdline, "fslmaths a.nii -fmean %s" % os.path.join(testdir, "a_filt%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_unarymaths(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - maths = fsl.UnaryMaths(in_file="a.nii", out_file="b.nii") + maths = fsl.UnaryMaths(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, maths.cmd, "fslmaths" @@ -404,21 +370,19 @@ def test_unarymaths(fsl_output_type=None): # Test that we don't need to ask for an out file for op in ops: - maths = fsl.UnaryMaths(in_file="a.nii", operation=op) + maths = fsl.UnaryMaths(in_file="a.nii", operation=op, output_type=fsl_output_type) yield assert_equal, maths.cmdline, "fslmaths a.nii -%s %s" % (op, os.path.join(testdir, "a_%s%s" % (op, out_ext))) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_binarymaths(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - maths = fsl.BinaryMaths(in_file="a.nii", out_file="c.nii") + maths = fsl.BinaryMaths(in_file="a.nii", out_file="c.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, maths.cmd, "fslmaths" @@ -431,7 +395,7 @@ def test_binarymaths(fsl_output_type=None): operands = ["b.nii", -2, -0.5, 0, .123456, np.pi, 500] for op in ops: for ent in operands: - maths = fsl.BinaryMaths(in_file="a.nii", out_file="c.nii", operation=op) + maths = fsl.BinaryMaths(in_file="a.nii", out_file="c.nii", operation=op, output_type=fsl_output_type) if ent == "b.nii": maths.inputs.operand_file = ent yield assert_equal, maths.cmdline, "fslmaths a.nii -%s b.nii c.nii" % op @@ -441,21 +405,19 @@ def test_binarymaths(fsl_output_type=None): # Test that we don't need to ask for an out file for op in ops: - maths = fsl.BinaryMaths(in_file="a.nii", operation=op, operand_file="b.nii") + maths = fsl.BinaryMaths(in_file="a.nii", operation=op, operand_file="b.nii", output_type=fsl_output_type) yield assert_equal, maths.cmdline, "fslmaths a.nii -%s b.nii %s" % (op, os.path.join(testdir, "a_maths%s" % out_ext)) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_multimaths(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - maths = fsl.MultiImageMaths(in_file="a.nii", out_file="c.nii") + maths = fsl.MultiImageMaths(in_file="a.nii", out_file="c.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, maths.cmd, "fslmaths" @@ -473,22 +435,20 @@ def test_multimaths(fsl_output_type=None): yield assert_equal, maths.cmdline, "fslmaths a.nii %s c.nii" % ostr % ("a.nii", "b.nii") # Test that we don't need to ask for an out file - maths = fsl.MultiImageMaths(in_file="a.nii", op_string="-add %s -mul 5", operand_files=["b.nii"]) + maths = fsl.MultiImageMaths(in_file="a.nii", op_string="-add %s -mul 5", operand_files=["b.nii"], output_type=fsl_output_type) yield assert_equal, maths.cmdline, \ "fslmaths a.nii -add b.nii -mul 5 %s" % os.path.join(testdir, "a_maths%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) def test_tempfilt(fsl_output_type=None): - prev_type = set_output_type(fsl_output_type) files, testdir, origdir, out_ext = create_files_in_directory() # Get the command - filt = fsl.TemporalFilter(in_file="a.nii", out_file="b.nii") + filt = fsl.TemporalFilter(in_file="a.nii", out_file="b.nii", output_type=fsl_output_type) # Test the underlying command yield assert_equal, filt.cmd, "fslmaths" @@ -504,13 +464,12 @@ def test_tempfilt(fsl_output_type=None): yield assert_equal, filt.cmdline, "fslmaths a.nii -bptf %.6f %.6f b.nii" % win # Test that we don't need to ask for an out file - filt = fsl.TemporalFilter(in_file="a.nii", highpass_sigma=64) + filt = fsl.TemporalFilter(in_file="a.nii", highpass_sigma=64, output_type=fsl_output_type) yield assert_equal, filt.cmdline, \ "fslmaths a.nii -bptf 64.000000 -1.000000 %s" % os.path.join(testdir, "a_filt%s" % out_ext) # Clean up our mess clean_directory(testdir, origdir) - set_output_type(prev_type) @skipif(no_fsl) diff --git a/nipype/interfaces/fsl/utils.py b/nipype/interfaces/fsl/utils.py index 1892466989..b05d6ce46f 100644 --- a/nipype/interfaces/fsl/utils.py +++ b/nipype/interfaces/fsl/utils.py @@ -27,26 +27,27 @@ import numpy as np from .base import FSLCommand, FSLCommandInputSpec, Info -from ..base import (traits, TraitedSpec, OutputMultiPath, File, - CommandLine, CommandLineInputSpec, isdefined) +from ..base import (traits, TraitedSpec, InputMultiPath, OutputMultiPath, + File, GenFile, CommandLine, CommandLineInputSpec, + isdefined, Undefined) from ...utils.filemanip import (load_json, save_json, split_filename, fname_presuffix, copyfile) - -warn = warnings.warn +from ... import logging +IFLOGGER = logging.getLogger('interface') class CopyGeomInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, mandatory=True, argstr="%s", position=0, - desc="source image") - dest_file = File(exists=True, mandatory=True, argstr="%s", position=1, - desc="destination image", copyfile=True, output_name='out_file', - name_source='dest_file', name_template='%s') - ignore_dims = traits.Bool(desc=('Do not copy image dimensions'), - argstr='-d', position="-1") + in_file = File(exists=True, mandatory=True, argstr='%s', position=0, + desc='source image') + dest_file = File(exists=True, mandatory=True, argstr='%s', position=1, + desc='destination image', copyfile=True, + output_name='out_file') + ignore_dims = traits.Bool(False, usedefault=True, argstr='-d', position=-1, + desc='Do not copy image dimensions') class CopyGeomOutputSpec(TraitedSpec): - out_file = File(exists=True, desc="image with new geometry header") + out_file = File(exists=True, desc='image with new geometry header') class CopyGeom(FSLCommand): @@ -57,22 +58,20 @@ class CopyGeom(FSLCommand): or Nifti to Nifti will work properly. Copying from different files will result in loss of information or potentially incorrect settings. """ - _cmd = "fslcpgeom" + _cmd = 'fslcpgeom' input_spec = CopyGeomInputSpec output_spec = CopyGeomOutputSpec class RobustFOVInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, - desc='input filename', - argstr='-i %s', position=0, mandatory=True) - out_roi = File(desc="ROI volume output name", argstr="-r %s", - name_source=['in_file'], hash_files=False, - name_template='%s_ROI') + in_file = File(exists=True, argstr='-i %s', position=0, mandatory=True, + desc='input filename') + out_roi = GenFile(template='{in_file}_ROI{output_type_}', argstr='-r %s', + hash_files=False, desc='ROI volume output name') class RobustFOVOutputSpec(TraitedSpec): - out_roi = File(exists=True, desc="ROI volume output name") + out_roi = File(exists=True, desc='ROI volume output name') class RobustFOV(FSLCommand): @@ -82,28 +81,27 @@ class RobustFOV(FSLCommand): class ImageMeantsInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, - desc='input file for computing the average timeseries', - argstr='-i %s', position=0, mandatory=True) - out_file = File(desc='name of output text matrix', - argstr='-o %s', genfile=True, hash_files=False) + in_file = File(exists=True, argstr='-i %s', position=0, mandatory=True, + desc='input file for computing the average timeseries') + out_file = GenFile(template='{in_file}_ts.txt', argstr='-o %s', + hash_files=False, desc='name of output text matrix') mask = File(exists=True, desc='input 3D mask', argstr='-m %s') spatial_coord = traits.List(traits.Int, - desc=(' requested spatial coordinate ' - '(instead of mask)'), + desc=' requested spatial coordinate ' + '(instead of mask)', argstr='-c %s') - use_mm = traits.Bool(desc=('use mm instead of voxel coordinates (for -c ' - 'option)'), argstr='--usemm') - show_all = traits.Bool(desc=('show all voxel time series (within mask) ' - 'instead of averaging'), argstr='--showall') - eig = traits.Bool(desc=('calculate Eigenvariate(s) instead of mean (output ' - 'will have 0 mean)'), argstr='--eig') + use_mm = traits.Bool(desc='use mm instead of voxel coordinates (for -c ' + 'option)', argstr='--usemm') + show_all = traits.Bool(desc='show all voxel time series (within mask) ' + 'instead of averaging', argstr='--showall') + eig = traits.Bool(desc='calculate Eigenvariate(s) instead of mean (output ' + 'will have 0 mean)', argstr='--eig') order = traits.Int(1, desc='select number of Eigenvariates', argstr='--order=%d', usedefault=True) - nobin = traits.Bool(desc=('do not binarise the mask for calculation of ' - 'Eigenvariates'), argstr='--no_bin') - transpose = traits.Bool(desc=('output results in transpose format (one row ' - 'per voxel/mean)'), argstr='--transpose') + nobin = traits.Bool(desc='do not binarise the mask for calculation of ' + 'Eigenvariates', argstr='--no_bin') + transpose = traits.Bool(desc='output results in transpose format (one row ' + 'per voxel/mean)', argstr='--transpose') class ImageMeantsOutputSpec(TraitedSpec): @@ -120,34 +118,22 @@ class ImageMeants(FSLCommand): input_spec = ImageMeantsInputSpec output_spec = ImageMeantsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): - outputs['out_file'] = self._gen_fname(self.inputs.in_file, - suffix='_ts', - ext='.txt', - change_ext=True) - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()[name] - return None - class SmoothInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, argstr="%s", position=0, mandatory=True) + in_file = File(exists=True, argstr='%s', position=0, mandatory=True) + smoothed_file = GenFile(template='{in_file}_smooth{output_type_}', argstr='%s', + position=2, hash_files=False, desc='smoothed, output file') sigma = traits.Float( - argstr="-kernel gauss %.03f -fmean", position=1, xor=['fwhm'], mandatory=True, + argstr='-kernel gauss %.03f -fmean', position=1, xor=['fwhm'], mandatory=True, desc='gaussian kernel sigma in mm (not voxels)') fwhm = traits.Float( - argstr="-kernel gauss %.03f -fmean", position=1, xor=['sigma'], mandatory=True, + argstr='-kernel gauss %.03f -fmean', position=1, xor=['sigma'], mandatory=True, desc='gaussian kernel fwhm, will be converted to sigma in mm (not voxels)') - smoothed_file = File( - argstr="%s", position=2, name_source=['in_file'], name_template='%s_smooth', hash_files=False) + def _format_arg(self, name, trait_spec, value): + if name == 'fwhm': + value = float(value) / np.sqrt(8 * np.log(2)) + return super(SmoothInputSpec, self)._format_arg(name, trait_spec, value) class SmoothOutputSpec(TraitedSpec): smoothed_file = File(exists=True) @@ -162,6 +148,7 @@ class Smooth(FSLCommand): Setting the kernel width using sigma: + >>> from nipype.interfaces.fsl import Smooth >>> sm = Smooth() >>> sm.inputs.in_file = 'functional2.nii' >>> sm.inputs.sigma = 8.0 @@ -178,13 +165,12 @@ class Smooth(FSLCommand): One of sigma or fwhm must be set: - >>> from nipype.interfaces.fsl import Smooth >>> sm = Smooth() >>> sm.inputs.in_file = 'functional2.nii' >>> sm.cmdline #doctest: +ELLIPSIS Traceback (most recent call last): ... - ValueError: Smooth requires a value for one of the inputs ... + ValueError: ... """ @@ -192,29 +178,34 @@ class Smooth(FSLCommand): output_spec = SmoothOutputSpec _cmd = 'fslmaths' - def _format_arg(self, name, trait_spec, value): - if name == 'fwhm': - sigma = float(value) / np.sqrt(8 * np.log(2)) - return super(Smooth, self)._format_arg(name, trait_spec, sigma) - return super(Smooth, self)._format_arg(name, trait_spec, value) - class MergeInputSpec(FSLCommandInputSpec): - in_files = traits.List(File(exists=True), argstr="%s", position=2, - mandatory=True) - dimension = traits.Enum('t', 'x', 'y', 'z', 'a', argstr="-%s", position=0, - desc=("dimension along which to merge, optionally " - "set tr input when dimension is t"), - mandatory=True) + in_files = InputMultiPath(File(exists=True), argstr='%s', position=2, + mandatory=True) + dimension = traits.Enum( + 't', 'x', 'y', 'z', 'a', argstr='-%s', position=0, mandatory=True, + desc='dimension along which to merge, optionally set tr input when' + ' dimension is t') tr = traits.Float(position=-1, argstr='%.2f', - desc=('use to specify TR in seconds (default is 1.00 ' - 'sec), overrides dimension and sets it to tr')) - merged_file = File(argstr="%s", position=1, name_source='in_files', - name_template='%s_merged', hash_files=False) + desc='use to specify TR in seconds (default is 1.00 sec), ' + 'overrides dimension and sets it to tr') + merged_file = GenFile( + template='{in_files[0]}_merged{output_type_}', argstr='%s', position=1, + hash_files=False, desc='output, merged file') + def _format_arg(self, name, spec, value): + if name == 'tr': + if self.dimension != 't': + raise ValueError('When TR is specified, dimension must be t') + return spec.argstr % value + if name == 'dimension': + if isdefined(self.tr): + return '-tr' + return spec.argstr % value + return super(MergeInputSpec, self)._format_arg(name, spec, value) class MergeOutputSpec(TraitedSpec): - merged_file = File(exists=True) + merged_file = File(exists=True, desc='output, merged file') class Merge(FSLCommand): @@ -248,40 +239,33 @@ class Merge(FSLCommand): input_spec = MergeInputSpec output_spec = MergeOutputSpec - def _format_arg(self, name, spec, value): - if name == 'tr': - if self.inputs.dimension != 't': - raise ValueError('When TR is specified, dimension must be t') - return spec.argstr % value - if name == 'dimension': - if isdefined(self.inputs.tr): - return '-tr' - return spec.argstr % value - return super(Merge, self)._format_arg(name, spec, value) - class ExtractROIInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, argstr="%s", - position=0, desc="input file", mandatory=True) - roi_file = File(argstr="%s", position=1, - desc="output file", genfile=True, hash_files=False) - x_min = traits.Int(argstr="%d", position=2) - x_size = traits.Int(argstr="%d", position=3) - y_min = traits.Int(argstr="%d", position=4) - y_size = traits.Int(argstr="%d", position=5) - z_min = traits.Int(argstr="%d", position=6) - z_size = traits.Int(argstr="%d", position=7) - t_min = traits.Int(argstr="%d", position=8) - t_size = traits.Int(argstr="%d", position=9) + in_file = File(exists=True, argstr='%s', + position=0, desc='input file', mandatory=True) + roi_file = GenFile(template='{in_file}_roi{output_type_}', argstr='%s', position=1, + hash_files=False, desc='output file') + x_min = traits.Int(argstr='%d', position=2) + x_size = traits.Int(argstr='%d', position=3) + y_min = traits.Int(argstr='%d', position=4) + y_size = traits.Int(argstr='%d', position=5) + z_min = traits.Int(argstr='%d', position=6) + z_size = traits.Int(argstr='%d', position=7) + t_min = traits.Int(argstr='%d', position=8) + t_size = traits.Int(argstr='%d', position=9) _crop_xor = ['x_min', 'x_size', 'y_min', 'y_size', 'z_min', 'z_size', 't_min', 't_size'] crop_list = traits.List(traits.Tuple(traits.Int, traits.Int), - argstr="%s", position=2, xor=_crop_xor, - desc="list of two tuples specifying crop options") + argstr='%s', position=2, xor=_crop_xor, + desc='list of two tuples specifying crop options') + def _format_arg(self, name, spec, value): + if name == 'crop_list': + return ' '.join(map(str, sum(list(map(list, value)), []))) + return super(ExtractROIInputSpec, self)._format_arg(name, spec, value) class ExtractROIOutputSpec(TraitedSpec): - roi_file = File(exists=True) + roi_file = File(exists=True, desc='output file') class ExtractROI(FSLCommand): @@ -301,11 +285,12 @@ class ExtractROI(FSLCommand): -------- >>> from nipype.interfaces.fsl import ExtractROI - >>> from nipype.testing import anatfile - >>> fslroi = ExtractROI(in_file=anatfile, roi_file='bar.nii', t_min=0, - ... t_size=1) - >>> fslroi.cmdline == 'fslroi %s bar.nii 0 1' % anatfile - True + >>> fslroi = ExtractROI() + >>> fslroi.inputs.in_file = 'functional.nii' + >>> fslroi.inputs.t_min = 0 + >>> fslroi.inputs.t_size = 1 + >>> fslroi.cmdline + 'fslroi functional.nii functional_roi.nii.gz 0 1' """ @@ -314,50 +299,14 @@ class ExtractROI(FSLCommand): input_spec = ExtractROIInputSpec output_spec = ExtractROIOutputSpec - def _format_arg(self, name, spec, value): - - if name == "crop_list": - return " ".join(map(str, sum(list(map(list, value)), []))) - return super(ExtractROI, self)._format_arg(name, spec, value) - - def _list_outputs(self): - """Create a Bunch which contains all possible files generated - by running the interface. Some files are always generated, others - depending on which ``inputs`` options are set. - - - Returns - ------- - - outputs : Bunch object - Bunch object containing all possible files generated by - interface object. - - If None, file was not generated - Else, contains path, filename of generated outputfile - - """ - outputs = self._outputs().get() - outputs['roi_file'] = self.inputs.roi_file - if not isdefined(outputs['roi_file']): - outputs['roi_file'] = self._gen_fname(self.inputs.in_file, - suffix='_roi') - outputs['roi_file'] = os.path.abspath(outputs['roi_file']) - return outputs - - def _gen_filename(self, name): - if name == 'roi_file': - return self._list_outputs()[name] - return None - class SplitInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, argstr="%s", position=0, mandatory=True, - desc="input filename") - out_base_name = traits.Str(argstr="%s", position=1, desc="outputs prefix") - dimension = traits.Enum('t', 'x', 'y', 'z', argstr="-%s", position=2, + in_file = File(exists=True, argstr='%s', position=0, mandatory=True, + desc='input filename') + out_base_name = traits.Str(argstr='%s', position=1, desc='outputs prefix') + dimension = traits.Enum('t', 'x', 'y', 'z', argstr='-%s', position=2, mandatory=True, - desc="dimension along which the file will be split") + desc='dimension along which the file will be split') class SplitOutputSpec(TraitedSpec): @@ -372,43 +321,28 @@ class Split(FSLCommand): input_spec = SplitInputSpec output_spec = SplitOutputSpec - def _list_outputs(self): - """Create a Bunch which contains all possible files generated - by running the interface. Some files are always generated, others - depending on which ``inputs`` options are set. - - Returns - ------- - - outputs : Bunch object - Bunch object containing all possible files generated by - interface object. - - If None, file was not generated - Else, contains path, filename of generated outputfile - - """ - outputs = self._outputs().get() - ext = Info.output_type_to_ext(self.inputs.output_type) + def _post_run(self): + ext = self.inputs.output_type_ outbase = 'vol*' if isdefined(self.inputs.out_base_name): outbase = '%s*' % self.inputs.out_base_name - outputs['out_files'] = sorted(glob(os.path.join(os.getcwd(), - outbase + ext))) - return outputs + self.outputs.out_files = sorted(glob(os.path.join(os.getcwd(), + outbase + ext))) class ImageMathsInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, argstr="%s", mandatory=True, position=1) - in_file2 = File(exists=True, argstr="%s", position=3) - out_file = File(argstr="%s", position=4, genfile=True, hash_files=False) - op_string = traits.Str(argstr="%s", position=2, - desc="string defining the operation, i. e. -add") - suffix = traits.Str(desc="out_file suffix") - out_data_type = traits.Enum('char', 'short', 'int', 'float', 'double', - 'input', argstr="-odt %s", position=5, - desc=("output datatype, one of (char, short, " - "int, float, double, input)")) + in_file = File(exists=True, argstr='%s', mandatory=True, position=1) + in_file2 = File(exists=True, argstr='%s', position=3) + out_file = GenFile( + template='{in_file}_maths{output_type_}', argstr='%s', position=4, + hash_files=False) + op_string = traits.Str(argstr='%s', position=2, + desc='string defining the operation, i. e. -add') + suffix = traits.Str(desc='out_file suffix', deprecated=True) + out_data_type = traits.Enum('char', 'short', 'int', 'float', 'double', 'input', + argstr='-odt %s', position=5, + desc='output datatype, one of (char, short, ' + 'int, float, double, input)') class ImageMathsOutputSpec(TraitedSpec): @@ -425,68 +359,60 @@ class ImageMaths(FSLCommand): >>> from nipype.interfaces import fsl >>> from nipype.testing import anatfile - >>> maths = fsl.ImageMaths(in_file=anatfile, op_string= '-add 5', - ... out_file='foo_maths.nii') - >>> maths.cmdline == 'fslmaths %s -add 5 foo_maths.nii' % anatfile - True + >>> maths = fsl.ImageMaths() + >>> maths.inputs.in_file = 'anatomical.nii' + >>> maths.inputs.op_string= '-add 5' + >>> maths.cmdline + 'fslmaths anatomical.nii -add 5 anatomical_maths.nii.gz' """ input_spec = ImageMathsInputSpec output_spec = ImageMathsOutputSpec - _cmd = 'fslmaths' - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()[name] - return None - - def _parse_inputs(self, skip=None): - return super(ImageMaths, self)._parse_inputs(skip=['suffix']) - - def _list_outputs(self): - suffix = '_maths' # ohinds: build suffix - if isdefined(self.inputs.suffix): - suffix = self.inputs.suffix - outputs = self._outputs().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): - outputs['out_file'] = self._gen_fname(self.inputs.in_file, - suffix=suffix) - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs - class FilterRegressorInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, argstr="-i %s", - desc="input file name (4D image)", mandatory=True, + in_file = File(exists=True, argstr='-i %s', + desc='input file name (4D image)', mandatory=True, position=1) - out_file = File(argstr="-o %s", - desc="output file name for the filtered data", - genfile=True, position=2, hash_files=False) - design_file = File(exists=True, argstr="-d %s", position=3, mandatory=True, - desc=("name of the matrix with time courses (e.g. GLM " - "design or MELODIC mixing matrix)")) - filter_columns = traits.List(traits.Int, argstr="-f '%s'", - xor=["filter_all"], mandatory=True, + out_file = File(template='{in_file}_regfilt{output_type_}', + argstr='-o %s', position=2, hash_files=False, + desc='output file name for the filtered data') + design_file = File(exists=True, argstr='-d %s', position=3, mandatory=True, + desc='name of the matrix with time courses (e.g. GLM ' + 'design or MELODIC mixing matrix)') + filter_columns = traits.List(traits.Int, argstr='-f \'%s\'', + xor=['filter_all'], mandatory=True, position=4, - desc=("(1-based) column indices to filter out " - "of the data")) - filter_all = traits.Bool(mandatory=True, argstr="-f '%s'", - xor=["filter_columns"], position=4, - desc=("use all columns in the design file in " - "denoising")) - mask = File(exists=True, argstr="-m %s", desc="mask image file name") - var_norm = traits.Bool(argstr="--vn", - desc="perform variance-normalization on data") - out_vnscales = traits.Bool(argstr="--out_vnscales", - desc=("output scaling factors for variance " - "normalization")) + desc='(1-based) column indices to filter out ' + 'of the data') + filter_all = traits.Bool(mandatory=True, argstr='-f \'%s\'', + xor=['filter_columns'], position=4, + desc='use all columns in the design file in ' + 'denoising') + mask = File(exists=True, argstr='-m %s', desc='mask image file name') + var_norm = traits.Bool(argstr='--vn', + desc='perform variance-normalization on data') + out_vnscales = traits.Bool(argstr='--out_vnscales', + desc='output scaling factors for variance ' + 'normalization') + + def _format_arg(self, name, trait_spec, value): + if name == 'filter_columns': + return trait_spec.argstr % ','.join(map(str, value)) + elif name == 'filter_all': + design = np.loadtxt(self.design_file) + try: + n_cols = design.shape[1] + except IndexError: + n_cols = 1 + return trait_spec.argstr % ','.join(map(str, list(range(1, n_cols + 1)))) + return super(FilterRegressorInputSpec, self)._format_arg(name, trait_spec, value) class FilterRegressorOutputSpec(TraitedSpec): - out_file = File(exists=True, desc="output file name for the filtered data") + out_file = File(exists=True, desc='output file name for the filtered data') class FilterRegressor(FSLCommand): @@ -498,47 +424,33 @@ class FilterRegressor(FSLCommand): output_spec = FilterRegressorOutputSpec _cmd = 'fsl_regfilt' - def _format_arg(self, name, trait_spec, value): - if name == 'filter_columns': - return trait_spec.argstr % ",".join(map(str, value)) - elif name == "filter_all": - design = np.loadtxt(self.inputs.design_file) - try: - n_cols = design.shape[1] - except IndexError: - n_cols = 1 - return trait_spec.argstr % ",".join(map(str, list(range(1, n_cols + 1)))) - return super(FilterRegressor, self)._format_arg(name, trait_spec, value) - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): - outputs['out_file'] = self._gen_fname( - self.inputs.in_file, suffix='_regfilt') - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()[name] - return None - class ImageStatsInputSpec(FSLCommandInputSpec): split_4d = traits.Bool(argstr='-t', position=1, - desc=('give a separate output line for each 3D ' - 'volume of a 4D timeseries')) - in_file = File(exists=True, argstr="%s", mandatory=True, position=2, + desc='give a separate output line for each 3D ' + 'volume of a 4D timeseries') + in_file = File(exists=True, argstr='%s', mandatory=True, position=2, desc='input file to generate stats of') - op_string = traits.Str(argstr="%s", mandatory=True, position=3, - desc=("string defining the operation, options are " - "applied in order, e.g. -M -l 10 -M will " - "report the non-zero mean, apply a threshold " - "and then report the new nonzero mean")) - mask_file = File(exists=True, argstr="", + op_string = traits.Str(argstr='%s', mandatory=True, position=3, + desc='string defining the operation, options are ' + 'applied in order, e.g. -M -l 10 -M will ' + 'report the non-zero mean, apply a threshold ' + 'and then report the new nonzero mean') + mask_file = File(exists=True, argstr='-k %s', desc='mask file used for option -k %s') + def _format_arg(self, name, trait_spec, value): + if name == 'mask_file': + return '' + if name == 'op_string': + if '-k %s' in value: + if isdefined(self.mask_file): + return self.op_string % self.mask_file + else: + raise ValueError( + '-k %s option in op_string requires mask_file') + return super(ImageStatsInputSpec, self)._format_arg(name, trait_spec, value) + class ImageStatsOutputSpec(TraitedSpec): out_stat = traits.Any(desc='stats output') @@ -566,17 +478,7 @@ class ImageStats(FSLCommand): _cmd = 'fslstats' - def _format_arg(self, name, trait_spec, value): - if name == 'mask_file': - return '' - if name == 'op_string': - if '-k %s' in self.inputs.op_string: - if isdefined(self.inputs.mask_file): - return self.inputs.op_string % self.inputs.mask_file - else: - raise ValueError( - '-k %s option in op_string requires mask_file') - return super(ImageStats, self)._format_arg(name, trait_spec, value) + def aggregate_outputs(self, runtime=None, needed_outputs=None): outputs = self._outputs() @@ -600,11 +502,10 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): out_stat = out_stat[0] save_json(outfile, dict(stat=out_stat)) outputs.out_stat = out_stat - return outputs class AvScaleInputSpec(FSLCommandInputSpec): - mat_file = File(exists=True, argstr="%s", + mat_file = File(exists=True, argstr='%s', desc='mat file to read', position=0) @@ -654,18 +555,17 @@ def lines_to_float(lines): out = runtime.stdout.split('\n') outputs.rotation_translation_matrix = lines_to_float(out[1:5]) - outputs.scales = lines_to_float([out[6].split(" = ")[1]]) - outputs.skews = lines_to_float([out[8].split(" = ")[1]]) - outputs.average_scaling = lines_to_float([out[10].split(" = ")[1]]) - outputs.determinant = lines_to_float([out[12].split(" = ")[1]]) - if out[13].split(": ")[1] == 'preserved': + outputs.scales = lines_to_float([out[6].split(' = ')[1]]) + outputs.skews = lines_to_float([out[8].split(' = ')[1]]) + outputs.average_scaling = lines_to_float([out[10].split(' = ')[1]]) + outputs.determinant = lines_to_float([out[12].split(' = ')[1]]) + if out[13].split(': ')[1] == 'preserved': outputs.left_right_orientation_preserved = True else: outputs.left_right_orientation_preserved = False outputs.forward_half_transform = lines_to_float(out[16:20]) outputs.backward_half_transform = lines_to_float(out[22:-1]) - return outputs class OverlayInputSpec(FSLCommandInputSpec): @@ -680,8 +580,8 @@ class OverlayInputSpec(FSLCommandInputSpec): background_image = File(exists=True, position=4, mandatory=True, argstr='%s', desc='image to use as background') _xor_inputs = ('auto_thresh_bg', 'full_bg_range', 'bg_thresh') - auto_thresh_bg = traits.Bool(desc=('automatically threshold the background ' - 'image'), + auto_thresh_bg = traits.Bool(desc='automatically threshold the background ' + 'image', argstr='-a', position=5, xor=_xor_inputs, mandatory=True) full_bg_range = traits.Bool(desc='use full range of background image', @@ -695,20 +595,38 @@ class OverlayInputSpec(FSLCommandInputSpec): desc='statistical image to overlay in color') stat_thresh = traits.Tuple(traits.Float, traits.Float, position=7, mandatory=True, argstr='%.2f %.2f', - desc=('min and max values for the statistical ' - 'overlay')) - show_negative_stats = traits.Bool(desc=('display negative statistics in ' - 'overlay'), xor=['stat_image2'], + desc='min and max values for the statistical ' + 'overlay') + show_negative_stats = traits.Bool(desc='display negative statistics in ' + 'overlay', xor=['stat_image2'], argstr='%s', position=8) stat_image2 = File(exists=True, position=9, xor=['show_negative_stats'], argstr='%s', desc='second statistical image to overlay in color') stat_thresh2 = traits.Tuple(traits.Float, traits.Float, position=10, - desc=('min and max values for second ' - 'statistical overlay'), + desc='min and max values for second ' + 'statistical overlay', argstr='%.2f %.2f') - out_file = File(desc='combined image volume', - position=-1, argstr='%s', genfile=True, hash_files=False) + out_file = GenFile(template='overlay{output_type_}', position=-1, argstr='%s', + hash_files=False, desc='combined image volume') + + + def _format_arg(self, name, spec, value): + if name == 'transparency': + if value: + return '1' + else: + return '0' + if name == 'out_type': + if value == 'float': + return '0' + else: + return '1' + if name == 'show_negative_stats': + return '%s %.2f %.2f' % (self.stat_image, + self.stat_thresh[0] * -1, + self.stat_thresh[1] * -1) + return super(OverlayInputSpec, self)._format_arg(name, spec, value) class OverlayOutputSpec(TraitedSpec): @@ -730,6 +648,8 @@ class Overlay(FSLCommand): >>> combine.inputs.stat_image = 'zstat1.nii.gz' >>> combine.inputs.stat_thresh = (3.5, 10) >>> combine.inputs.show_negative_stats = True + >>> combine.cmdline + 'overlay 1 0 mean_func.nii.gz -a zstat1.nii.gz 3.50 10.00 zstat1.nii.gz -3.50 -10.00 overlay.nii.gz' >>> res = combine.run() #doctest: +SKIP @@ -738,93 +658,65 @@ class Overlay(FSLCommand): input_spec = OverlayInputSpec output_spec = OverlayOutputSpec - def _format_arg(self, name, spec, value): - if name == 'transparency': - if value: - return '1' - else: - return '0' - if name == 'out_type': - if value == 'float': - return '0' - else: - return '1' - if name == 'show_negative_stats': - return '%s %.2f %.2f' % (self.inputs.stat_image, - self.inputs.stat_thresh[0] * -1, - self.inputs.stat_thresh[1] * -1) - return super(Overlay, self)._format_arg(name, spec, value) - - def _list_outputs(self): - outputs = self._outputs().get() - out_file = self.inputs.out_file - if not isdefined(out_file): - if isdefined(self.inputs.stat_image2) and ( - not isdefined(self.inputs.show_negative_stats) or not - self.inputs.show_negative_stats): - stem = "%s_and_%s" % (split_filename(self.inputs.stat_image)[1], - split_filename(self.inputs.stat_image2)[1]) - else: - stem = split_filename(self.inputs.stat_image)[1] - out_file = self._gen_fname(stem, suffix='_overlay') - outputs['out_file'] = os.path.abspath(out_file) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()['out_file'] - return None - class SlicerInputSpec(FSLCommandInputSpec): in_file = File(exists=True, position=1, argstr='%s', mandatory=True, desc='input volume') image_edges = File(exists=True, position=2, argstr='%s', - desc=('volume to display edge overlay for (useful for ' - 'checking registration')) + desc='volume to display edge overlay for (useful for ' + 'checking registration') label_slices = traits.Bool( - position=3, argstr='-L', desc='display slice number', - usedefault=True, default_value=True) + True, position=3, argstr='-L', desc='display slice number', + usedefault=True, mandatory=True) colour_map = File(exists=True, position=4, argstr='-l %s', - desc=('use different colour map from that stored in ' - 'nifti header')) + desc='use different colour map from that stored in ' + 'nifti header') intensity_range = traits.Tuple(traits.Float, traits.Float, position=5, argstr='-i %.3f %.3f', desc='min and max intensities to display') threshold_edges = traits.Float(position=6, argstr='-e %.3f', desc='use threshold for edges') dither_edges = traits.Bool(position=7, argstr='-t', - desc=('produce semi-transparent (dithered) ' - 'edges')) + desc='produce semi-transparent (dithered) ' + 'edges') nearest_neighbour = traits.Bool(position=8, argstr='-n', - desc=('use nearest neighbor interpolation ' - 'for output')) - show_orientation = traits.Bool(position=9, argstr='%s', usedefault=True, - default_value=True, + desc='use nearest neighbor interpolation ' + 'for output') + hide_orientation = traits.Bool(False, position=9, argstr='-u', usedefault=True, desc='label left-right orientation') + show_orientation = traits.Bool(True, position=9, deprecated=True, + new_name='hide_orientation') + _xor_options = ('single_slice', 'middle_slices', 'all_axial', 'sample_axial') single_slice = traits.Enum('x', 'y', 'z', position=10, argstr='-%s', xor=_xor_options, requires=['slice_number'], - desc=('output picture of single slice in the x, ' - 'y, or z plane')) + desc='output picture of single slice in the x, ' + 'y, or z plane') slice_number = traits.Int(position=11, argstr='-%d', desc='slice number to save in picture') middle_slices = traits.Bool(position=10, argstr='-a', xor=_xor_options, - desc=('output picture of mid-sagittal, axial, ' - 'and coronal slices')) + desc='output picture of mid-sagittal, axial, ' + 'and coronal slices') all_axial = traits.Bool(position=10, argstr='-A', xor=_xor_options, requires=['image_width'], desc='output all axial slices into one picture') sample_axial = traits.Int(position=10, argstr='-S %d', xor=_xor_options, requires=['image_width'], - desc=('output every n axial slices into one ' - 'picture')) + desc='output every n axial slices into one ' + 'picture') image_width = traits.Int(position=-2, argstr='%d', desc='max picture width') - out_file = File(position=-1, genfile=True, argstr='%s', - desc='picture to write', hash_files=False) scaling = traits.Float(position=0, argstr='-s %f', desc='image scale') + out_file = GenFile(template='{in_file}.png', position=-1, argstr='%s', + desc='picture to write', hash_files=False) + + + def _format_arg(self, name, spec, value): + if name == 'show_orientation': + return None if value else '-u' + return super(SlicerInputSpec, self)._format_arg(name, spec, value) + class SlicerOutputSpec(TraitedSpec): out_file = File(exists=True, desc='picture to write') @@ -838,11 +730,12 @@ class Slicer(FSLCommand): -------- >>> from nipype.interfaces import fsl - >>> from nipype.testing import example_data >>> slice = fsl.Slicer() - >>> slice.inputs.in_file = example_data('functional.nii') + >>> slice.inputs.in_file = 'functional.nii' >>> slice.inputs.all_axial = True >>> slice.inputs.image_width = 750 + >>> slice.cmdline + 'slicer functional.nii -L -A 750 functional.png' >>> res = slice.run() #doctest: +SKIP @@ -851,73 +744,46 @@ class Slicer(FSLCommand): input_spec = SlicerInputSpec output_spec = SlicerOutputSpec - def _format_arg(self, name, spec, value): - if name == 'show_orientation': - if value: - return '' - else: - return '-u' - elif name == "label_slices": - if value: - return '-L' - else: - return '' - return super(Slicer, self)._format_arg(name, spec, value) - - def _list_outputs(self): - outputs = self._outputs().get() - out_file = self.inputs.out_file - if not isdefined(out_file): - out_file = self._gen_fname(self.inputs.in_file, ext='.png') - outputs['out_file'] = os.path.abspath(out_file) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()['out_file'] - return None - class PlotTimeSeriesInputSpec(FSLCommandInputSpec): - in_file = traits.Either(File(exists=True), traits.List(File(exists=True)), - mandatory=True, argstr="%s", position=1, - desc=("file or list of files with columns of " - "timecourse information")) - plot_start = traits.Int(argstr="--start=%d", xor=("plot_range",), - desc="first column from in-file to plot") - plot_finish = traits.Int(argstr="--finish=%d", xor=("plot_range",), - desc="final column from in-file to plot") - plot_range = traits.Tuple(traits.Int, traits.Int, argstr="%s", - xor=("plot_start", "plot_finish"), - desc=("first and last columns from the in-file " - "to plot")) - title = traits.Str(argstr="%s", desc="plot title") - legend_file = File(exists=True, argstr="--legend=%s", desc="legend file") - labels = traits.Either(traits.Str, traits.List(traits.Str), - argstr="%s", desc="label or list of labels") - y_min = traits.Float(argstr="--ymin=%.2f", desc="minumum y value", - xor=("y_range",)) - y_max = traits.Float(argstr="--ymax=%.2f", desc="maximum y value", - xor=("y_range",)) - y_range = traits.Tuple(traits.Float, traits.Float, argstr="%s", - xor=("y_min", "y_max"), - desc="min and max y axis values") - x_units = traits.Int(argstr="-u %d", usedefault=True, default_value=1, - desc=("scaling units for x-axis (between 1 and length " - "of in file)")) - plot_size = traits.Tuple(traits.Int, traits.Int, argstr="%s", - desc="plot image height and width") - x_precision = traits.Int(argstr="--precision=%d", - desc="precision of x-axis labels") - sci_notation = traits.Bool(argstr="--sci", - desc="switch on scientific notation") - out_file = File(argstr="-o %s", genfile=True, - desc="image to write", hash_files=False) + in_file = InputMultiPath( + File(exists=True), sep=',', mandatory=True, argstr='%s', position=1, + desc='file or list of files with columns of timecourse information') + plot_start = traits.Int(argstr='--start=%d', xor=['plot_range'], + desc='first column from in-file to plot') + plot_finish = traits.Int(argstr='--finish=%d', xor=['plot_range',], + desc='final column from in-file to plot') + plot_range = traits.Tuple( + traits.Int, traits.Int, argstr='--start=%d --finish=%d', + xor=['plot_start', 'plot_finish'], + desc='first and last columns from the in-file to plot') + title = traits.Str(argstr='-t \'%s\'', desc='plot title') + legend_file = File(exists=True, argstr='--legend=%s', desc='legend file') + labels = traits.Either(traits.Str, traits.List(traits.Str), sep=',', + argstr='-a %s', desc='label or list of labels') + y_min = traits.Float(argstr="--ymin=%.2f", desc='minumum y value', + xor=['y_range']) + y_max = traits.Float(argstr="--ymax=%.2f", desc='maximum y value', + xor=['y_range']) + y_range = traits.Tuple( + traits.Float, traits.Float, argstr='--ymin=%.2f --ymax=%.2f', + xor=['y_min', 'y_max'], desc='min and max y axis values') + x_units = traits.Int(argstr='-u %d', usedefault=True, default_value=1, + desc='scaling units for x-axis (between 1 and length ' + 'of in file)') + plot_size = traits.Tuple( + traits.Int, traits.Int, argstr='-h %d -w %d', + desc='plot image height and width') + x_precision = traits.Int(argstr='--precision=%d', + desc='precision of x-axis labels') + sci_notation = traits.Bool(argstr='--sci', + desc='switch on scientific notation') + out_file = File(template='{in_file}.png', argstr='-o %s', + desc='image to write', hash_files=False) class PlotTimeSeriesOutputSpec(TraitedSpec): - out_file = File(exists=True, desc='image to write') @@ -929,78 +795,66 @@ class PlotTimeSeries(FSLCommand): >>> import nipype.interfaces.fsl as fsl >>> plotter = fsl.PlotTimeSeries() - >>> plotter.inputs.in_file = 'functional.par' + >>> plotter.inputs.in_file = ['functional.par', 'functional.par'] >>> plotter.inputs.title = 'Functional timeseries' >>> plotter.inputs.labels = ['run1', 'run2'] + >>> plotter.cmdline + "fsl_tsplot functional.par,functional.par -a run1,run2 -t 'Functional timeseries' -u 1" >>> plotter.run() #doctest: +SKIP """ - _cmd = "fsl_tsplot" + _cmd = 'fsl_tsplot' input_spec = PlotTimeSeriesInputSpec output_spec = PlotTimeSeriesOutputSpec + +class PlotMotionParamsInputSpec(FSLCommandInputSpec): + in_file = InputMultiPath( + File(exists=True), mandatory=True, argstr='-i %s', sep=',', position=1, + desc='file with motion parameters') + in_source = traits.Enum('spm', 'fsl', mandatory=True, + desc='which program generated the motion ' + 'parameter file - fsl, spm') + plot_type = traits.Enum('rotations', 'translations', 'displacement', + argstr='%s', mandatory=True, + desc='which motion type to plot - rotations, ' + 'translations, displacement') + plot_size = traits.Tuple(traits.Int, traits.Int, argstr='-h %d -w %d', + desc='plot image height and width') + out_file = File(template='{in_file}_{plot_type[:5]}.png', argstr='-o %s', + desc='image to write', hash_files=False) + + def _format_arg(self, name, spec, value): - if name == "in_file": - if isinstance(value, list): - args = ",".join(value) - return "-i %s" % args - else: - return "-i %s" % value - elif name == "labels": - if isinstance(value, list): - args = ",".join(value) - return "-a %s" % args - else: - return "-a %s" % value - elif name == "title": - return "-t \'%s\'" % value - elif name == "plot_range": - return "--start=%d --finish=%d" % value - elif name == "y_range": - return "--ymin=%d --ymax=%d" % value - elif name == "plot_size": - return "-h %d -w %d" % value - return super(PlotTimeSeries, self)._format_arg(name, spec, value) - - def _list_outputs(self): - outputs = self._outputs().get() - out_file = self.inputs.out_file - if not isdefined(out_file): - if isinstance(self.inputs.in_file, list): - infile = self.inputs.in_file[0] - else: - infile = self.inputs.in_file - out_file = self._gen_fname(infile, ext='.png') - outputs['out_file'] = os.path.abspath(out_file) - return outputs - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()['out_file'] - return None + if name == 'plot_type': + source = self.in_source + if self.plot_type == 'displacement': + title = '-t \'MCFLIRT estimated mean displacement (mm)\'' + labels = '-a abs,rel' + return '%s %s' % (title, labels) -class PlotMotionParamsInputSpec(FSLCommandInputSpec): + # Get the right starting and ending position depending on source + # package + sfdict = dict(fsl_rot=(1, 3), fsl_tra=( + 4, 6), spm_rot=(4, 6), spm_tra=(1, 3)) + + # Format the title properly + sfstr = "--start=%d --finish=%d" % sfdict[ + '%s_%s' % (source, value[:3])] + titledict = dict(fsl='MCFLIRT', spm='Realign') + unitdict = dict(rot='radians', tra='mm') - in_file = traits.Either(File(exists=True), traits.List(File(exists=True)), - mandatory=True, argstr="%s", position=1, - desc="file with motion parameters") - in_source = traits.Enum("spm", "fsl", mandatory=True, - desc=("which program generated the motion " - "parameter file - fsl, spm")) - plot_type = traits.Enum("rotations", "translations", "displacement", - argstr="%s", mandatory=True, - desc=("which motion type to plot - rotations, " - "translations, displacement")) - plot_size = traits.Tuple(traits.Int, traits.Int, argstr="%s", - desc="plot image height and width") - out_file = File(argstr="-o %s", genfile=True, - desc="image to write", hash_files=False) + title = "\'%s estimated %s (%s)\'" % ( + titledict[source], value, unitdict[value[:3]]) + return '-t %s %s -a x,y,z' % (title, sfstr) + return super(PlotMotionParamsInputSpec, self)._format_arg(name, spec, value) -class PlotMotionParamsOutputSpec(TraitedSpec): +class PlotMotionParamsOutputSpec(TraitedSpec): out_file = File(exists=True, desc='image to write') @@ -1017,6 +871,8 @@ class PlotMotionParams(FSLCommand): >>> plotter.inputs.in_file = 'functional.par' >>> plotter.inputs.in_source = 'fsl' >>> plotter.inputs.plot_type = 'rotations' + >>> plotter.cmdline + "fsl_tsplot -i functional.par -t 'MCFLIRT estimated rotations (radians)' --start=1 --finish=3 -a x,y,z" >>> res = plotter.run() #doctest: +SKIP @@ -1035,86 +891,51 @@ class PlotMotionParams(FSLCommand): input_spec = PlotMotionParamsInputSpec output_spec = PlotMotionParamsOutputSpec - def _format_arg(self, name, spec, value): - - if name == "plot_type": - source = self.inputs.in_source - - if self.inputs.plot_type == 'displacement': - title = '-t \'MCFLIRT estimated mean displacement (mm)\'' - labels = '-a abs,rel' - return '%s %s' % (title, labels) - - # Get the right starting and ending position depending on source - # package - sfdict = dict(fsl_rot=(1, 3), fsl_tra=( - 4, 6), spm_rot=(4, 6), spm_tra=(1, 3)) - - # Format the title properly - sfstr = "--start=%d --finish=%d" % sfdict[ - "%s_%s" % (source, value[:3])] - titledict = dict(fsl="MCFLIRT", spm="Realign") - unitdict = dict(rot="radians", tra="mm") - - title = "\'%s estimated %s (%s)\'" % ( - titledict[source], value, unitdict[value[:3]]) - - return "-t %s %s -a x,y,z" % (title, sfstr) - elif name == "plot_size": - return "-h %d -w %d" % value - elif name == "in_file": - if isinstance(value, list): - args = ",".join(value) - return "-i %s" % args - else: - return "-i %s" % value - return super(PlotMotionParams, self)._format_arg(name, spec, value) - - def _list_outputs(self): - outputs = self._outputs().get() - out_file = self.inputs.out_file - if not isdefined(out_file): - if isinstance(self.inputs.in_file, list): - infile = self.inputs.in_file[0] - else: - infile = self.inputs.in_file - plttype = dict(rot="rot", tra="trans", dis="disp")[ - self.inputs.plot_type[:3]] - out_file = fname_presuffix( - infile, suffix="_%s.png" % plttype, use_ext=False) - outputs['out_file'] = os.path.abspath(out_file) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()['out_file'] - return None +class ConvertXFMInputSpec(FSLCommandInputSpec): + in_file = File(exists=True, mandatory=True, argstr='%s', position=-1, + desc='input transformation matrix') + in_file2 = File(exists=True, argstr='%s', position=-2, + desc='second input matrix (for use with fix_scale_skew or ' + 'concat_xfm') + operation = traits.Enum( + 'inverse', 'concat', 'fixscaleskew', usedefault=True, mandatory=True, + argstr='-%s', position=-3, desc='operation mode') + + _options = ['invert_xfm', 'concat_xfm', 'fix_scale_skew'] + invert_xfm = traits.Bool(argstr='-inverse', position=-3, xor=_options, + desc='invert input transformation') + concat_xfm = traits.Bool(argstr='-concat', position=-3, xor=_options, + requires=['in_file2'], + desc='write joint transformation of two input ' + 'matrices') + fix_scale_skew = traits.Bool(argstr='-fixscaleskew', position=-3, + xor=_options, requires=['in_file2'], + desc='use secondary matrix to fix scale and ' + 'skew') + out_file = GenFile(template='{in_file}_{operation[:3]}.mat', argstr='-omat %s', position=1, + desc='final transformation matrix', hash_files=False) + + def parse_args(self, skip=None): + if skip is None: + skip = [] + if isdefined(self.invert_xfm) and self.invert_xfm: + self.invert_xfm = Undefined + self.operation = 'inverse' + if isdefined(self.concat_xfm) and self.concat_xfm: + self.concat_xfm = Undefined + self.operation = 'concat' + if isdefined(self.fix_scale_skew) and self.fix_scale_skew: + self.fix_scale_skew = Undefined + self.operation = 'fixscaleskew' -class ConvertXFMInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, mandatory=True, argstr="%s", position=-1, - desc="input transformation matrix") - in_file2 = File(exists=True, argstr="%s", position=-2, - desc=("second input matrix (for use with fix_scale_skew or " - "concat_xfm")) - _options = ["invert_xfm", "concat_xfm", "fix_scale_skew"] - invert_xfm = traits.Bool(argstr="-inverse", position=-3, xor=_options, - desc="invert input transformation") - concat_xfm = traits.Bool(argstr="-concat", position=-3, xor=_options, - requires=["in_file2"], - desc=("write joint transformation of two input " - "matrices")) - fix_scale_skew = traits.Bool(argstr="-fixscaleskew", position=-3, - xor=_options, requires=["in_file2"], - desc=("use secondary matrix to fix scale and " - "skew")) - out_file = File(genfile=True, argstr="-omat %s", position=1, - desc="final transformation matrix", hash_files=False) + skip += ['invert_xfm', 'concat_xfm', 'fix_scale_skew'] + return super(ConvertXFMInputSpec, self).parse_args(skip) class ConvertXFMOutputSpec(TraitedSpec): - out_file = File(exists=True, desc="output transformation matrix") + out_file = File(exists=True, desc='output transformation matrix') class ConvertXFM(FSLCommand): @@ -1125,67 +946,42 @@ class ConvertXFM(FSLCommand): >>> import nipype.interfaces.fsl as fsl >>> invt = fsl.ConvertXFM() - >>> invt.inputs.in_file = "flirt.mat" + >>> invt.inputs.in_file = 'flirt.mat' >>> invt.inputs.invert_xfm = True - >>> invt.inputs.out_file = 'flirt_inv.mat' >>> invt.cmdline 'convert_xfm -omat flirt_inv.mat -inverse flirt.mat' + >>> invt.inputs.in_file2 = 'flirt.mat' + >>> invt.inputs.invert_xfm = False + >>> invt.inputs.operation = 'concat' + >>> invt.cmdline + 'convert_xfm -omat flirt_con.mat -concat flirt.mat flirt.mat' + """ - _cmd = "convert_xfm" + _cmd = 'convert_xfm' input_spec = ConvertXFMInputSpec output_spec = ConvertXFMOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outfile = self.inputs.out_file - if not isdefined(outfile): - _, infile1, _ = split_filename(self.inputs.in_file) - if self.inputs.invert_xfm: - outfile = fname_presuffix(infile1, - suffix="_inv.mat", - newpath=os.getcwd(), - use_ext=False) - else: - if self.inputs.concat_xfm: - _, infile2, _ = split_filename(self.inputs.in_file2) - outfile = fname_presuffix("%s_%s" % (infile1, infile2), - suffix=".mat", - newpath=os.getcwd(), - use_ext=False) - else: - outfile = fname_presuffix(infile1, - suffix="_fix.mat", - newpath=os.getcwd(), - use_ext=False) - outputs["out_file"] = os.path.abspath(outfile) - return outputs - - def _gen_filename(self, name): - if name == "out_file": - return self._list_outputs()["out_file"] - return None - class SwapDimensionsInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, mandatory=True, argstr="%s", position="1", - desc="input image") - _dims = ["x", "-x", "y", "-y", "z", - "-z", "RL", "LR", "AP", "PA", "IS", "SI"] + in_file = File(exists=True, mandatory=True, argstr='%s', position=1, + desc='input image') + _dims = ['x', '-x', 'y', '-y', 'z', + '-z', 'RL', 'LR', 'AP', 'PA', 'IS', 'SI'] new_dims = traits.Tuple(traits.Enum(_dims), traits.Enum(_dims), - traits.Enum(_dims), argstr="%s %s %s", + traits.Enum(_dims), argstr='%s %s %s', mandatory=True, - desc="3-tuple of new dimension order") - out_file = File(genfile=True, argstr="%s", - desc="image to write", hash_files=False) + desc='3-tuple of new dimension order') + out_file = GenFile( + template='{in_file}_newdims{output_type_}', argstr='%s', + desc='image to write', hash_files=False) class SwapDimensionsOutputSpec(TraitedSpec): - - out_file = File(exists=True, desc="image with new dimensions") + out_file = File(exists=True, desc='image with new dimensions') class SwapDimensions(FSLCommand): @@ -1196,33 +992,20 @@ class SwapDimensions(FSLCommand): (-)x, (-)y, or (-z), or nifti-syle dimension codes (RL, LR, AP, PA, IS, SI). """ - _cmd = "fslswapdim" + _cmd = 'fslswapdim' input_spec = SwapDimensionsInputSpec output_spec = SwapDimensionsOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self.inputs.out_file - if not isdefined(self.inputs.out_file): - outputs["out_file"] = self._gen_fname(self.inputs.in_file, - suffix='_newdims') - outputs["out_file"] = os.path.abspath(outputs["out_file"]) - return outputs - - def _gen_filename(self, name): - if name == "out_file": - return self._list_outputs()["out_file"] - return None - class PowerSpectrumInputSpec(FSLCommandInputSpec): # We use position args here as list indices - so a negative number # will put something on the end in_file = File(exists=True, - desc="input 4D file to estimate the power spectrum", + desc='input 4D file to estimate the power spectrum', argstr='%s', position=0, mandatory=True) - out_file = File(desc='name of output 4D file for power spectrum', - argstr='%s', position=1, genfile=True, hash_files=False) + out_file = GenFile( + template='{in_file}_ps{output_type_}', argstr='%s', position=1, hash_files=False, + desc='name of output 4D file for power spectrum') class PowerSpectrumOutputSpec(TraitedSpec): @@ -1248,32 +1031,15 @@ class PowerSpectrum(FSLCommand): input_spec = PowerSpectrumInputSpec output_spec = PowerSpectrumOutputSpec - def _gen_outfilename(self): - out_file = self.inputs.out_file - if not isdefined(out_file) and isdefined(self.inputs.in_file): - out_file = self._gen_fname(self.inputs.in_file, - suffix='_ps') - return out_file - - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = os.path.abspath(self._gen_outfilename()) - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._gen_outfilename() - return None - class SigLossInputSpec(FSLCommandInputSpec): in_file = File(mandatory=True, exists=True, argstr='-i %s', desc='b0 fieldmap file') - out_file = File(argstr='-s %s', - desc='output signal loss estimate file', - genfile=True) + out_file = GenFile( + template='{in_file}_sigloss{output_type_}', argstr='-s %s', hash_files=False, + desc='output signal loss estimate file') mask_file = File(exists=True, argstr='-m %s', @@ -1297,7 +1063,7 @@ class SigLoss(FSLCommand): -------- >>> sigloss = SigLoss() - >>> sigloss.inputs.in_file = "phase.nii" + >>> sigloss.inputs.in_file = 'phase.nii' >>> sigloss.inputs.echo_time = 0.03 >>> res = sigloss.run() # doctest: +SKIP @@ -1307,24 +1073,11 @@ class SigLoss(FSLCommand): output_spec = SigLossOuputSpec _cmd = 'sigloss' - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']) and \ - isdefined(self.inputs.in_file): - outputs['out_file'] = self._gen_fname(self.inputs.in_file, - suffix='_sigloss') - return outputs - - def _gen_filename(self, name): - if name == 'out_file': - return self._list_outputs()['out_file'] - return None - class Reorient2StdInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, mandatory=True, argstr="%s") - out_file = File(genfile=True, hash_files=False, argstr="%s") + in_file = File(exists=True, mandatory=True, argstr='%s') + out_file = GenFile(template='{in_file}_reoriented{output_type_}', + hash_files=False, argstr='%s') class Reorient2StdOutputSpec(TraitedSpec): @@ -1340,7 +1093,7 @@ class Reorient2Std(FSLCommand): -------- >>> reorient = Reorient2Std() - >>> reorient.inputs.in_file = "functional.nii" + >>> reorient.inputs.in_file = 'functional.nii' >>> res = reorient.run() # doctest: +SKIP @@ -1349,74 +1102,57 @@ class Reorient2Std(FSLCommand): input_spec = Reorient2StdInputSpec output_spec = Reorient2StdOutputSpec - def _gen_filename(self, name): - if name == 'out_file': - return self._gen_fname(self.inputs.in_file, - suffix="_reoriented") - return None - - def _list_outputs(self): - outputs = self.output_spec().get() - if not isdefined(self.inputs.out_file): - outputs['out_file'] = self._gen_filename('out_file') - else: - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs - class InvWarpInputSpec(FSLCommandInputSpec): warp = File(exists=True, argstr='--warp=%s', mandatory=True, - desc=('Name of file containing warp-coefficients/fields. This ' + desc='Name of file containing warp-coefficients/fields. This ' 'would typically be the output from the --cout switch of ' 'fnirt (but can also use fields, like the output from ' - '--fout).')) + '--fout).') reference = File(exists=True, argstr='--ref=%s', mandatory=True, - desc=('Name of a file in target space. Note that the ' + desc='Name of a file in target space. Note that the ' 'target space is now different from the target ' 'space that was used to create the --warp file. It ' 'would typically be the file that was specified ' - 'with the --in argument when running fnirt.')) - inverse_warp = File(argstr='--out=%s', name_source=['warp'], - hash_files=False, name_template='%s_inverse', - desc=('Name of output file, containing warps that are ' - 'the "reverse" of those in --warp. This will be ' - 'a field-file (rather than a file of spline ' - 'coefficients), and it will have any affine ' - 'component included as part of the ' - 'displacements.')) + 'with the --in argument when running fnirt.') absolute = traits.Bool(argstr='--abs', xor=['relative'], - desc=('If set it indicates that the warps in --warp ' + desc='If set it indicates that the warps in --warp ' 'should be interpreted as absolute, provided ' 'that it is not created by fnirt (which ' 'always uses relative warps). If set it also ' 'indicates that the output --out should be ' - 'absolute.')) + 'absolute.') relative = traits.Bool(argstr='--rel', xor=['absolute'], - desc=('If set it indicates that the warps in --warp ' + desc='If set it indicates that the warps in --warp ' 'should be interpreted as relative. I.e. the ' 'values in --warp are displacements from the ' 'coordinates in the --ref space. If set it ' 'also indicates that the output --out should ' - 'be relative.')) + 'be relative.') niter = traits.Int(argstr='--niter=%d', - desc=('Determines how many iterations of the ' - 'gradient-descent search that should be run.')) + desc='Determines how many iterations of the ' + 'gradient-descent search that should be run.') regularise = traits.Float(argstr='--regularise=%f', desc='Regularization strength (deafult=1.0).') noconstraint = traits.Bool(argstr='--noconstraint', desc='Do not apply Jacobian constraint') jacobian_min = traits.Float(argstr='--jmin=%f', - desc=('Minimum acceptable Jacobian value for ' - 'constraint (default 0.01)')) + desc='Minimum acceptable Jacobian value for ' + 'constraint (default 0.01)') jacobian_max = traits.Float(argstr='--jmax=%f', - desc=('Maximum acceptable Jacobian value for ' - 'constraint (default 100.0)')) + desc='Maximum acceptable Jacobian value for ' + 'constraint (default 100.0)') + inverse_warp = GenFile( + template='{warp}_inverse{output_type_}', argstr='--out=%s', hash_files=False, + desc='Name of output file, containing warps that are the \'reverse\' of those in' + ' --warp. This will be a field-file (rather than a file of spline coefficients),' + ' and it will have any affine component included as part of the displacements.') class InvWarpOutputSpec(TraitedSpec): inverse_warp = File(exists=True, - desc=('Name of output file, containing warps that are ' - 'the "reverse" of those in --warp.')) + desc='Name of output file, containing warps that are ' + 'the \'reverse\' of those in --warp.') class InvWarp(FSLCommand): @@ -1429,9 +1165,9 @@ class InvWarp(FSLCommand): >>> from nipype.interfaces.fsl import InvWarp >>> invwarp = InvWarp() - >>> invwarp.inputs.warp = "struct2mni.nii" - >>> invwarp.inputs.reference = "anatomical.nii" - >>> invwarp.inputs.output_type = "NIFTI_GZ" + >>> invwarp.inputs.warp = 'struct2mni.nii' + >>> invwarp.inputs.reference = 'anatomical.nii' + >>> invwarp.inputs.output_type = 'NIFTI_GZ' >>> invwarp.cmdline 'invwarp --out=struct2mni_inverse.nii.gz --ref=anatomical.nii --warp=struct2mni.nii' >>> res = invwarp.run() # doctest: +SKIP @@ -1446,12 +1182,12 @@ class InvWarp(FSLCommand): class ComplexInputSpec(FSLCommandInputSpec): - complex_in_file = File(exists=True, argstr="%s", position=2) - complex_in_file2 = File(exists=True, argstr="%s", position=3) + complex_in_file = File(exists=True, argstr='%s', position=2) + complex_in_file2 = File(exists=True, argstr='%s', position=3) - real_in_file = File(exists=True, argstr="%s", position=2) - imaginary_in_file = File(exists=True, argstr="%s", position=3) - magnitude_in_file = File(exists=True, argstr="%s", position=2) + real_in_file = File(exists=True, argstr='%s', position=2) + imaginary_in_file = File(exists=True, argstr='%s', position=3) + magnitude_in_file = File(exists=True, argstr='%s', position=2) phase_in_file = File(exists=True, argstr='%s', position=3) _ofs = ['complex_out_file', @@ -1461,16 +1197,6 @@ class ComplexInputSpec(FSLCommandInputSpec): 'complex_cartesian', 'complex_polar', 'complex_split', 'complex_merge', ] - complex_out_file = File(genfile=True, argstr="%s", position=-3, - xor=_ofs + _conversion[:2]) - magnitude_out_file = File(genfile=True, argstr="%s", position=-4, - xor=_ofs[:1] + _ofs[3:] + _conversion[1:]) - phase_out_file = File(genfile=True, argstr="%s", position=-3, - xor=_ofs[:1] + _ofs[3:] + _conversion[1:]) - real_out_file = File(genfile=True, argstr="%s", position=-4, - xor=_ofs[:3] + _conversion[:1] + _conversion[2:]) - imaginary_out_file = File(genfile=True, argstr="%s", position=-3, - xor=_ofs[:3] + _conversion[:1] + _conversion[2:]) start_vol = traits.Int(position=-2, argstr='%d') end_vol = traits.Int(position=-1, argstr='%d') @@ -1496,6 +1222,29 @@ class ComplexInputSpec(FSLCommandInputSpec): position=1,) # requires=['complex_in_file','complex_in_file2','complex_out_file']) + # Auto-generate output file names + complex_out_file = GenFile( + template='generated_cplx{output_type_}', argstr='%s', position=-3) + magnitude_out_file = GenFile( + template='{complex_in_file}_mag{output_type_}', argstr='%s', position=-4) + phase_out_file = GenFile( + template='{complex_in_file}_phase{output_type_}', argstr='%s', position=-3) + real_out_file = GenFile( + template='{complex_in_file}_real{output_type_}', argstr='%s', position=-4) + imaginary_out_file = GenFile( + template='{complex_in_file}_imag{output_type_}', argstr='%s', position=-3) + + + def parse_args(self, skip=None): + if skip is None: + skip = [] + if self.real_cartesian: + skip += self._ofs[:3] + elif self.real_polar: + skip += self._ofs[:1] + self._ofs[3:] + else: + skip += self._ofs[1:] + return super(ComplexInputSpec, self).parse_args(skip) class ComplexOuputSpec(TraitedSpec): magnitude_out_file = File() @@ -1512,7 +1261,7 @@ class Complex(FSLCommand): -------- >>> cplx = Complex() - >>> cplx.inputs.complex_in_file = "complex.nii" + >>> cplx.inputs.complex_in_file = 'complex.nii' >>> cplx.real_polar = True >>> res = cplx.run() # doctest: +SKIP @@ -1522,79 +1271,28 @@ class Complex(FSLCommand): input_spec = ComplexInputSpec output_spec = ComplexOuputSpec - def _parse_inputs(self, skip=None): - if skip is None: - skip = [] - if self.inputs.real_cartesian: - skip += self.inputs._ofs[:3] - elif self.inputs.real_polar: - skip += self.inputs._ofs[:1] + self.inputs._ofs[3:] - else: - skip += self.inputs._ofs[1:] - return super(Complex, self)._parse_inputs(skip) - - def _gen_filename(self, name): - if name == 'complex_out_file': - if self.inputs.complex_cartesian: - in_file = self.inputs.real_in_file - elif self.inputs.complex_polar: - in_file = self.inputs.magnitude_in_file - elif self.inputs.complex_split or self.inputs.complex_merge: - in_file = self.inputs.complex_in_file - else: - return None - return self._gen_fname(in_file, suffix="_cplx") - elif name == 'magnitude_out_file': - return self._gen_fname(self.inputs.complex_in_file, suffix="_mag") - elif name == 'phase_out_file': - return self._gen_fname(self.inputs.complex_in_file, suffix="_phase") - elif name == 'real_out_file': - return self._gen_fname(self.inputs.complex_in_file, suffix="_real") - elif name == 'imaginary_out_file': - return self._gen_fname(self.inputs.complex_in_file, suffix="_imag") - return None - - def _get_output(self, name): - output = getattr(self.inputs, name) - if not isdefined(output): - output = self._gen_filename(name) - return os.path.abspath(output) - - def _list_outputs(self): - outputs = self.output_spec().get() - if self.inputs.complex_cartesian or self.inputs.complex_polar or \ - self.inputs.complex_split or self.inputs.complex_merge: - outputs['complex_out_file'] = self._get_output('complex_out_file') - elif self.inputs.real_cartesian: - outputs['real_out_file'] = self._get_output('real_out_file') - outputs['imaginary_out_file'] = self._get_output('imaginary_out_file') - elif self.inputs.real_polar: - outputs['magnitude_out_file'] = self._get_output('magnitude_out_file') - outputs['phase_out_file'] = self._get_output('phase_out_file') - return outputs - class WarpUtilsInputSpec(FSLCommandInputSpec): in_file = File(exists=True, argstr='--in=%s', mandatory=True, - desc=('Name of file containing warp-coefficients/fields. This ' + desc='Name of file containing warp-coefficients/fields. This ' 'would typically be the output from the --cout switch of ' 'fnirt (but can also use fields, like the output from ' - '--fout).')) + '--fout).') reference = File(exists=True, argstr='--ref=%s', mandatory=True, - desc=('Name of a file in target space. Note that the ' + desc='Name of a file in target space. Note that the ' 'target space is now different from the target ' 'space that was used to create the --warp file. It ' 'would typically be the file that was specified ' - 'with the --in argument when running fnirt.')) + 'with the --in argument when running fnirt.') out_format = traits.Enum('spline', 'field', argstr='--outformat=%s', - desc=('Specifies the output format. If set to field (default) ' + desc='Specifies the output format. If set to field (default) ' 'the output will be a (4D) field-file. If set to spline ' - 'the format will be a (4D) file of spline coefficients.')) + 'the format will be a (4D) file of spline coefficients.') warp_resolution = traits.Tuple(traits.Float, traits.Float, traits.Float, argstr='--warpres=%0.4f,%0.4f,%0.4f', - desc=('Specifies the resolution/knot-spacing of the splines pertaining ' + desc='Specifies the resolution/knot-spacing of the splines pertaining ' 'to the coefficients in the --out file. This parameter is only ' 'relevant if --outformat is set to spline. It should be noted ' 'that if the --in file has a higher resolution, the resulting ' @@ -1602,39 +1300,50 @@ class WarpUtilsInputSpec(FSLCommandInputSpec): ' sense) file in the space of fields with the --warpres' ' resolution. It should also be noted that the resolution ' 'will always be an integer multiple of the voxel ' - 'size.')) + 'size.') knot_space = traits.Tuple(traits.Int, traits.Int, traits.Int, argstr='--knotspace=%d,%d,%d', - desc=('Alternative (to --warpres) specification of the resolution of ' - 'the output spline-field.')) + desc='Alternative (to --warpres) specification of the resolution of ' + 'the output spline-field.') - out_file = File(argstr='--out=%s', position=-1, name_source=['in_file'], output_name='out_file', - desc=('Name of output file. The format of the output depends on what other ' - 'parameters are set. The default format is a (4D) field-file. If the ' - '--outformat is set to spline the format will be a (4D) file of spline ' - 'coefficients.')) + out_file = GenFile( + template='{in_file}_{out_format}{output_type_}', argstr='--out=%s', position=-1, + desc='Name of output file. The format of the output depends on what other ' + 'parameters are set. The default format is a (4D) field-file. If the ' + '--outformat is set to spline the format will be a (4D) file of spline ' + 'coefficients.') write_jacobian = traits.Bool(False, mandatory=True, usedefault=True, desc='Switch on --jac flag with automatically generated filename') - out_jacobian = File(argstr='--jac=%s', - desc=('Specifies that a (3D) file of Jacobian determinants corresponding ' - 'to --in should be produced and written to filename.')) + out_jacobian = GenFile(template='{in_file}_jac{output_type_}', argstr='--jac=%s', + desc='Specifies that a (3D) file of Jacobian determinants corresponding ' + 'to --in should be produced and written to filename.') with_affine = traits.Bool(False, argstr='--withaff', - desc=('Specifies that the affine transform (i.e. that which was ' + desc='Specifies that the affine transform (i.e. that which was ' 'specified for the --aff parameter in fnirt) should be ' 'included as displacements in the --out file. That can be ' 'useful for interfacing with software that cannot decode ' 'FSL/fnirt coefficient-files (where the affine transform is ' - 'stored separately from the displacements).')) + 'stored separately from the displacements).') + def parse_args(self, skip=None): + if skip is None: + skip = [] + if not self.write_jacobian: + skip += ['out_jacobian'] + return super(WarpUtilsInputSpec, self).parse_args(skip) class WarpUtilsOutputSpec(TraitedSpec): - out_file = File(desc=('Name of output file, containing the warp as field or coefficients.')) - out_jacobian = File(desc=('Name of output file, containing the map of the determinant of ' - 'the Jacobian')) + out_file = File(desc='Name of output file, containing the warp as field or coefficients.') + out_jacobian = File(desc='Name of output file, containing the map of the determinant of ' + 'the Jacobian') + + def post_run(self): + if not self.inputs.write_jacobian: + self.outputs.out_jacobian = Undefined class WarpUtils(FSLCommand): @@ -1647,13 +1356,13 @@ class WarpUtils(FSLCommand): >>> from nipype.interfaces.fsl import WarpUtils >>> warputils = WarpUtils() - >>> warputils.inputs.in_file = "warpfield.nii" - >>> warputils.inputs.reference = "T1.nii" + >>> warputils.inputs.in_file = 'warpfield.nii' + >>> warputils.inputs.reference = 'T1.nii' >>> warputils.inputs.out_format = 'spline' >>> warputils.inputs.warp_resolution = (10,10,10) - >>> warputils.inputs.output_type = "NIFTI_GZ" + >>> warputils.inputs.output_type = 'NIFTI_GZ' >>> warputils.cmdline # doctest: +ELLIPSIS - 'fnirtfileutils --in=warpfield.nii --outformat=spline --ref=T1.nii --warpres=10.0000,10.0000,10.0000 --out=warpfield_coeffs.nii.gz' + 'fnirtfileutils --in=warpfield.nii --outformat=spline --ref=T1.nii --warpres=10.0000,10.0000,10.0000 --out=warpfield_spline.nii.gz' >>> res = invwarp.run() # doctest: +SKIP @@ -1664,109 +1373,86 @@ class WarpUtils(FSLCommand): _cmd = 'fnirtfileutils' - def _parse_inputs(self, skip=None): - if skip is None: - skip = [] - - suffix = 'field' - if isdefined(self.inputs.out_format) and self.inputs.out_format == 'spline': - suffix = 'coeffs' - - trait_spec = self.inputs.trait('out_file') - trait_spec.name_template = "%s_" + suffix - - if self.inputs.write_jacobian: - if not isdefined(self.inputs.out_jacobian): - jac_spec = self.inputs.trait('out_jacobian') - jac_spec.name_source = ['in_file'] - jac_spec.name_template = '%s_jac' - jac_spec.output_name = 'out_jacobian' - else: - skip += ['out_jacobian'] - - skip += ['write_jacobian'] - return super(WarpUtils, self)._parse_inputs(skip=skip) - class ConvertWarpInputSpec(FSLCommandInputSpec): reference = File(exists=True, argstr='--ref=%s', mandatory=True, position=1, - desc=('Name of a file in target space of the full transform.')) + desc='Name of a file in target space of the full transform.') - out_file = File(argstr='--out=%s', position=-1, name_source=['reference'], - name_template='%s_concatwarp', output_name='out_file', - desc=('Name of output file, containing warps that are the combination of all ' - 'those given as arguments. The format of this will be a field-file (rather ' - 'than spline coefficients) with any affine components included.')) + out_file = GenFile( + template='{reference}_concatwarp{output_type_}', argstr='--out=%s', position=-1, + desc='Name of output file, containing warps that are the combination of all ' + 'those given as arguments. The format of this will be a field-file (rather ' + 'than spline coefficients) with any affine components included.') premat = File(exists=True, argstr='--premat=%s', desc='filename for pre-transform (affine matrix)') warp1 = File(exists=True, argstr='--warp1=%s', - desc=('Name of file containing initial warp-fields/coefficients (follows premat). This could e.g. be a ' + desc='Name of file containing initial warp-fields/coefficients (follows premat). This could e.g. be a ' 'fnirt-transform from a subjects structural scan to an average of a group ' - 'of subjects.')) + 'of subjects.') - midmat = File(exists=True, argstr="--midmat=%s", - desc="Name of file containing mid-warp-affine transform") + midmat = File(exists=True, argstr='--midmat=%s', + desc='Name of file containing mid-warp-affine transform') warp2 = File(exists=True, argstr='--warp2=%s', - desc=('Name of file containing secondary warp-fields/coefficients (after warp1/midmat but before postmat). This could e.g. be a ' + desc='Name of file containing secondary warp-fields/coefficients (after warp1/midmat but before postmat). This could e.g. be a ' 'fnirt-transform from the average of a group of subjects to some standard ' - 'space (e.g. MNI152).')) + 'space (e.g. MNI152).') postmat = File(exists=True, argstr='--postmat=%s', - desc=('Name of file containing an affine transform (applied last). It could e.g. be an affine ' + desc='Name of file containing an affine transform (applied last). It could e.g. be an affine ' 'transform that maps the MNI152-space into a better approximation to the ' - 'Talairach-space (if indeed there is one).')) + 'Talairach-space (if indeed there is one).') shift_in_file = File(exists=True, argstr='--shiftmap=%s', - desc=('Name of file containing a "shiftmap", a non-linear transform with ' + desc='Name of file containing a \'shiftmap\', a non-linear transform with ' 'displacements only in one direction (applied first, before premat). This would typically be a ' 'fieldmap that has been pre-processed using fugue that maps a ' 'subjects functional (EPI) data onto an undistorted space (i.e. a space ' - 'that corresponds to his/her true anatomy).')) + 'that corresponds to his/her true anatomy).') shift_direction = traits.Enum('y-', 'y', 'x', 'x-', 'z', 'z-', - argstr="--shiftdir=%s", requires=['shift_in_file'], - desc=('Indicates the direction that the distortions from ' + argstr='--shiftdir=%s', requires=['shift_in_file'], + desc='Indicates the direction that the distortions from ' '--shiftmap goes. It depends on the direction and ' - 'polarity of the phase-encoding in the EPI sequence.')) + 'polarity of the phase-encoding in the EPI sequence.') cons_jacobian = traits.Bool(False, argstr='--constrainj', - desc=('Constrain the Jacobian of the warpfield to lie within specified ' - 'min/max limits.')) + desc='Constrain the Jacobian of the warpfield to lie within specified ' + 'min/max limits.') jacobian_min = traits.Float(argstr='--jmin=%f', - desc=('Minimum acceptable Jacobian value for ' - 'constraint (default 0.01)')) + desc='Minimum acceptable Jacobian value for ' + 'constraint (default 0.01)') jacobian_max = traits.Float(argstr='--jmax=%f', - desc=('Maximum acceptable Jacobian value for ' - 'constraint (default 100.0)')) + desc='Maximum acceptable Jacobian value for ' + 'constraint (default 100.0)') abswarp = traits.Bool(argstr='--abs', xor=['relwarp'], - desc=('If set it indicates that the warps in --warp1 and --warp2 should be ' + desc='If set it indicates that the warps in --warp1 and --warp2 should be ' 'interpreted as absolute. I.e. the values in --warp1/2 are the ' 'coordinates in the next space, rather than displacements. This flag ' 'is ignored if --warp1/2 was created by fnirt, which always creates ' - 'relative displacements.')) + 'relative displacements.') relwarp = traits.Bool(argstr='--rel', xor=['abswarp'], - desc=('If set it indicates that the warps in --warp1/2 should be interpreted ' + desc='If set it indicates that the warps in --warp1/2 should be interpreted ' 'as relative. I.e. the values in --warp1/2 are displacements from the ' - 'coordinates in the next space.')) + 'coordinates in the next space.') out_abswarp = traits.Bool(argstr='--absout', xor=['out_relwarp'], - desc=('If set it indicates that the warps in --out should be absolute, i.e. ' - 'the values in --out are displacements from the coordinates in --ref.')) + desc='If set it indicates that the warps in --out should be absolute, i.e. ' + 'the values in --out are displacements from the coordinates in --ref.') out_relwarp = traits.Bool(argstr='--relout', xor=['out_abswarp'], - desc=('If set it indicates that the warps in --out should be relative, i.e. ' - 'the values in --out are displacements from the coordinates in --ref.')) + desc='If set it indicates that the warps in --out should be relative, i.e. ' + 'the values in --out are displacements from the coordinates in --ref.') class ConvertWarpOutputSpec(TraitedSpec): out_file = File(exists=True, - desc=('Name of output file, containing the warp as field or coefficients.')) + desc='Name of output file, containing the warp as field or coefficients.') class ConvertWarp(FSLCommand): @@ -1779,10 +1465,10 @@ class ConvertWarp(FSLCommand): >>> from nipype.interfaces.fsl import ConvertWarp >>> warputils = ConvertWarp() - >>> warputils.inputs.warp1 = "warpfield.nii" - >>> warputils.inputs.reference = "T1.nii" + >>> warputils.inputs.warp1 = 'warpfield.nii' + >>> warputils.inputs.reference = 'T1.nii' >>> warputils.inputs.relwarp = True - >>> warputils.inputs.output_type = "NIFTI_GZ" + >>> warputils.inputs.output_type = 'NIFTI_GZ' >>> warputils.cmdline # doctest: +ELLIPSIS 'convertwarp --ref=T1.nii --rel --warp1=warpfield.nii --out=T1_concatwarp.nii.gz' >>> res = invwarp.run() # doctest: +SKIP @@ -1797,16 +1483,16 @@ class ConvertWarp(FSLCommand): class WarpPointsBaseInputSpec(CommandLineInputSpec): in_coords = File(exists=True, position=-1, argstr='%s', mandatory=True, - desc=('filename of file containing coordinates')) + desc='filename of file containing coordinates') xfm_file = File(exists=True, argstr='-xfm %s', xor=['warp_file'], - desc=('filename of affine transform (e.g. source2dest.mat)')) + desc='filename of affine transform (e.g. source2dest.mat)') warp_file = File(exists=True, argstr='-warp %s', xor=['xfm_file'], - desc=('filename of warpfield (e.g. ' - 'intermediate2dest_warp.nii.gz)')) + desc='filename of warpfield (e.g. ' + 'intermediate2dest_warp.nii.gz)') coord_vox = traits.Bool(True, argstr='-vox', xor=['coord_mm'], - desc=('all coordinates in voxels - default')) + desc='all coordinates in voxels - default') coord_mm = traits.Bool(False, argstr='-mm', xor=['coord_vox'], - desc=('all coordinates in mm')) + desc='all coordinates in mm') out_file = File(name_source='in_coords', name_template='%s_warped', output_name='out_file', desc='output file name') @@ -1814,14 +1500,14 @@ class WarpPointsBaseInputSpec(CommandLineInputSpec): class WarpPointsInputSpec(WarpPointsBaseInputSpec): src_file = File(exists=True, argstr='-src %s', mandatory=True, - desc=('filename of source image')) + desc='filename of source image') dest_file = File(exists=True, argstr='-dest %s', mandatory=True, - desc=('filename of destination image')) + desc='filename of destination image') class WarpPointsOutputSpec(TraitedSpec): out_file = File(exists=True, - desc=('Name of output file, containing the warp as field or coefficients.')) + desc='Name of output file, containing the warp as field or coefficients.') class WarpPoints(CommandLine): @@ -1866,13 +1552,13 @@ def _format_arg(self, name, trait_spec, value): else: return super(WarpPoints, self)._format_arg(name, trait_spec, value) - def _parse_inputs(self, skip=None): + def parse_args(self, skip=None): import os.path as op - fname, ext = op.splitext(self.inputs.in_coords) + fname, ext = op.splitext(self.in_coords) setattr(self, '_in_file', fname) setattr(self, '_outformat', ext[1:]) - first_args = super(WarpPoints, self)._parse_inputs(skip=['in_coords', 'out_file']) + first_args = super(WarpPoints, self).parse_args(skip=['in_coords', 'out_file']) second_args = fname + '.txt' @@ -1995,12 +1681,12 @@ def _run_interface(self, runtime): class WarpPointsToStdInputSpec(WarpPointsBaseInputSpec): img_file = File(exists=True, argstr='-img %s', mandatory=True, - desc=('filename of input image')) + desc='filename of input image') std_file = File(exists=True, argstr='-std %s', mandatory=True, - desc=('filename of destination image')) + desc='filename of destination image') premat_file = File(exists=True, argstr='-premat %s', - desc=('filename of pre-warp affine transform ' - '(e.g. example_func2highres.mat)')) + desc='filename of pre-warp affine transform ' + '(e.g. example_func2highres.mat)') class WarpPointsToStd(WarpPoints): @@ -2035,22 +1721,23 @@ class WarpPointsToStd(WarpPoints): class MotionOutliersInputSpec(FSLCommandInputSpec): - in_file = File(exists=True, mandatory=True, desc="unfiltered 4D image", argstr="-i %s") - out_file = File(argstr="-o %s", name_source='in_file', name_template='%s_outliers.txt', - keep_extension=True, desc='output outlier file name', hash_files=False) - mask = File(exists=True, argstr="-m %s", desc="mask image for calculating metric") - metric = traits.Enum('refrms', ['refrms', 'dvars', 'refmse', 'fd', 'fdrms'], argstr="--%s", desc="metrics: refrms - RMS intensity difference to reference volume as metric [default metric],\ + in_file = File(exists=True, mandatory=True, desc='unfiltered 4D image', argstr='-i %s') + mask = File(exists=True, argstr='-m %s', desc='mask image for calculating metric') + metric = traits.Enum('refrms', ['refrms', 'dvars', 'refmse', 'fd', 'fdrms'], argstr='--%s', desc="metrics: refrms - RMS intensity difference to reference volume as metric [default metric],\ refmse - Mean Square Error version of refrms (used in original version of fsl_motion_outliers) \ dvars - DVARS \ fd - frame displacement \ fdrms - FD with RMS matrix calculation") - threshold = traits.Float(argstr="--thresh=%g", desc="specify absolute threshold value (otherwise use box-plot cutoff = P75 + 1.5*IQR)") - no_motion_correction = traits.Bool(argstr="--nomoco", desc="do not run motion correction (assumed already done)") - dummy = traits.Int(argstr="--dummy=%d", desc='number of dummy scans to delete (before running anything and creating EVs)') - out_metric_values = File(argstr="-s %s", name_source='in_file', name_template='%s_metrics.txt', - keep_extension=True, desc='output metric values (DVARS etc.) file name', hash_files=False) - out_metric_plot = File(argstr="-p %s", name_source='in_file', name_template='%s_metrics.png', - keep_extension=True, desc='output metric values plot (DVARS etc.) file name', hash_files=False) + threshold = traits.Float(argstr='--thresh=%g', desc="specify absolute threshold value (otherwise use box-plot cutoff = P75 + 1.5*IQR)") + no_motion_correction = traits.Bool(argstr='--nomoco', desc='do not run motion correction (assumed already done)') + dummy = traits.Int(argstr='--dummy=%d', desc='number of dummy scans to delete (before running anything and creating EVs)') + + out_file = GenFile(template='{in_file}_outliers.txt', argstr='-o %s', hash_files=False, + desc='output outlier file name') + out_metric_values = GenFile(template='{in_file}_metrics.txt', argstr='-s %s', hash_files=False, + desc='output metric values (DVARS etc.) file name') + out_metric_plot = GenFile(template='{in_file}_metrics.png', argstr='-p %s', hash_files=False, + desc='output metric values plot (DVARS etc.) file name') class MotionOutliersOutputSpec(TraitedSpec): @@ -2066,7 +1753,7 @@ class MotionOutliers(FSLCommand): -------- >>> from nipype.interfaces.fsl import MotionOutliers >>> mo = MotionOutliers() - >>> mo.inputs.in_file = "epi.nii" + >>> mo.inputs.in_file = 'epi.nii' >>> mo.cmdline # doctest: +ELLIPSIS 'fsl_motion_outliers -i epi.nii -o epi_outliers.txt -p epi_metrics.png -s epi_metrics.txt' >>> res = mo.run() # doctest: +SKIP diff --git a/nipype/interfaces/io.py b/nipype/interfaces/io.py index 6f0ad3bc32..7747580fc4 100644 --- a/nipype/interfaces/io.py +++ b/nipype/interfaces/io.py @@ -34,11 +34,9 @@ from warnings import warn import sqlite3 - -from .base import (TraitedSpec, traits, File, Directory, - BaseInterface, InputMultiPath, isdefined, - OutputMultiPath, DynamicTraitedSpec, - Undefined, BaseInterfaceInputSpec) +from .base import (traits, Undefined, File, Directory, isdefined, InputMultiPath, + OutputMultiPath, TraitedSpec, DynamicTraitedSpec, + BaseInterfaceInputSpec, BaseInterface) from .. import config from ..external.six import string_types from ..utils.filemanip import (copyfile, list_to_filename, @@ -89,7 +87,7 @@ def copytree(src, dst, use_hardlink=False): if os.path.isdir(srcname): copytree(srcname, dstname, use_hardlink) else: - copyfile(srcname, dstname, True, hashmethod='content', + copyfile(srcname, dstname, True, hash_method='content', use_hardlink=use_hardlink) except (IOError, os.error) as why: errors.append((srcname, dstname, str(why))) @@ -124,7 +122,7 @@ class IOBase(BaseInterface): def _run_interface(self, runtime): return runtime - def _list_outputs(self): + def _post_run(self): raise NotImplementedError def _outputs(self): @@ -136,14 +134,14 @@ def _add_output_traits(self, base): # Class to track percentage of S3 file upload class ProgressPercentage(object): - ''' + """ Callable class instsance (via __call__ method) that displays upload percentage of a file to S3 - ''' + """ def __init__(self, filename): - ''' - ''' + """ + """ # Import packages import threading @@ -155,8 +153,8 @@ def __init__(self, filename): self._lock = threading.Lock() def __call__(self, bytes_amount): - ''' - ''' + """ + """ # Import packages import sys @@ -178,8 +176,8 @@ def __call__(self, bytes_amount): # DataSink inputs class DataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): - ''' - ''' + """ + """ # Init inputspec data attributes base_directory = Directory( @@ -372,7 +370,7 @@ def _substitute(self, pathstr): # Check for s3 in base directory def _check_s3_base_dir(self): - ''' + """ Method to see if the datasink's base directory specifies an S3 bucket path; if it does, it parses the path for the bucket name in the form 's3://bucket_name/...' and returns it @@ -388,7 +386,7 @@ def _check_s3_base_dir(self): bucket_name : string name of the S3 bucket to connect to; if the base directory is not a valid S3 path, defaults to '' - ''' + """ # Init variables s3_str = 's3://' @@ -419,7 +417,7 @@ def _check_s3_base_dir(self): # Function to return AWS secure environment variables def _return_aws_keys(self): - ''' + """ Method to return AWS access key id and secret access key using credentials found in a local file. @@ -434,7 +432,7 @@ def _return_aws_keys(self): string of the AWS access key ID aws_secret_access_key : string string of the AWS secret access key - ''' + """ # Import packages import os @@ -474,7 +472,7 @@ def _return_aws_keys(self): # Fetch bucket object def _fetch_bucket(self, bucket_name): - ''' + """ Method to return a bucket object which can be used to interact with an AWS S3 bucket using credentials found in a local file. @@ -490,7 +488,7 @@ def _fetch_bucket(self, bucket_name): bucket : boto3.resources.factory.s3.Bucket boto3 s3 Bucket object which is used to interact with files in an S3 bucket on AWS - ''' + """ # Import packages import logging @@ -567,9 +565,9 @@ def _fetch_bucket(self, bucket_name): # Send up to S3 method def _upload_to_s3(self, bucket, src, dst): - ''' + """ Method to upload outputs to S3 bucket instead of on local disk - ''' + """ # Import packages import hashlib @@ -636,13 +634,13 @@ def _upload_to_s3(self, bucket, src, dst): Callback=ProgressPercentage(src_f)) # List outputs, main run routine - def _list_outputs(self): + def _post_run(self): """Execute this module. """ # Init variables iflogger = logging.getLogger('interface') - outputs = self.output_spec().get() + out_files = [] # Use hardlink use_hardlink = str2bool(config.get('execution', 'try_hard_link_datasink')) @@ -752,7 +750,7 @@ def _list_outputs(self): # If src is a file, copy it to dst if os.path.isfile(src): iflogger.debug('copyfile: %s %s' % (src, dst)) - copyfile(src, dst, copy=True, hashmethod='content', + copyfile(src, dst, copy=True, hash_method='content', use_hardlink=use_hardlink) out_files.append(dst) # If src is a directory, copy entire contents to dst dir @@ -765,9 +763,7 @@ def _list_outputs(self): out_files.append(dst) # Return outputs dictionary - outputs['out_file'] = out_files - - return outputs + self.outputs.out_file = out_files class S3DataGrabberInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -861,7 +857,7 @@ def _add_output_traits(self, base): """ return add_traits(base, self.inputs.template_args.keys()) - def _list_outputs(self): + def _post_run(self): # infields are mandatory, however I could not figure out how to set 'mandatory' flag dynamically # hence manual check if self._infields: @@ -880,7 +876,7 @@ def _list_outputs(self): # keys are outfields, args are template args for the outfield for key, args in self.inputs.template_args.items(): - outputs[key] = [] + setattr(self.outputs, key, []) template = self.inputs.template if hasattr(self.inputs, 'field_template') and \ isdefined(self.inputs.field_template) and \ @@ -903,7 +899,7 @@ def _list_outputs(self): else: if self.inputs.sort_filelist: filelist = human_order_sorted(filelist) - outputs[key] = list_to_filename(filelist) + setattr(self.outputs, key, list_to_filename(filelist)) for argnum, arglist in enumerate(args): maxlen = 1 for arg in arglist: @@ -940,17 +936,17 @@ def _list_outputs(self): raise IOError(msg) else: warn(msg) - outputs[key].append(None) + getattr(self.outputs, key).append(None) else: if self.inputs.sort_filelist: outfiles = human_order_sorted(outfiles) - outputs[key].append(list_to_filename(outfiles)) - if any([val is None for val in outputs[key]]): - outputs[key] = [] - if len(outputs[key]) == 0: - outputs[key] = None - elif len(outputs[key]) == 1: - outputs[key] = outputs[key][0] + getattr(self.outputs, key).append(list_to_filename(outfiles)) + if any([val is None for val in getattr(self.outputs, key)]): + setattr(self.outputs, key, []) + if len(getattr(self.outputs, key)) == 0: + setattr(self.outputs, key, None) + elif len(getattr(self.outputs, key)) == 1: + setattr(self.outputs, key, getattr(self.outputs, key)[0]) # Outputs are currently stored as locations on S3. # We must convert to the local location specified # and download the files. @@ -960,13 +956,15 @@ def _list_outputs(self): #tuple, numpy array) and we iterate through each of its #values. If it doesn't, it's string-like (string, #unicode), and we convert that value directly. + + cur_value = getattr(self.outputs, key) if hasattr(val,'__iter__'): for i,path in enumerate(val): - outputs[key][i] = self.s3tolocal(path, bkt) + cur_value[i] = self.s3tolocal(path, bkt) else: - outputs[key] = self.s3tolocal(val, bkt) + cur_value[i] = self.s3tolocal(val, bkt) + setattr(self.outputs, key, cur_value) - return outputs # Takes an s3 address and downloads the file to a local # directory, returning the local path. @@ -1106,7 +1104,7 @@ def _add_output_traits(self, base): """ return add_traits(base, list(self.inputs.template_args.keys())) - def _list_outputs(self): + def _post_run(self): # infields are mandatory, however I could not figure out how to set 'mandatory' flag dynamically # hence manual check if self._infields: @@ -1119,7 +1117,7 @@ def _list_outputs(self): outputs = {} for key, args in list(self.inputs.template_args.items()): - outputs[key] = [] + setattr(self.outputs, key, []) template = self.inputs.template if hasattr(self.inputs, 'field_template') and \ isdefined(self.inputs.field_template) and \ @@ -1142,7 +1140,7 @@ def _list_outputs(self): else: if self.inputs.sort_filelist: filelist = human_order_sorted(filelist) - outputs[key] = list_to_filename(filelist) + setattr(self.outputs, key, list_to_filename(filelist)) for argnum, arglist in enumerate(args): maxlen = 1 for arg in arglist: @@ -1176,18 +1174,17 @@ def _list_outputs(self): raise IOError(msg) else: warn(msg) - outputs[key].append(None) + getattr(self.outputs, key).append(None) else: if self.inputs.sort_filelist: outfiles = human_order_sorted(outfiles) - outputs[key].append(list_to_filename(outfiles)) - if any([val is None for val in outputs[key]]): - outputs[key] = [] - if len(outputs[key]) == 0: - outputs[key] = None - elif len(outputs[key]) == 1: - outputs[key] = outputs[key][0] - return outputs + getattr(self.outputs, key).append(list_to_filename(outfiles)) + if any([val is None for val in getattr(self.outputs, key)]): + setattr(self.outputs, key, []) + if len(getattr(self.outputs, key)) == 0: + setattr(self.outputs, key, None) + elif len(getattr(self.outputs, key)) == 1: + setattr(self.outputs, key, getattr(self.outputs, key)[0]) class SelectFilesInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -1281,7 +1278,7 @@ def _add_output_traits(self, base): """Add the dynamic output fields""" return add_traits(base, list(self._templates.keys())) - def _list_outputs(self): + def _post_run(self): """Find the files and expose them as interface outputs.""" outputs = {} info = dict([(k, v) for k, v in list(self.inputs.__dict__.items()) @@ -1329,9 +1326,7 @@ def _list_outputs(self): if field not in force_lists: filelist = list_to_filename(filelist) - outputs[field] = filelist - - return outputs + setattr(self.outputs, field, filelist) class DataFinderInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -1473,13 +1468,9 @@ def _run_interface(self, runtime): if not self.result: raise RuntimeError("Regular expression did not match any files!") + self.outputs.update(self.result) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs.update(self.result) - return outputs - class FSSourceInputSpec(BaseInterfaceInputSpec): subjects_dir = Directory(mandatory=True, @@ -1611,7 +1602,7 @@ def _get_files(self, path, key, dirval, altkey=None): keydir, ''.join((globprefix, key, globsuffix))) return [os.path.abspath(f) for f in glob.glob(globpattern)] - def _list_outputs(self): + def _post_run(self): subjects_dir = self.inputs.subjects_dir subject_path = os.path.join(subjects_dir, self.inputs.subject_id) output_traits = self._outputs() @@ -1621,8 +1612,7 @@ def _list_outputs(self): output_traits.traits()[k].loc, output_traits.traits()[k].altkey) if val: - outputs[k] = list_to_filename(val) - return outputs + setattr(self.outputs, k, list_to_filename(val)) class XNATSourceInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -1734,7 +1724,7 @@ def _add_output_traits(self, base): """ return add_traits(base, list(self.inputs.query_template_args.keys())) - def _list_outputs(self): + def _post_run(self): # infields are mandatory, however I could not figure out # how to set 'mandatory' flag dynamically, hence manual check @@ -1761,7 +1751,7 @@ def _list_outputs(self): outputs = {} for key, args in list(self.inputs.query_template_args.items()): - outputs[key] = [] + setattr(self.outputs, key, []) template = self.inputs.query_template if hasattr(self.inputs, 'field_template') and \ isdefined(self.inputs.field_template) and \ @@ -1773,11 +1763,11 @@ def _list_outputs(self): raise IOError('Template %s returned no files' % template ) - outputs[key] = list_to_filename( + setattr(self.outputs, key, list_to_filename( [str(file_object.get()) for file_object in file_objects if file_object.exists() - ]) + ])) for argnum, arglist in enumerate(args): maxlen = 1 for arg in arglist: @@ -1831,12 +1821,11 @@ def _list_outputs(self): ] ) - outputs[key].insert(i, outfiles) - if len(outputs[key]) == 0: - outputs[key] = None - elif len(outputs[key]) == 1: - outputs[key] = outputs[key][0] - return outputs + getattr(self.outputs, key).insert(i, outfiles) + if len(getattr(self.outputs, key)) == 0: + setattr(self.outputs, key, None) + elif len(getattr(self.outputs, key)) == 1: + setattr(self.outputs, key, getattr(self.outputs, key)[0]) class XNATSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -1895,7 +1884,7 @@ class XNATSink(IOBase): """ input_spec = XNATSinkInputSpec - def _list_outputs(self): + def _post_run(self): """Execute this module. """ @@ -2099,7 +2088,7 @@ def __init__(self, input_names, **inputs): self._input_names = filename_to_list(input_names) add_traits(self.inputs, [name for name in self._input_names]) - def _list_outputs(self): + def _post_run(self): """Execute this module. """ conn = sqlite3.connect(self.inputs.database_file, @@ -2152,7 +2141,7 @@ def __init__(self, input_names, **inputs): self._input_names = filename_to_list(input_names) add_traits(self.inputs, [name for name in self._input_names]) - def _list_outputs(self): + def _post_run(self): """Execute this module. """ import MySQLdb @@ -2293,7 +2282,7 @@ def __init__(self, infields=None, outfields=None, **kwargs): ): self.inputs.template += '$' - def _list_outputs(self): + def _post_run(self): try: paramiko except NameError: @@ -2316,7 +2305,7 @@ def _list_outputs(self): outputs = {} for key, args in list(self.inputs.template_args.items()): - outputs[key] = [] + setattr(self.outputs, key, []) template = self.inputs.template if hasattr(self.inputs, 'field_template') and \ isdefined(self.inputs.field_template) and \ @@ -2344,7 +2333,7 @@ def _list_outputs(self): else: if self.inputs.sort_filelist: filelist = human_order_sorted(filelist) - outputs[key] = list_to_filename(filelist) + setattr(self.outputs, key, list_to_filename(filelist)) if self.inputs.download_files: for f in filelist: sftp.get(f, f) @@ -2393,28 +2382,26 @@ def _list_outputs(self): raise IOError(msg) else: warn(msg) - outputs[key].append(None) + getattr(self.outputs, key).append(None) else: if self.inputs.sort_filelist: outfiles = human_order_sorted(outfiles) - outputs[key].append(list_to_filename(outfiles)) + getattr(self.outputs, key).append(list_to_filename(outfiles)) if self.inputs.download_files: for f in outfiles: try: sftp.get(os.path.join(filledtemplate_dir, f), f) except IOError: iflogger.info('remote file %s not found' % f) - if any([val is None for val in outputs[key]]): - outputs[key] = [] - if len(outputs[key]) == 0: - outputs[key] = None - elif len(outputs[key]) == 1: - outputs[key] = outputs[key][0] + if any([val is None for val in getattr(self.outputs, key)]): + setattr(self.outputs, key, []) + if len(getattr(self.outputs, key)) == 0: + setattr(self.outputs, key, None) + elif len(getattr(self.outputs, key)) == 1: + setattr(self.outputs, key, getattr(self.outputs, key)[0]) for k, v in list(outputs.items()): - outputs[k] = os.path.join(os.getcwd(), v) - - return outputs + setattr(self.outputs, k, os.path.join(os.getcwd(), v)) def _get_ssh_client(self): config = paramiko.SSHConfig() @@ -2468,7 +2455,7 @@ class JSONFileGrabber(IOBase): output_spec = DynamicTraitedSpec _always_run = True - def _list_outputs(self): + def _post_run(self): import simplejson outputs = {} @@ -2480,15 +2467,13 @@ def _list_outputs(self): raise RuntimeError('JSON input has no dictionary structure') for key, value in data.items(): - outputs[key] = value + setattr(self.outputs, key, value) if isdefined(self.inputs.defaults): defaults = self.inputs.defaults for key, value in defaults.items(): if key not in list(outputs.keys()): - outputs[key] = value - - return outputs + setattr(self.outputs, key, value) class JSONFileSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -2570,7 +2555,7 @@ def _process_name(self, name, val): return name, val - def _list_outputs(self): + def _post_run(self): import simplejson import os.path as op @@ -2590,6 +2575,5 @@ def _list_outputs(self): with open(out_file, 'w') as f: simplejson.dump(out_dict, f) - outputs = self.output_spec().get() - outputs['out_file'] = out_file - return outputs + + self.outputs.out_file = out_file diff --git a/nipype/interfaces/meshfix.py b/nipype/interfaces/meshfix.py index f5a891465e..21f85aaab3 100644 --- a/nipype/interfaces/meshfix.py +++ b/nipype/interfaces/meshfix.py @@ -111,21 +111,20 @@ class MeshFix(CommandLine): input_spec = MeshFixInputSpec output_spec = MeshFixOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if isdefined(self.inputs.out_filename): path, name, ext = split_filename(self.inputs.out_filename) ext = ext.replace('.', '') out_types = ['stl', 'msh', 'wrl', 'vrml', 'fs', 'off'] # Make sure that the output filename uses one of the possible file types if any(ext == out_type.lower() for out_type in out_types): - outputs['mesh_file'] = op.abspath(self.inputs.out_filename) + self.outputs.mesh_file = op.abspath(self.inputs.out_filename) else: - outputs['mesh_file'] = op.abspath(name + '.' + self.inputs.output_type) + self.outputs.mesh_file = op.abspath(name + '.' + self.inputs.output_type) else: - outputs['mesh_file'] = op.abspath(self._gen_outfilename()) - return outputs - + self.outputs.mesh_file = op.abspath(self._gen_outfilename()) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() diff --git a/nipype/interfaces/minc/minc.py b/nipype/interfaces/minc/minc.py index c54e855125..4490ef52f3 100644 --- a/nipype/interfaces/minc/minc.py +++ b/nipype/interfaces/minc/minc.py @@ -1697,24 +1697,23 @@ def _gen_output_base(self): # '_bluroutput' return output_base - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + output_file_base = self._gen_output_base() - outputs['output_file'] = output_file_base + '_blur.mnc' + self.outputs.output_file = output_file_base + '_blur.mnc' if isdefined(self.inputs.gradient): - outputs['gradient_dxyz'] = output_file_base + '_dxyz.mnc' + self.outputs.gradient_dxyz = output_file_base + '_dxyz.mnc' if isdefined(self.inputs.partial): - outputs['partial_dx'] = output_file_base + '_dx.mnc' - outputs['partial_dy'] = output_file_base + '_dy.mnc' - outputs['partial_dz'] = output_file_base + '_dz.mnc' - outputs['partial_dxyz'] = output_file_base + '_dxyz.mnc' - - return outputs + self.outputs.partial_dx = output_file_base + '_dx.mnc' + self.outputs.partial_dy = output_file_base + '_dy.mnc' + self.outputs.partial_dz = output_file_base + '_dz.mnc' + self.outputs.partial_dxyz = output_file_base + '_dxyz.mnc' + @property def cmdline(self): output_file_base = self.inputs.output_file_base @@ -1968,7 +1967,7 @@ class MathInputSpec(CommandLineInputSpec): 'segment', 'nsegment', 'isnan', - 'isnan'] # FIXME enforce this in _parse_inputs and check for other members + 'isnan'] # FIXME enforce this in parse_args and check for other members invert = traits.Either( traits.Float(), @@ -2094,7 +2093,7 @@ def _format_arg(self, name, spec, value): return super(Math, self)._format_arg(name, spec, value) - def _parse_inputs(self): + def parse_args(self): """A number of the command line options expect precisely one or two files. """ @@ -2146,7 +2145,7 @@ def _parse_inputs(self): 'Due to the %s option we expected at least one file but input_files is of length %d' % (n, nr_input_files,)) - return super(Math, self)._parse_inputs() + return super(Math, self).parse_args() class ResampleInputSpec(CommandLineInputSpec): @@ -2972,12 +2971,11 @@ class Gennlxfm(CommandLine): output_spec = GennlxfmOutputSpec _cmd = 'gennlxfm' - def _list_outputs(self): + def _post_run(self): outputs = super(Gennlxfm, self)._list_outputs() - outputs['output_grid'] = re.sub( - '.(nlxfm|xfm)$', '_grid_0.mnc', outputs['output_file']) - return outputs - + self.outputs.output_grid = re.sub( + '.(nlxfm|xfm)$', '_grid_0.mnc', self.outputs.output_file) + class XfmConcatInputSpec(CommandLineInputSpec): input_files = InputMultiPath( @@ -3036,19 +3034,18 @@ class XfmConcat(CommandLine): output_spec = XfmConcatOutputSpec _cmd = 'xfmconcat' - def _list_outputs(self): + def _post_run(self): outputs = super(XfmConcat, self)._list_outputs() - if os.path.exists(outputs['output_file']): - if 'grid' in open(outputs['output_file'], 'r').read(): - outputs['output_grids'] = glob.glob( + if os.path.exists(self.outputs.output_file): + if 'grid' in open(self.outputs.output_file, 'r').read(): + self.outputs.output_grids = glob.glob( re.sub( '.(nlxfm|xfm)$', '_grid_*.mnc', - outputs['output_file'])) - - return outputs + self.outputs.output_file)) + class BestLinRegInputSpec(CommandLineInputSpec): source = File( @@ -3232,18 +3229,17 @@ def _gen_filename(self, name): else: raise NotImplemented - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['output_xfm'] = os.path.abspath( + def _post_run(self): + + self.outputs.output_xfm = os.path.abspath( self._gen_filename('output_xfm')) - assert os.path.exists(outputs['output_xfm']) - if 'grid' in open(outputs['output_xfm'], 'r').read(): - outputs['output_grid'] = re.sub( - '.(nlxfm|xfm)$', '_grid_0.mnc', outputs['output_xfm']) - - return outputs + assert os.path.exists(self.outputs.output_xfm) + if 'grid' in open(self.outputs.output_xfm, 'r').read(): + self.outputs.output_grid = re.sub( + '.(nlxfm|xfm)$', '_grid_0.mnc', self.outputs.output_xfm) + class XfmAvgInputSpec(CommandLineInputSpec): input_files = InputMultiPath( @@ -3337,17 +3333,16 @@ def _gen_filename(self, name): def _gen_outfilename(self): return self._gen_filename('output_file') - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['output_file'] = os.path.abspath(self._gen_outfilename()) - - assert os.path.exists(outputs['output_file']) - if 'grid' in open(outputs['output_file'], 'r').read(): - outputs['output_grid'] = re.sub( - '.(nlxfm|xfm)$', '_grid_0.mnc', outputs['output_file']) + def _post_run(self): + + self.outputs.output_file = os.path.abspath(self._gen_outfilename()) - return outputs + assert os.path.exists(self.outputs.output_file) + if 'grid' in open(self.outputs.output_file, 'r').read(): + self.outputs.output_grid = re.sub( + '.(nlxfm|xfm)$', '_grid_0.mnc', self.outputs.output_file) + class XfmInvertInputSpec(CommandLineInputSpec): input_file = traits.File( @@ -3411,17 +3406,16 @@ def _gen_filename(self, name): def _gen_outfilename(self): return self._gen_filename('output_file') - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['output_file'] = os.path.abspath(self._gen_outfilename()) - - assert os.path.exists(outputs['output_file']) - if 'grid' in open(outputs['output_file'], 'r').read(): - outputs['output_grid'] = re.sub( - '.(nlxfm|xfm)$', '_grid_0.mnc', outputs['output_file']) + def _post_run(self): + + self.outputs.output_file = os.path.abspath(self._gen_outfilename()) - return outputs + assert os.path.exists(self.outputs.output_file) + if 'grid' in open(self.outputs.output_file, 'r').read(): + self.outputs.output_grid = re.sub( + '.(nlxfm|xfm)$', '_grid_0.mnc', self.outputs.output_file) + class BigAverageInputSpec(CommandLineInputSpec): input_files = InputMultiPath( @@ -3673,13 +3667,13 @@ class VolSymm(CommandLine): output_spec = VolSymmOutputSpec _cmd = 'volsymm' - def _list_outputs(self): + def _post_run(self): outputs = super(VolSymm, self)._list_outputs() # Have to manually check for the grid files. - if os.path.exists(outputs['trans_file']): - if 'grid' in open(outputs['trans_file'], 'r').read(): - outputs['output_grid'] = re.sub( - '.(nlxfm|xfm)$', '_grid_0.mnc', outputs['trans_file']) + if os.path.exists(self.outputs.trans_file): + if 'grid' in open(self.outputs.trans_file, 'r').read(): + self.outputs.output_grid = re.sub( + '.(nlxfm|xfm)$', '_grid_0.mnc', self.outputs.trans_file) - return outputs + \ No newline at end of file diff --git a/nipype/interfaces/minc/tests/test_auto_BBox.py b/nipype/interfaces/minc/tests/test_auto_BBox.py index 8ea5f0b34b..d2a05db604 100644 --- a/nipype/interfaces/minc/tests/test_auto_BBox.py +++ b/nipype/interfaces/minc/tests/test_auto_BBox.py @@ -26,8 +26,8 @@ def test_BBox_inputs(): xor=('one_line', 'two_lines'), ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), output_file=dict(hash_files=False, keep_extension=False, diff --git a/nipype/interfaces/minc/tests/test_auto_Dump.py b/nipype/interfaces/minc/tests/test_auto_Dump.py index 0a41c74c90..9eda182ce9 100644 --- a/nipype/interfaces/minc/tests/test_auto_Dump.py +++ b/nipype/interfaces/minc/tests/test_auto_Dump.py @@ -34,8 +34,8 @@ def test_Dump_inputs(): netcdf_name=dict(argstr='-n %s', ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), output_file=dict(hash_files=False, keep_extension=False, diff --git a/nipype/interfaces/minc/tests/test_auto_Extract.py b/nipype/interfaces/minc/tests/test_auto_Extract.py index 04ecb3b7d3..a87c127a2e 100644 --- a/nipype/interfaces/minc/tests/test_auto_Extract.py +++ b/nipype/interfaces/minc/tests/test_auto_Extract.py @@ -68,8 +68,8 @@ def test_Extract_inputs(): xor=('normalize', 'nonormalize'), ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), output_file=dict(hash_files=False, keep_extension=False, diff --git a/nipype/interfaces/minc/tests/test_auto_ToRaw.py b/nipype/interfaces/minc/tests/test_auto_ToRaw.py index a647ed48d0..059962d84a 100644 --- a/nipype/interfaces/minc/tests/test_auto_ToRaw.py +++ b/nipype/interfaces/minc/tests/test_auto_ToRaw.py @@ -23,8 +23,8 @@ def test_ToRaw_inputs(): xor=('normalize', 'nonormalize'), ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), output_file=dict(hash_files=False, keep_extension=False, diff --git a/nipype/interfaces/mne/base.py b/nipype/interfaces/mne/base.py index 96d238f8ef..40d0ea50ec 100644 --- a/nipype/interfaces/mne/base.py +++ b/nipype/interfaces/mne/base.py @@ -73,8 +73,8 @@ def _get_files(self, path, key, dirval, altkey=None): globpattern = op.join(keydir, ''.join((globprefix, key, globsuffix))) return glob.glob(globpattern) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + subjects_dir = self.inputs.subjects_dir subject_path = op.join(subjects_dir, self.inputs.subject_id) output_traits = self._outputs() @@ -94,8 +94,8 @@ def _list_outputs(self): out_files = op.abspath(value_list) else: raise TypeError - outputs[k] = out_files + setattr(self.outputs, k, out_files) if not k.rfind('surface') == -1: mesh_paths.append(out_files) - outputs['mesh_files'] = mesh_paths - return outputs + self.outputs.mesh_files = mesh_paths + diff --git a/nipype/interfaces/mrtrix/convert.py b/nipype/interfaces/mrtrix/convert.py index 89cf1c2299..ce7aecf8e2 100644 --- a/nipype/interfaces/mrtrix/convert.py +++ b/nipype/interfaces/mrtrix/convert.py @@ -239,11 +239,9 @@ def _run_interface(self, runtime): iflogger.info(trk_header) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = op.abspath(self.inputs.out_filename) - return outputs - + def _post_run(self): + self.outputs.out_file = op.abspath(self.inputs.out_filename) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() diff --git a/nipype/interfaces/mrtrix/preprocess.py b/nipype/interfaces/mrtrix/preprocess.py index 67242e9705..a12632bd9d 100644 --- a/nipype/interfaces/mrtrix/preprocess.py +++ b/nipype/interfaces/mrtrix/preprocess.py @@ -67,15 +67,14 @@ class MRConvert(CommandLine): input_spec = MRConvertInputSpec output_spec = MRConvertOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['converted'] = self.inputs.out_filename - if not isdefined(outputs['converted']): - outputs['converted'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.converted = self.inputs.out_filename + if not isdefined(self.outputs.converted): + self.outputs.converted = op.abspath(self._gen_outfilename()) else: - outputs['converted'] = op.abspath(outputs['converted']) - return outputs - + self.outputs.converted = op.abspath(self.outputs.converted) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -180,15 +179,14 @@ class Tensor2Vector(CommandLine): input_spec = Tensor2VectorInputSpec output_spec = Tensor2VectorOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['vector'] = self.inputs.out_filename - if not isdefined(outputs['vector']): - outputs['vector'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.vector = self.inputs.out_filename + if not isdefined(self.outputs.vector): + self.outputs.vector = op.abspath(self._gen_outfilename()) else: - outputs['vector'] = op.abspath(outputs['vector']) - return outputs - + self.outputs.vector = op.abspath(self.outputs.vector) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -229,15 +227,14 @@ class Tensor2FractionalAnisotropy(CommandLine): input_spec = Tensor2FractionalAnisotropyInputSpec output_spec = Tensor2FractionalAnisotropyOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['FA'] = self.inputs.out_filename - if not isdefined(outputs['FA']): - outputs['FA'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.FA = self.inputs.out_filename + if not isdefined(self.outputs.FA): + self.outputs.FA = op.abspath(self._gen_outfilename()) else: - outputs['FA'] = op.abspath(outputs['FA']) - return outputs - + self.outputs.FA = op.abspath(self.outputs.FA) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -278,15 +275,14 @@ class Tensor2ApparentDiffusion(CommandLine): input_spec = Tensor2ApparentDiffusionInputSpec output_spec = Tensor2ApparentDiffusionOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['ADC'] = self.inputs.out_filename - if not isdefined(outputs['ADC']): - outputs['ADC'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.ADC = self.inputs.out_filename + if not isdefined(self.outputs.ADC): + self.outputs.ADC = op.abspath(self._gen_outfilename()) else: - outputs['ADC'] = op.abspath(outputs['ADC']) - return outputs - + self.outputs.ADC = op.abspath(self.outputs.ADC) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -328,15 +324,14 @@ class MRMultiply(CommandLine): input_spec = MRMultiplyInputSpec output_spec = MRMultiplyOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_filename - if not isdefined(outputs['out_file']): - outputs['out_file'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.out_file = self.inputs.out_filename + if not isdefined(self.outputs.out_file): + self.outputs.out_file = op.abspath(self._gen_outfilename()) else: - outputs['out_file'] = op.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = op.abspath(self.outputs.out_file) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -377,7 +372,7 @@ class MRTrixViewer(CommandLine): input_spec = MRTrixViewerInputSpec output_spec = MRTrixViewerOutputSpec - def _list_outputs(self): + def _post_run(self): return @@ -407,7 +402,7 @@ class MRTrixInfo(CommandLine): input_spec = MRTrixInfoInputSpec output_spec = MRTrixInfoOutputSpec - def _list_outputs(self): + def _post_run(self): return @@ -442,11 +437,10 @@ class GenerateWhiteMatterMask(CommandLine): input_spec = GenerateWhiteMatterMaskInputSpec output_spec = GenerateWhiteMatterMaskOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['WMprobabilitymap'] = op.abspath(self._gen_outfilename()) - return outputs - + def _post_run(self): + + self.outputs.WMprobabilitymap = op.abspath(self._gen_outfilename()) + def _gen_filename(self, name): if name is 'out_WMProb_filename': return self._gen_outfilename() @@ -488,15 +482,14 @@ class Erode(CommandLine): input_spec = ErodeInputSpec output_spec = ErodeOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_filename - if not isdefined(outputs['out_file']): - outputs['out_file'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.out_file = self.inputs.out_filename + if not isdefined(self.outputs.out_file): + self.outputs.out_file = op.abspath(self._gen_outfilename()) else: - outputs['out_file'] = op.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = op.abspath(self.outputs.out_file) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -546,15 +539,14 @@ class Threshold(CommandLine): input_spec = ThresholdInputSpec output_spec = ThresholdOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_filename - if not isdefined(outputs['out_file']): - outputs['out_file'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.out_file = self.inputs.out_filename + if not isdefined(self.outputs.out_file): + self.outputs.out_file = op.abspath(self._gen_outfilename()) else: - outputs['out_file'] = op.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = op.abspath(self.outputs.out_file) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -595,15 +587,14 @@ class MedianFilter3D(CommandLine): input_spec = MedianFilter3DInputSpec output_spec = MedianFilter3DOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_filename - if not isdefined(outputs['out_file']): - outputs['out_file'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.out_file = self.inputs.out_filename + if not isdefined(self.outputs.out_file): + self.outputs.out_file = op.abspath(self._gen_outfilename()) else: - outputs['out_file'] = op.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = op.abspath(self.outputs.out_file) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -653,15 +644,14 @@ class MRTransform(CommandLine): input_spec = MRTransformInputSpec output_spec = MRTransformOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_filename - if not isdefined(outputs['out_file']): - outputs['out_file'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.out_file = self.inputs.out_filename + if not isdefined(self.outputs.out_file): + self.outputs.out_file = op.abspath(self._gen_outfilename()) else: - outputs['out_file'] = op.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = op.abspath(self.outputs.out_file) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() diff --git a/nipype/interfaces/mrtrix/tensors.py b/nipype/interfaces/mrtrix/tensors.py index 3ef2ecc901..98b7a556a6 100644 --- a/nipype/interfaces/mrtrix/tensors.py +++ b/nipype/interfaces/mrtrix/tensors.py @@ -75,15 +75,14 @@ class DWI2SphericalHarmonicsImage(CommandLine): input_spec = DWI2SphericalHarmonicsImageInputSpec output_spec = DWI2SphericalHarmonicsImageOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['spherical_harmonics_image'] = self.inputs.out_filename - if not isdefined(outputs['spherical_harmonics_image']): - outputs['spherical_harmonics_image'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.spherical_harmonics_image = self.inputs.out_filename + if not isdefined(self.outputs.spherical_harmonics_image): + self.outputs.spherical_harmonics_image = op.abspath(self._gen_outfilename()) else: - outputs['spherical_harmonics_image'] = op.abspath(outputs['spherical_harmonics_image']) - return outputs - + self.outputs.spherical_harmonics_image = op.abspath(self.outputs.spherical_harmonics_image) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -158,15 +157,14 @@ class ConstrainedSphericalDeconvolution(CommandLine): input_spec = ConstrainedSphericalDeconvolutionInputSpec output_spec = ConstrainedSphericalDeconvolutionOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['spherical_harmonics_image'] = self.inputs.out_filename - if not isdefined(outputs['spherical_harmonics_image']): - outputs['spherical_harmonics_image'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.spherical_harmonics_image = self.inputs.out_filename + if not isdefined(self.outputs.spherical_harmonics_image): + self.outputs.spherical_harmonics_image = op.abspath(self._gen_outfilename()) else: - outputs['spherical_harmonics_image'] = op.abspath(outputs['spherical_harmonics_image']) - return outputs - + self.outputs.spherical_harmonics_image = op.abspath(self.outputs.spherical_harmonics_image) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -212,15 +210,14 @@ class EstimateResponseForSH(CommandLine): input_spec = EstimateResponseForSHInputSpec output_spec = EstimateResponseForSHOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['response'] = self.inputs.out_filename - if not isdefined(outputs['response']): - outputs['response'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.response = self.inputs.out_filename + if not isdefined(self.outputs.response): + self.outputs.response = op.abspath(self._gen_outfilename()) else: - outputs['response'] = op.abspath(outputs['response']) - return outputs - + self.outputs.response = op.abspath(self.outputs.response) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() @@ -296,11 +293,10 @@ def _run_interface(self, runtime): encoding = concat_files(self.inputs.bvec_file, self.inputs.bval_file, self.inputs.invert_x, self.inputs.invert_y, self.inputs.invert_z) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['encoding_file'] = op.abspath(self._gen_filename('out_encoding_file')) - return outputs - + def _post_run(self): + + self.outputs.encoding_file = op.abspath(self._gen_filename('out_encoding_file')) + def _gen_filename(self, name): if name is 'out_encoding_file': return self._gen_outfilename() diff --git a/nipype/interfaces/mrtrix/tracking.py b/nipype/interfaces/mrtrix/tracking.py index c4d49118a8..db3141320e 100644 --- a/nipype/interfaces/mrtrix/tracking.py +++ b/nipype/interfaces/mrtrix/tracking.py @@ -107,15 +107,14 @@ class Tracks2Prob(CommandLine): input_spec = Tracks2ProbInputSpec output_spec = Tracks2ProbOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['tract_image'] = self.inputs.out_filename - if not isdefined(outputs['tract_image']): - outputs['tract_image'] = op.abspath(self._gen_outfilename()) + def _post_run(self): + + self.outputs.tract_image = self.inputs.out_filename + if not isdefined(self.outputs.tract_image): + self.outputs.tract_image = op.abspath(self._gen_outfilename()) else: - outputs['tract_image'] = os.path.abspath(outputs['tract_image']) - return outputs - + self.outputs.tract_image = os.path.abspath(self.outputs.tract_image) + def _gen_filename(self, name): if name is 'out_filename': return self._gen_outfilename() diff --git a/nipype/interfaces/mrtrix3/base.py b/nipype/interfaces/mrtrix3/base.py index 40a8e93a88..615e97b0f7 100644 --- a/nipype/interfaces/mrtrix3/base.py +++ b/nipype/interfaces/mrtrix3/base.py @@ -64,7 +64,7 @@ def _format_arg(self, name, trait_spec, value): return super(MRTrix3Base, self)._format_arg(name, trait_spec, value) - def _parse_inputs(self, skip=None): + def parse_args(self, skip=None): if skip is None: skip = [] @@ -83,4 +83,4 @@ def _parse_inputs(self, skip=None): except AttributeError: pass - return super(MRTrix3Base, self)._parse_inputs(skip=skip) + return super(MRTrix3Base, self).parse_args(skip=skip) diff --git a/nipype/interfaces/mrtrix3/connectivity.py b/nipype/interfaces/mrtrix3/connectivity.py index 64e73f8069..3b0142a0fb 100644 --- a/nipype/interfaces/mrtrix3/connectivity.py +++ b/nipype/interfaces/mrtrix3/connectivity.py @@ -104,11 +104,10 @@ class BuildConnectome(MRTrix3Base): input_spec = BuildConnectomeInputSpec output_spec = BuildConnectomeOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs - + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + class LabelConfigInputSpec(CommandLineInputSpec): in_file = File(exists=True, argstr='%s', mandatory=True, position=-3, @@ -163,7 +162,7 @@ class LabelConfig(MRTrix3Base): input_spec = LabelConfigInputSpec output_spec = LabelConfigOutputSpec - def _parse_inputs(self, skip=None): + def parse_args(self, skip=None): if skip is None: skip = [] @@ -179,9 +178,9 @@ def _parse_inputs(self, skip=None): path, 'src/dwi/tractography/connectomics/' 'example_configs/fs_default.txt') - return super(LabelConfig, self)._parse_inputs(skip=skip) + return super(LabelConfig, self).parse_args(skip=skip) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/mrtrix3/preprocess.py b/nipype/interfaces/mrtrix3/preprocess.py index e52c84071d..252efecc98 100644 --- a/nipype/interfaces/mrtrix3/preprocess.py +++ b/nipype/interfaces/mrtrix3/preprocess.py @@ -105,14 +105,13 @@ class ResponseSD(MRTrix3Base): input_spec = ResponseSDInputSpec output_spec = ResponseSDOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) if isdefined(self.inputs.out_sf): - outputs['out_sf'] = op.abspath(self.inputs.out_sf) - return outputs - + self.outputs.out_sf = op.abspath(self.inputs.out_sf) + class ACTPrepareFSLInputSpec(CommandLineInputSpec): in_file = File(exists=True, argstr='%s', mandatory=True, position=-2, @@ -148,11 +147,10 @@ class ACTPrepareFSL(CommandLine): input_spec = ACTPrepareFSLInputSpec output_spec = ACTPrepareFSLOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs - + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + class ReplaceFSwithFIRSTInputSpec(CommandLineInputSpec): in_file = File(exists=True, argstr='%s', mandatory=True, position=-4, @@ -195,7 +193,7 @@ class ReplaceFSwithFIRST(CommandLine): input_spec = ReplaceFSwithFIRSTInputSpec output_spec = ReplaceFSwithFIRSTOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/mrtrix3/reconst.py b/nipype/interfaces/mrtrix3/reconst.py index ce023fbdef..ba83ee8b55 100644 --- a/nipype/interfaces/mrtrix3/reconst.py +++ b/nipype/interfaces/mrtrix3/reconst.py @@ -71,11 +71,10 @@ class FitTensor(MRTrix3Base): input_spec = FitTensorInputSpec output_spec = FitTensorOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs - + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + class EstimateFODInputSpec(MRTrix3BaseInputSpec): in_file = File(exists=True, argstr='%s', mandatory=True, position=-3, @@ -187,7 +186,7 @@ class EstimateFOD(MRTrix3Base): input_spec = EstimateFODInputSpec output_spec = EstimateFODOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/mrtrix3/tests/test_auto_MRTrix3Base.py b/nipype/interfaces/mrtrix3/tests/test_auto_MRTrix3Base.py index c03da343e2..8349b13b9d 100644 --- a/nipype/interfaces/mrtrix3/tests/test_auto_MRTrix3Base.py +++ b/nipype/interfaces/mrtrix3/tests/test_auto_MRTrix3Base.py @@ -21,3 +21,11 @@ def test_MRTrix3Base_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_MRTrix3Base_outputs(): + output_map = dict() + outputs = MRTrix3Base.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/mrtrix3/tracking.py b/nipype/interfaces/mrtrix3/tracking.py index 7495211543..b92ed88a09 100644 --- a/nipype/interfaces/mrtrix3/tracking.py +++ b/nipype/interfaces/mrtrix3/tracking.py @@ -248,7 +248,7 @@ def _format_arg(self, name, trait_spec, value): return super(Tractography, self)._format_arg(name, trait_spec, value) - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/mrtrix3/utils.py b/nipype/interfaces/mrtrix3/utils.py index 6a5b68f521..1b10842651 100644 --- a/nipype/interfaces/mrtrix3/utils.py +++ b/nipype/interfaces/mrtrix3/utils.py @@ -56,10 +56,9 @@ class BrainMask(CommandLine): input_spec = BrainMaskInputSpec output_spec = BrainMaskOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) class Mesh2PVEInputSpec(CommandLineInputSpec): @@ -103,10 +102,9 @@ class Mesh2PVE(CommandLine): input_spec = Mesh2PVEInputSpec output_spec = Mesh2PVEOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) class Generate5ttInputSpec(CommandLineInputSpec): @@ -150,10 +148,9 @@ class Generate5tt(CommandLine): input_spec = Generate5ttInputSpec output_spec = Generate5ttOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) class TensorMetricsInputSpec(CommandLineInputSpec): @@ -207,14 +204,11 @@ class TensorMetrics(CommandLine): input_spec = TensorMetricsInputSpec output_spec = TensorMetricsOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - + def _post_run(self): for k in list(outputs.keys()): if isdefined(getattr(self.inputs, k)): - outputs[k] = op.abspath(getattr(self.inputs, k)) + setattr(self.outputs, k, op.abspath(getattr(self.inputs, k))) - return outputs class ComputeTDIInputSpec(CommandLineInputSpec): @@ -347,10 +341,9 @@ class ComputeTDI(MRTrix3Base): input_spec = ComputeTDIInputSpec output_spec = ComputeTDIOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) class TCK2VTKInputSpec(CommandLineInputSpec): @@ -398,7 +391,7 @@ class TCK2VTK(MRTrix3Base): input_spec = TCK2VTKInputSpec output_spec = TCK2VTKOutputSpec - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = op.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + + self.outputs.out_file = op.abspath(self.inputs.out_file) + diff --git a/nipype/interfaces/nipy/model.py b/nipype/interfaces/nipy/model.py index f6ff113530..168bd3f9a5 100644 --- a/nipype/interfaces/nipy/model.py +++ b/nipype/interfaces/nipy/model.py @@ -73,9 +73,9 @@ class FitGLMOutputSpec(TraitedSpec): class FitGLM(BaseInterface): - ''' + """ Fit GLM model based on the specified design. Supports only single or concatenated runs. - ''' + """ input_spec = FitGLMInputSpec output_spec = FitGLMOutputSpec @@ -192,21 +192,19 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["beta"] = self._beta_file - outputs["nvbeta"] = self._nvbeta - outputs["s2"] = self._s2_file - outputs["dof"] = self._dof - outputs["constants"] = self._constants - outputs["axis"] = self._axis - outputs["reg_names"] = self._reg_names + def _post_run(self): + self.outputs.beta = self._beta_file + self.outputs.nvbeta = self._nvbeta + self.outputs.s2 = self._s2_file + self.outputs.dof = self._dof + self.outputs.constants = self._constants + self.outputs.axis = self._axis + self.outputs.reg_names = self._reg_names if self.inputs.model == "ar1": - outputs["a"] = self._a_file + self.outputs.a = self._a_file if self.inputs.save_residuals: - outputs["residuals"] = self._residuals_file - return outputs - + self.outputs.residuals = self._residuals_file + class EstimateContrastInputSpec(BaseInterfaceInputSpec): contrasts = traits.List( @@ -252,9 +250,9 @@ class EstimateContrastOutputSpec(TraitedSpec): class EstimateContrast(BaseInterface): - ''' + """ Estimate contrast of a fitted model. - ''' + """ input_spec = EstimateContrastInputSpec output_spec = EstimateContrastOutputSpec @@ -312,9 +310,8 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["stat_maps"] = self._stat_maps - outputs["p_maps"] = self._p_maps - outputs["z_maps"] = self._z_maps - return outputs + def _post_run(self): + self.outputs.stat_maps = self._stat_maps + self.outputs.p_maps = self._p_maps + self.outputs.z_maps = self._z_maps + \ No newline at end of file diff --git a/nipype/interfaces/nipy/preprocess.py b/nipype/interfaces/nipy/preprocess.py index 49a493f02c..106931e2b2 100644 --- a/nipype/interfaces/nipy/preprocess.py +++ b/nipype/interfaces/nipy/preprocess.py @@ -70,11 +70,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["brain_mask"] = self._brain_mask_path - return outputs - + def _post_run(self): + self.outputs.brain_mask = self._brain_mask_path + class FmriRealign4dInputSpec(BaseInterfaceInputSpec): @@ -190,12 +188,10 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self._out_file_path - outputs['par_file'] = self._par_file_path - return outputs - + def _post_run(self): + self.outputs.out_file = self._out_file_path + self.outputs.par_file = self._par_file_path + class SpaceTimeRealignerInputSpec(BaseInterfaceInputSpec): @@ -321,12 +317,10 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = self._out_file_path - outputs['par_file'] = self._par_file_path - return outputs - + def _post_run(self): + self.outputs.out_file = self._out_file_path + self.outputs.par_file = self._par_file_path + class TrimInputSpec(BaseInterfaceInputSpec): in_file = File( @@ -365,7 +359,7 @@ class Trim(BaseInterface): output_spec = TrimOutputSpec def _run_interface(self, runtime): - out_file = self._list_outputs()['out_file'] + out_file = self.outputs.out_file nii = nb.load(self.inputs.in_file) if self.inputs.end_index == 0: s = slice(self.inputs.begin_index, nii.shape[3]) @@ -375,13 +369,13 @@ def _run_interface(self, runtime): nb.save(nii2, out_file) return runtime - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): - outputs['out_file'] = fname_presuffix( + def _post_run(self): + + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): + self.outputs.out_file = fname_presuffix( self.inputs.in_file, newpath=os.getcwd(), suffix=self.inputs.suffix) - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs + self.outputs.out_file = os.path.abspath(self.outputs.out_file) + \ No newline at end of file diff --git a/nipype/interfaces/nipy/utils.py b/nipype/interfaces/nipy/utils.py index 0e78111c0e..84e8ae9715 100644 --- a/nipype/interfaces/nipy/utils.py +++ b/nipype/interfaces/nipy/utils.py @@ -99,7 +99,6 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['similarity'] = self._similarity - return outputs + def _post_run(self): + self.outputs.similarity = self._similarity + \ No newline at end of file diff --git a/nipype/interfaces/nitime/analysis.py b/nipype/interfaces/nitime/analysis.py index da76a5882f..8d206411d7 100644 --- a/nipype/interfaces/nitime/analysis.py +++ b/nipype/interfaces/nitime/analysis.py @@ -167,8 +167,8 @@ def _run_interface(self, runtime): return runtime # Rewrite _list_outputs (look at BET) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + # if isdefined(self.inputs.output_csv_file): @@ -176,8 +176,8 @@ def _list_outputs(self): # file name + path) # Always defined (the arrays): - outputs['coherence_array'] = self.coherence - outputs['timedelay_array'] = self.delay + self.outputs.coherence_array = self.coherence + self.outputs.timedelay_array = self.delay # Conditional if isdefined(self.inputs.output_csv_file) and hasattr(self, 'coherence'): @@ -185,18 +185,17 @@ def _list_outputs(self): # coherence values to this file "coherence_csv" and makes the # time_delay csv file?? self._make_output_files() - outputs['coherence_csv'] = fname_presuffix(self.inputs.output_csv_file, suffix='_coherence') + self.outputs.coherence_csv = fname_presuffix(self.inputs.output_csv_file, suffix='_coherence') - outputs['timedelay_csv'] = fname_presuffix(self.inputs.output_csv_file, suffix='_delay') + self.outputs.timedelay_csv = fname_presuffix(self.inputs.output_csv_file, suffix='_delay') if isdefined(self.inputs.output_figure_file) and hasattr(self, 'coherence'): self._make_output_figures() - outputs['coherence_fig'] = fname_presuffix(self.inputs.output_figure_file, suffix='_coherence') - outputs['timedelay_fig'] = fname_presuffix(self.inputs.output_figure_file, suffix='_delay') - - return outputs + self.outputs.coherence_fig = fname_presuffix(self.inputs.output_figure_file, suffix='_coherence') + self.outputs.timedelay_fig = fname_presuffix(self.inputs.output_figure_file, suffix='_delay') + def _make_output_files(self): """ Generate the output csv files. diff --git a/nipype/interfaces/petpvc.py b/nipype/interfaces/petpvc.py index 00dbbfb3ea..c21edfc638 100644 --- a/nipype/interfaces/petpvc.py +++ b/nipype/interfaces/petpvc.py @@ -162,17 +162,16 @@ class PETPVC(CommandLine): output_spec = PETPVCOutputSpec _cmd = 'petpvc' - def _list_outputs(self): - outputs = self.output_spec().get() - outputs['out_file'] = self.inputs.out_file - if not isdefined(outputs['out_file']): + def _post_run(self): + + self.outputs.out_file = self.inputs.out_file + if not isdefined(self.outputs.out_file): method_name = self.inputs.pvc.lower() - outputs['out_file'] = self._gen_fname(self.inputs.in_file, + self.outputs.out_file = self._gen_fname(self.inputs.in_file, suffix='_{}_pvc'.format(method_name)) - outputs['out_file'] = os.path.abspath(outputs['out_file']) - return outputs - + self.outputs.out_file = os.path.abspath(self.outputs.out_file) + def _gen_fname(self, basename, cwd=None, suffix=None, change_ext=True, ext='.nii.gz'): """Generate a filename based on the given parameters. @@ -220,5 +219,5 @@ def _gen_fname(self, basename, cwd=None, suffix=None, change_ext=True, def _gen_filename(self, name): if name == 'out_file': - return self._list_outputs()['out_file'] + return self.outputs.out_file return None diff --git a/nipype/interfaces/slicer/tests/test_auto_SlicerCommandLine.py b/nipype/interfaces/slicer/tests/test_auto_SlicerCommandLine.py index e480e76324..003a27e3e3 100644 --- a/nipype/interfaces/slicer/tests/test_auto_SlicerCommandLine.py +++ b/nipype/interfaces/slicer/tests/test_auto_SlicerCommandLine.py @@ -21,3 +21,11 @@ def test_SlicerCommandLine_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_SlicerCommandLine_outputs(): + output_map = dict() + outputs = SlicerCommandLine.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/specs.py b/nipype/interfaces/specs.py new file mode 100644 index 0000000000..ac165cf783 --- /dev/null +++ b/nipype/interfaces/specs.py @@ -0,0 +1,658 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +""" +Definition of inputs/outputs of interfaces. +""" + +from __future__ import print_function +from __future__ import division + +from copy import deepcopy +import os +import re +from textwrap import wrap + +from future import standard_library +standard_library.install_aliases() +from builtins import range +from builtins import object + +from .traits_extension import (traits, Undefined, TraitDictObject, TraitListObject, TraitError, + isdefined, File, has_metadata) +from ..utils.filemanip import md5, auto_hash, split_filename +from ..utils.misc import is_container +from ..utils.errors import InterfaceInputsError +from .. import logging, LooseVersion +from .. import __version__ +from ..external.six import string_types + +NIPYPE_VERSION = LooseVersion(__version__) +IFLOGGER = logging.getLogger('interface') +__docformat__ = 'restructuredtext' + + +class BaseTraitedSpec(traits.HasTraits): + """Provide a few methods necessary to support nipype interface api + + The inputs attribute of interfaces call certain methods that are not + available in traits.HasTraits. These are provided here. + + new metadata: + + * usedefault : set this to True if the default value of the trait should be + used. Unless this is set, the attributes are set to traits.Undefined + + new attribute: + + * get_hashval : returns a tuple containing the state of the trait as a dict + and hashvalue corresponding to dict. + + XXX Reconsider this in the long run, but it seems like the best + solution to move forward on the refactoring. + """ + + def __init__(self, **kwargs): + """ Initialize handlers and inputs""" + # NOTE: In python 2.6, object.__init__ no longer accepts input + # arguments. HasTraits does not define an __init__ and + # therefore these args were being ignored. + # super(TraitedSpec, self).__init__(*args, **kwargs) + super(BaseTraitedSpec, self).__init__(**kwargs) + traits.push_exception_handler(reraise_exceptions=True) + undefined_traits = {} + for trait in self.copyable_trait_names(): + if not self.traits()[trait].usedefault: + undefined_traits[trait] = Undefined + self.trait_set(trait_change_notify=False, **undefined_traits) + + # Attach deprecated handler + has_deprecation = dict(deprecated=lambda t: t is not None) + deprecated = self.trait_names(**has_deprecation) + for elem in deprecated: + self.on_trait_change(self._check_deprecated, elem) + + # Forward inputs + self.set(**kwargs) + + def __repr__(self): + """ Return a well-formatted representation of the traits """ + outstr = [] + for name, value in sorted(self.trait_get().items()): + outstr.append('%s = %s' % (name, value)) + return '\n' + '\n'.join(outstr) + '\n' + + def items(self): + """ Name, trait generator for user modifiable traits + """ + for name in sorted(self.copyable_trait_names()): + yield name, self.traits()[name] + + def namesource_items(self): + """Get inputs that will generate outputs""" + meta = dict(name_source=lambda t: t is not None) + meta_ns = dict(ns=lambda t: t is not None) + return list(self.traits(**meta).items()) + list(self.traits(**meta_ns).items()) + + def _check_deprecated(self, name, new): + """ Generate a warning when a deprecated trait is set """ + if isdefined(new): + spec = self.traits()[name] + msg1 = ('Input %s in interface %s is deprecated.' % + (name, + self.__class__.__name__.split('InputSpec')[0])) + msg2 = ('Will be removed or raise an error as of release %s' + % spec.deprecated) + if spec.new_name: + if spec.new_name not in self.copyable_trait_names(): + raise TraitError(msg1 + ' Replacement trait %s not found' % + spec.new_name) + msg3 = 'It has been replaced by %s.' % spec.new_name + else: + msg3 = '' + msg = ' '.join((msg1, msg2, msg3)) + if LooseVersion(str(spec.deprecated)) < NIPYPE_VERSION: + raise TraitError(msg) + else: + if spec.new_name: + msg += 'Unsetting old value %s; setting new value %s.' % ( + name, spec.new_name) + IFLOGGER.warn(msg) + if spec.new_name: + self.trait_set(trait_change_notify=False, + **{'%s' % name: Undefined, + '%s' % spec.new_name: new}) + + def _hash_infile(self, adict, key): + """ Inject file hashes into adict[key]""" + stuff = adict[key] + if not is_container(stuff): + stuff = [stuff] + file_list = [] + for afile in stuff: + if is_container(afile): + hashlist = self._hash_infile({'infiles': afile}, 'infiles') + hashval = [val[1] for val in hashlist] + else: + hashval = auto_hash(afile) + + file_list.append((afile, hashval)) + return file_list + + def get(self, **kwargs): + """ Returns traited class as a dict + + Augments the trait get function to return a dictionary without + notification handles + """ + out = super(BaseTraitedSpec, self).get(**kwargs) + out = self._clean_container(out, Undefined) + return out + + def get_traitsfree(self, **kwargs): + """ Returns traited class as a dict + + Augments the trait get function to return a dictionary without + any traits. The dictionary does not contain any attributes that + were Undefined + """ + out = super(BaseTraitedSpec, self).get(**kwargs) + out = self._clean_container(out, skipundefined=True) + return out + + def _clean_container(self, obj, undefinedval=None, skipundefined=False): + """Convert a traited obejct into a pure python representation. + """ + if isinstance(obj, TraitDictObject) or isinstance(obj, dict): + outdict = {} + for key, val in list(obj.items()): + if isdefined(val): + outdict[key] = self._clean_container(val, undefinedval) + else: + if not skipundefined: + outdict[key] = undefinedval + return outdict + elif (isinstance(obj, TraitListObject) or + isinstance(obj, list) or isinstance(obj, tuple)): + out = [] + for val in obj: + if isdefined(val): + out.append(self._clean_container(val, undefinedval)) + else: + if not skipundefined: + out.append(undefinedval) + else: + out.append(None) + if isinstance(obj, tuple): + return tuple(out) + else: + if isdefined(obj): + out = obj + else: + if not skipundefined: + out = undefinedval + return out + + def get_hashval(self, hash_method=None): + """Return a dictionary of our items with hashes for each file. + + Searches through dictionary items and if an item is a file, it + calculates the md5 hash of the file contents and stores the + file name and hash value as the new key value. + + However, the overall bunch hash is calculated only on the hash + value of a file. The path and name of the file are not used in + the overall hash calculation. + + Returns + ------- + dict_withhash : dict + Copy of our dictionary with the new file hashes included + with each file. + hashvalue : str + The md5 hash value of the traited spec + + """ + + dict_withhash = [] + dict_nofilename = [] + for name, val in sorted(self.get().items()): + if isdefined(val): + trait = self.trait(name) + if has_metadata(trait.trait_type, "nohash", True): + continue + hash_files = (not has_metadata(trait.trait_type, "hash_files", + False) and not + has_metadata(trait.trait_type, "name_source")) + dict_nofilename.append((name, + self._get_sorteddict(val, hash_method=hash_method, + hash_files=hash_files))) + dict_withhash.append((name, + self._get_sorteddict(val, True, hash_method=hash_method, + hash_files=hash_files))) + return dict_withhash, md5(str(dict_nofilename).encode()).hexdigest() + + def _get_sorteddict(self, obj, dictwithhash=False, hash_method=None, + hash_files=True): + out = None + if isinstance(obj, dict): + obj_items = [(key, val) for key, val in sorted(obj.items()) if isdefined(val)] + out = [(key, self._get_sorteddict(val, dictwithhash, hash_method=hash_method, + hash_files=hash_files)) for key, val in obj_items] + elif isinstance(obj, (list, tuple)): + out = [self._get_sorteddict( + val, dictwithhash, hash_method=hash_method, hash_files=hash_files) + for val in obj if isdefined(val)] + if isinstance(obj, tuple): + return tuple(out) + elif isinstance(obj, float): + out = '%.10f' % obj + elif isinstance(obj, string_types) and hash_files and os.path.isfile(obj): + out = auto_hash(obj, hash_method) + if dictwithhash: + return (obj, out) + elif isdefined(obj): + out = obj + return out + + def _get_trait_desc(self, name, spec=None): + if spec is None: + spec = self.traits()[name] + + desc = spec.desc + xor = spec.xor + requires = spec.requires + argstr = spec.argstr + name_source = spec.name_source + if name_source is None: + name_source = spec.ns + + manhelpstr = ['\t%s' % name] + + type_info = spec.full_info(self, name, None) + + default = '' + if spec.usedefault: + default = ', nipype default value: %s' % str(spec.default_value()[1]) + line = "(%s%s)" % (type_info, default) + + manhelpstr = wrap(line, 70, + initial_indent=manhelpstr[0] + ': ', + subsequent_indent='\t\t ') + + if desc: + for line in desc.split('\n'): + line = re.sub(r'\s+', ' ', line) + manhelpstr += wrap(line, 70, initial_indent='\t\t', + subsequent_indent='\t\t') + + if argstr: + pos = spec.position + if pos is not None: + manhelpstr += wrap('flag: %s, position: %s' % (argstr, pos), 70, + initial_indent='\t\t', subsequent_indent='\t\t') + else: + manhelpstr += wrap('flag: %s' % argstr, 70, initial_indent='\t\t', + subsequent_indent='\t\t') + + if xor: + line = '%s' % ', '.join(xor) + manhelpstr += wrap(line, 70, initial_indent='\t\tmutually_exclusive: ', + subsequent_indent='\t\t ') + + if requires: + others = [field for field in requires if field != name] + line = '%s' % ', '.join(others) + manhelpstr += wrap(line, 70, initial_indent='\t\trequires: ', + subsequent_indent='\t\t ') + + if name_source: + tpl = ', name_template not defined' + if spec.name_template: + tpl = ', name_template is \'%s\'' % spec.name_template + manhelpstr += wrap(('name source: %s' % name_source) + tpl, 70, + initial_indent='\t\t', subsequent_indent='\t\t ') + return manhelpstr + + def help(self): + """Print help of these traits""" + helpstr = [] + for name, spec in sorted(self.traits(transient=None).items()): + helpstr += self._get_trait_desc(name, spec) + if len(helpstr) == 0: + helpstr += ['\tNone'] + return helpstr + + +class TraitedSpec(BaseTraitedSpec): + """ Create a subclass with strict traits. + + This is used in 90% of the cases. + """ + _ = traits.Disallow + + +class BaseInputSpec(BaseTraitedSpec): + """ Base class for InputSpecs with strict traits """ + _ = traits.Disallow + + def __init__(self, **kwargs): + """ Initialize handlers and inputs""" + super(BaseInputSpec, self).__init__(**kwargs) + + # Attach xor handler + has_xor = dict(xor=lambda t: t is not None) + xors = self.trait_names(**has_xor) + for elem in xors: + self.on_trait_change(self._check_xor, elem) + + def mandatory_items(self): + """Get those items that are mandatory""" + return list(self.traits(mandatory=True).items()) + + def optional_items(self): + """Get those items that are optional""" + allitems = self.traits(transient=None).items() + for k, _ in self.mandatory_items(): + try: + allitems.remove(k) + except ValueError: + pass + return allitems + + def _check_xor(self, obj, name, old, new): + """ Checks inputs with xor list """ + IFLOGGER.error('Called check_xorg with name %s' % name) + if isdefined(getattr(self, name)): + xor_list = self.traits()[name].xor + if not isinstance(xor_list, list): + xor_list = list(xor_list) + + if name in xor_list: + xor_list.remove(name) + # for each xor, set to default_value + for trait_name in xor_list: + trait_val = getattr(self, trait_name) + if isdefined(trait_val) and isinstance(trait_val, bool) and not trait_val: + trait_val = Undefined # Boolean inputs set false should not count as defined + if isdefined(trait_val): + self.trait_set(trait_change_notify=False, + **{'%s' % name: Undefined}) + msg = ('Input "%s" is mutually exclusive with input "%s", ' + 'which is already set') % (name, trait_name) + raise TraitError(msg) + + def _check_requires(self, name, spec=None): + if not isdefined(getattr(self, name)): + return True + if spec is None: + spec = self.traits()[name] + if spec.requires is None: + return True + + req_defined = [isdefined(rname) for rname in getattr(spec, 'requires', [])] + if not all(req_defined): + raise ValueError( + '%s requires a value for input \'%s\' because one of %s is set. For a list of' + ' required inputs, see %s.help()' % (self.__class__.__name__, name, + ', '.join(spec.requires), self.__class__.__name__)) + return True + + + def check_inputs(self): + """ Raises an exception if a mandatory input is Undefined + """ + for name, spec in list(self.mandatory_items()): + value = getattr(self, name) + if not isdefined(value): + xor_spec = getattr(spec, 'xor', []) + if xor_spec is None: + xor_spec = [] + + if xor_spec and not any([isdefined(getattr(self, xname)) for xname in xor_spec]): + raise ValueError( + '%s requires a value for one of these inputs %s. For a list of required inputs, ' + 'see %s.help()' % (self.__class__.__name__, [name] + xor_spec, self.__class__.__name__)) + self._check_requires(name) + + for elem in list(self.optional_items()): + self._check_requires(*elem) + + def get_filecopy_info(self): + """ Provides information about file inputs to copy or link to cwd. + Necessary for pipeline operation + """ + info = [] + metadata = dict(copyfile=lambda t: t is not None) + for name, spec in sorted(self.traits(**metadata).items()): + info.append(dict(key=name, copy=spec.copyfile)) + return info + + def check_version(self, version, raise_exception=True): + """ Raises an exception on version mismatch""" + unavailable_traits = [] + # check minimum version + check = dict(min_ver=lambda t: t is not None) + + for name in self.trait_names(**check): + min_ver = LooseVersion(str(self.traits()[name].min_ver)) + if min_ver > version: + unavailable_traits.append(name) + if not isdefined(getattr(self, name)): + continue + + msg = ('Trait %s (%s) (version %s < required %s)' % + (name, self.__class__.__name__, version, min_ver)) + if raise_exception: + raise Exception(msg) + else: + IFLOGGER.warn(msg) + + # Check maximum version + check = dict(max_ver=lambda t: t is not None) + for name in self.trait_names(**check): + max_ver = LooseVersion(str(self.traits()[name].max_ver)) + if max_ver < version: + unavailable_traits.append(name) + if not isdefined(getattr(self, name)): + continue + msg = ('Trait %s (%s) (version %s > required %s)' % + (name, self.__class__.__name__, version, max_ver)) + if raise_exception: + raise Exception(msg) + else: + IFLOGGER.warn(msg) + + return unavailable_traits + + def help(self): + """Print inputs formatted""" + manhelpstr = [] + for name, spec in sorted(self.mandatory_items()): + manhelpstr += self._get_trait_desc(name, spec) + opthelpstr = [] + for name, spec in sorted(self.optional_items()): + opthelpstr += self._get_trait_desc(name, spec) + + helpstr = [] + if manhelpstr: + manhelpstr.insert(0, '') + manhelpstr.insert(1, '\t[Mandatory]') + helpstr += manhelpstr + if opthelpstr: + opthelpstr.insert(0, '') + opthelpstr.insert(1, '\t[Optional]') + helpstr += opthelpstr + + if not helpstr: + return ['', '\tNone'] + + return helpstr + + +class DynamicTraitedSpec(BaseTraitedSpec): + """ A subclass to handle dynamic traits + + This class is a workaround for add_traits and clone_traits not + functioning well together. + """ + + def __deepcopy__(self, memo): + """ bug in deepcopy for HasTraits results in weird cloning behavior for + added traits + """ + id_self = id(self) + if id_self in memo: + return memo[id_self] + dup_dict = deepcopy(self.get(), memo) + # access all keys + for key in self.copyable_trait_names(): + _ = getattr(self, key) + # clone once + dup = self.clone_traits(memo=memo) + for key in self.copyable_trait_names(): + try: + _ = getattr(dup, key) + except: + pass + # clone twice + dup = self.clone_traits(memo=memo) + dup.set(**dup_dict) + return dup + + +class BaseInterfaceInputSpec(BaseInputSpec): + """ BaseInputSpec with an input added to ignore exceptions """ + ignore_exception = traits.Bool(False, usedefault=True, nohash=True, + desc='Print an error message instead of throwing an exception' + ' in case the interface fails to run') + + +class CommandLineInputSpec(BaseInterfaceInputSpec): + """ The InputSpec for interfaces wrapping a command line """ + args = traits.Str(argstr='%s', desc='Additional parameters to the command') + environ = traits.DictStrStr(desc='Environment variables', usedefault=True, + nohash=True) + # This input does not have a "usedefault=True" so the set_default_terminal_output() + # method would work + terminal_output = traits.Enum( + 'stream', 'allatonce', 'file', 'none', nohash=True, + desc='Control terminal output: `stream` - displays to terminal immediately (default), ' + '`allatonce` - waits till command is finished to display output, `file` - ' + 'writes output to file, `none` - output is ignored') + + def _format_arg(self, name, spec=None, value=None): + """A helper function for parse_args + + Formats a trait containing argstr metadata + """ + if spec is None: + spec = self.traits()[name] + + if value is None: + value = getattr(self, name) + + argstr = spec.argstr + IFLOGGER.debug('Formatting %s, value=%s' % (name, str(value))) + if spec.is_trait_type(traits.Bool) and "%" not in argstr: + if value: + # Boolean options have no format string. Just append options + # if True. + return argstr + else: + return None + # traits.Either turns into traits.TraitCompound and does not have any + # inner_traits + elif spec.is_trait_type(traits.List) \ + or (spec.is_trait_type(traits.TraitCompound) and + isinstance(value, list)): + # This is a bit simple-minded at present, and should be + # construed as the default. If more sophisticated behavior + # is needed, it can be accomplished with metadata (e.g. + # format string for list member str'ification, specifying + # the separator, etc.) + + # Depending on whether we stick with traitlets, and whether or + # not we beef up traitlets.List, we may want to put some + # type-checking code here as well + sep = spec.sep + if sep is None: + sep = ' ' + if argstr.endswith('...'): + + # repeatable option + # --id %d... will expand to + # --id 1 --id 2 --id 3 etc.,. + argstr = argstr.replace('...', '') + return sep.join([argstr % elt for elt in value]) + else: + return argstr % sep.join(str(elt) for elt in value) + else: + # Append options using format string. + return argstr % value + + def parse_args(self, skip=None): + """Parse all inputs using the ``argstr`` format string in the Trait. + + Any inputs that are assigned (not the default_value) are formatted + to be added to the command line. + + Returns + ------- + all_args : list + A list of all inputs formatted for the command line. + + """ + all_args = [] + initial_args = {} + final_args = {} + metadata = dict(argstr=lambda t: t is not None) + for name, spec in sorted(self.traits(**metadata).items()): + if skip and name in skip: + continue + value = getattr(self, name) + if not isdefined(value): + continue + + arg = self._format_arg(name, spec, value) + if arg is None: + continue + pos = spec.position + if pos is not None: + if int(pos) >= 0: + initial_args[pos] = arg + else: + final_args[pos] = arg + else: + all_args.append(arg) + first_args = [arg for pos, arg in sorted(initial_args.items())] + last_args = [arg for pos, arg in sorted(final_args.items())] + return first_args + all_args + last_args + + +class StdOutCommandLineInputSpec(CommandLineInputSpec): + """Appends a command line argument to pipe standard output to a file""" + out_file = File('standard.out', argstr="> %s", position=-1, usedefault=True) + +class StdOutCommandLineOutputSpec(TraitedSpec): + out_file = File(exists=True, desc='file containing the standard output') + + +class MpiCommandLineInputSpec(CommandLineInputSpec): + """Appends the necessary inputs to run MpiCommandLine interfaces""" + use_mpi = traits.Bool(False, usedefault=True, + desc='Whether or not to run the command with mpiexec') + n_procs = traits.Int(desc='Num processors to specify to mpiexec. Do not specify if this ' + 'is managed externally (e.g. through SGE)') + + +class SEMLikeCommandLineInputSpec(CommandLineInputSpec): + """Redefines the formatting of outputs""" + + def _format_arg(self, name, spec=None, value=None): + if name in list(self._outputs_filenames.keys()): + if isinstance(value, bool): + if value: + value = os.path.abspath(self._outputs_filenames[name]) + else: + return "" + return super(SEMLikeCommandLineInputSpec, self)._format_arg(name, spec, value) diff --git a/nipype/interfaces/spm/base.py b/nipype/interfaces/spm/base.py index 0ca470d0ed..8581d1da12 100644 --- a/nipype/interfaces/spm/base.py +++ b/nipype/interfaces/spm/base.py @@ -307,7 +307,7 @@ def _check_mlab_inputs(self): def _run_interface(self, runtime): """Executes the SPM function using MATLAB.""" self.mlab.inputs.script = self._make_matlab_command( - deepcopy(self._parse_inputs())) + deepcopy(self.parse_args())) results = self.mlab.run() runtime.returncode = results.runtime.returncode if self.mlab.inputs.uses_mcr: @@ -318,7 +318,7 @@ def _run_interface(self, runtime): runtime.merged = results.runtime.merged return runtime - def _list_outputs(self): + def _post_run(self): """Determine the expected outputs based on inputs.""" raise NotImplementedError @@ -330,7 +330,7 @@ def _format_arg(self, opt, spec, val): else: return val - def _parse_inputs(self, skip=()): + def parse_args(self, skip=()): spmdict = {} metadata = dict(field=lambda t: t is not None) for name, spec in list(self.inputs.traits(**metadata).items()): @@ -447,7 +447,7 @@ def _make_matlab_command(self, contents, postscript=None): ---------- contents : list - a list of dicts generated by _parse_inputs + a list of dicts generated by parse_args in each subclass cwd : string diff --git a/nipype/interfaces/spm/model.py b/nipype/interfaces/spm/model.py index 2a3472a648..1ff3175a8f 100644 --- a/nipype/interfaces/spm/model.py +++ b/nipype/interfaces/spm/model.py @@ -123,10 +123,10 @@ def _format_arg(self, opt, spec, val): return val return super(Level1Design, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm realign options if set to None ignore """ - einputs = super(Level1Design, self)._parse_inputs(skip=('mask_threshold')) + einputs = super(Level1Design, self).parse_args(skip=('mask_threshold')) for sessinfo in einputs[0]['sess']: sessinfo['scans'] = scans_for_fnames(filename_to_list(sessinfo['scans']), keep4d=False) if not isdefined(self.inputs.spm_mat_dir): @@ -152,12 +152,10 @@ def _make_matlab_command(self, content): postscript = None return super(Level1Design, self)._make_matlab_command(content, postscript=postscript) - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): spm = os.path.join(os.getcwd(), 'SPM.mat') - outputs['spm_mat_file'] = spm - return outputs - + self.outputs.spm_mat_file = spm + class EstimateModelInputSpec(SPMCommandInputSpec): spm_mat_file = File(exists=True, field='spmmat', desc='absolute path to SPM.mat', @@ -206,43 +204,41 @@ def _format_arg(self, opt, spec, val): return val return super(EstimateModel, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm realign options if set to None ignore """ - einputs = super(EstimateModel, self)._parse_inputs(skip=('flags')) + einputs = super(EstimateModel, self).parse_args(skip=('flags')) if isdefined(self.inputs.flags): einputs[0].update(self.inputs.flags) return einputs - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): pth, _ = os.path.split(self.inputs.spm_mat_file) spm12 = '12' in self.version.split('.')[0] if spm12: mask = os.path.join(pth, 'mask.nii') else: mask = os.path.join(pth, 'mask.img') - outputs['mask_image'] = mask + self.outputs.mask_image = mask spm = sio.loadmat(self.inputs.spm_mat_file, struct_as_record=False) betas = [] for vbeta in spm['SPM'][0, 0].Vbeta[0]: betas.append(str(os.path.join(pth, vbeta.fname[0]))) if betas: - outputs['beta_images'] = betas + self.outputs.beta_images = betas if spm12: resms = os.path.join(pth, 'ResMS.nii') else: resms = os.path.join(pth, 'ResMS.img') - outputs['residual_image'] = resms + self.outputs.residual_image = resms if spm12: rpv = os.path.join(pth, 'RPV.nii') else: rpv = os.path.join(pth, 'RPV.img') - outputs['RPVimage'] = rpv + self.outputs.RPVimage = rpv spm = os.path.join(pth, 'SPM.mat') - outputs['spm_mat_file'] = spm - return outputs - + self.outputs.spm_mat_file = spm + class EstimateContrastInputSpec(SPMCommandInputSpec): spm_mat_file = File(exists=True, field='spmmat', @@ -384,8 +380,7 @@ def _make_matlab_command(self, _): script += "spm_jobman('run',jobs);" return script - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): pth, _ = os.path.split(self.inputs.spm_mat_file) spm = sio.loadmat(self.inputs.spm_mat_file, struct_as_record=False) con_images = [] @@ -394,24 +389,23 @@ def _list_outputs(self): con_images.append(str(os.path.join(pth, con.Vcon[0, 0].fname[0]))) spmT_images.append(str(os.path.join(pth, con.Vspm[0, 0].fname[0]))) if con_images: - outputs['con_images'] = con_images - outputs['spmT_images'] = spmT_images + self.outputs.con_images = con_images + self.outputs.spmT_images = spmT_images spm12 = '12' in self.version.split('.')[0] if spm12: ess = glob(os.path.join(pth, 'ess*.nii')) else: ess = glob(os.path.join(pth, 'ess*.img')) if len(ess) > 0: - outputs['ess_images'] = sorted(ess) + self.outputs.ess_images = sorted(ess) if spm12: spmf = glob(os.path.join(pth, 'spmF*.nii')) else: spmf = glob(os.path.join(pth, 'spmF*.img')) if len(spmf) > 0: - outputs['spmF_images'] = sorted(spmf) - outputs['spm_mat_file'] = self.inputs.spm_mat_file - return outputs - + self.outputs.spmF_images = sorted(spmf) + self.outputs.spm_mat_file = self.inputs.spm_mat_file + class ThresholdInputSpec(SPMCommandInputSpec): spm_mat_file = File(exists=True, desc='absolute path to SPM.mat', copyfile=True, mandatory=True) @@ -436,7 +430,7 @@ class ThresholdOutputSpec(TraitedSpec): class Threshold(SPMCommand): - '''Topological FDR thresholding based on cluster extent/size. Smoothness is + """Topological FDR thresholding based on cluster extent/size. Smoothness is estimated from GLM residuals but is assumed to be the same for all of the voxels. @@ -449,7 +443,7 @@ class Threshold(SPMCommand): >>> thresh.inputs.contrast_index = 1 >>> thresh.inputs.extent_fdr_p_threshold = 0.05 >>> thresh.run() # doctest: +SKIP - ''' + """ input_spec = ThresholdInputSpec output_spec = ThresholdOutputSpec @@ -588,14 +582,11 @@ def aggregate_outputs(self, runtime=None): setattr(outputs, 'pre_topo_n_clusters', int(line[len("pre_topo_n_clusters = "):].strip())) elif line.startswith("cluster_forming_thr = "): setattr(outputs, 'cluster_forming_thr', float(line[len("cluster_forming_thr = "):].strip())) - return outputs - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['thresholded_map'] = self._gen_thresholded_map_filename() - outputs['pre_topo_fdr_map'] = self._gen_pre_topo_map_filename() - return outputs - + + def _post_run(self): + self.outputs.thresholded_map = self._gen_thresholded_map_filename() + self.outputs.pre_topo_fdr_map = self._gen_pre_topo_map_filename() + class ThresholdStatisticsInputSpec(SPMCommandInputSpec): spm_mat_file = File(exists=True, desc='absolute path to SPM.mat', copyfile=True, mandatory=True) @@ -615,7 +606,7 @@ class ThresholdStatisticsOutputSpec(TraitedSpec): class ThresholdStatistics(SPMCommand): - '''Given height and cluster size threshold calculate theoretical probabilities + """Given height and cluster size threshold calculate theoretical probabilities concerning false positives Examples @@ -627,7 +618,7 @@ class ThresholdStatistics(SPMCommand): >>> thresh.inputs.contrast_index = 1 >>> thresh.inputs.height_threshold = 4.56 >>> thresh.run() # doctest: +SKIP - ''' + """ input_spec = ThresholdStatisticsInputSpec output_spec = ThresholdStatisticsOutputSpec @@ -698,8 +689,7 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None): cur_output = line.split()[0] continue - return outputs - + class FactorialDesignInputSpec(SPMCommandInputSpec): spm_mat_dir = Directory(exists=True, field='dir', desc='directory to store SPM.mat file (opt)') @@ -770,20 +760,18 @@ def _format_arg(self, opt, spec, val): return outlist return super(FactorialDesign, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm realign options if set to None ignore """ - einputs = super(FactorialDesign, self)._parse_inputs() + einputs = super(FactorialDesign, self).parse_args() if not isdefined(self.inputs.spm_mat_dir): einputs[0]['dir'] = np.array([str(os.getcwd())], dtype=object) return einputs - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): spm = os.path.join(os.getcwd(), 'SPM.mat') - outputs['spm_mat_file'] = spm - return outputs - + self.outputs.spm_mat_file = spm + class OneSampleTTestDesignInputSpec(FactorialDesignInputSpec): in_files = traits.List(File(exists=True), field='des.t1.scans', diff --git a/nipype/interfaces/spm/preprocess.py b/nipype/interfaces/spm/preprocess.py index c1af8d025c..5f56621c4a 100644 --- a/nipype/interfaces/spm/preprocess.py +++ b/nipype/interfaces/spm/preprocess.py @@ -97,9 +97,8 @@ def _format_arg(self, opt, spec, val): separate_sessions=True) return super(SliceTiming, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['timecorrected_files'] = [] + def _post_run(self): + self.outputs.timecorrected_files = [] filelist = filename_to_list(self.inputs.in_files) for f in filelist: @@ -107,8 +106,7 @@ def _list_outputs(self): run = [fname_presuffix(in_f, prefix=self.inputs.out_prefix) for in_f in f] else: run = fname_presuffix(f, prefix=self.inputs.out_prefix) - outputs['timecorrected_files'].append(run) - return outputs + self.outputs.timecorrected_files.append(run) class RealignInputSpec(SPMCommandInputSpec): @@ -201,35 +199,34 @@ def _format_arg(self, opt, spec, val): separate_sessions=separate_sessions) return super(Realign, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm realign options if set to None ignore """ - einputs = super(Realign, self)._parse_inputs() + einputs = super(Realign, self).parse_args() return [{'%s' % (self.inputs.jobtype): einputs[0]}] - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): resliced_all = self.inputs.write_which[0] > 0 resliced_mean = self.inputs.write_which[1] > 0 if self.inputs.jobtype != "write": if isdefined(self.inputs.in_files): - outputs['realignment_parameters'] = [] + self.outputs.realignment_parameters = [] for imgf in self.inputs.in_files: if isinstance(imgf, list): tmp_imgf = imgf[0] else: tmp_imgf = imgf - outputs['realignment_parameters'].append(fname_presuffix(tmp_imgf, + self.outputs.realignment_parameters.append(fname_presuffix(tmp_imgf, prefix='rp_', suffix='.txt', use_ext=False)) if not isinstance(imgf, list) and func_is_3d(imgf): break if self.inputs.jobtype == "estimate": - outputs['realigned_files'] = self.inputs.in_files + self.outputs.realigned_files = self.inputs.in_files if self.inputs.jobtype == "estimate" or self.inputs.jobtype == "estwrite": - outputs['modified_in_files'] = self.inputs.in_files + self.outputs.modified_in_files = self.inputs.in_files if self.inputs.jobtype == "write" or self.inputs.jobtype == "estwrite": if isinstance(self.inputs.in_files[0], list): first_image = self.inputs.in_files[0][0] @@ -237,10 +234,10 @@ def _list_outputs(self): first_image = self.inputs.in_files[0] if resliced_mean: - outputs['mean_image'] = fname_presuffix(first_image, prefix='mean') + self.outputs.mean_image = fname_presuffix(first_image, prefix='mean') if resliced_all: - outputs['realigned_files'] = [] + self.outputs.realigned_files = [] for idx, imgf in enumerate(filename_to_list(self.inputs.in_files)): realigned_run = [] if isinstance(imgf, list): @@ -251,8 +248,7 @@ def _list_outputs(self): else: realigned_run = fname_presuffix(imgf, prefix=self.inputs.out_prefix) - outputs['realigned_files'].append(realigned_run) - return outputs + self.outputs.realigned_files.append(realigned_run) class CoregisterInputSpec(SPMCommandInputSpec): @@ -334,34 +330,32 @@ def _format_arg(self, opt, spec, val): return scans_for_fnames(val) return super(Coregister, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm coregister options if set to None ignore """ if self.inputs.jobtype == "write": - einputs = super(Coregister, self)._parse_inputs(skip=('jobtype', 'apply_to_files')) + einputs = super(Coregister, self).parse_args(skip=('jobtype', 'apply_to_files')) else: - einputs = super(Coregister, self)._parse_inputs(skip=('jobtype')) + einputs = super(Coregister, self).parse_args(skip=('jobtype')) jobtype = self.inputs.jobtype return [{'%s' % (jobtype): einputs[0]}] - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if self.inputs.jobtype == "estimate": if isdefined(self.inputs.apply_to_files): - outputs['coregistered_files'] = self.inputs.apply_to_files - outputs['coregistered_source'] = self.inputs.source + self.outputs.coregistered_files = self.inputs.apply_to_files + self.outputs.coregistered_source = self.inputs.source elif self.inputs.jobtype == "write" or self.inputs.jobtype == "estwrite": if isdefined(self.inputs.apply_to_files): - outputs['coregistered_files'] = [] + self.outputs.coregistered_files = [] for imgf in filename_to_list(self.inputs.apply_to_files): - outputs['coregistered_files'].append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) + self.outputs.coregistered_files.append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) - outputs['coregistered_source'] = [] + self.outputs.coregistered_source = [] for imgf in filename_to_list(self.inputs.source): - outputs['coregistered_source'].append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) + self.outputs.coregistered_source.append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) - return outputs class NormalizeInputSpec(SPMCommandInputSpec): @@ -462,10 +456,10 @@ def _format_arg(self, opt, spec, val): raise ValueError('%s must have 3 elements' % opt) return super(Normalize, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): """validate spm normalize options if set to None ignore """ - einputs = super(Normalize, self)._parse_inputs(skip=('jobtype', + einputs = super(Normalize, self).parse_args(skip=('jobtype', 'apply_to_files')) if isdefined(self.inputs.apply_to_files): inputfiles = deepcopy(self.inputs.apply_to_files) @@ -479,28 +473,27 @@ def _parse_inputs(self): einputs[0]['subj']['resample'] = scans_for_fname(self.inputs.source) return [{'%s' % (jobtype): einputs[0]}] - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): jobtype = self.inputs.jobtype if jobtype.startswith('est'): - outputs['normalization_parameters'] = [] + self.outputs.normalization_parameters = [] for imgf in filename_to_list(self.inputs.source): - outputs['normalization_parameters'].append(fname_presuffix(imgf, + self.outputs.normalization_parameters.append(fname_presuffix(imgf, suffix='_sn.mat', use_ext=False)) - outputs['normalization_parameters'] = list_to_filename(outputs['normalization_parameters']) + self.outputs.normalization_parameters = list_to_filename(self.outputs.normalization_parameters) if self.inputs.jobtype == "estimate": if isdefined(self.inputs.apply_to_files): - outputs['normalized_files'] = self.inputs.apply_to_files - outputs['normalized_source'] = self.inputs.source + self.outputs.normalized_files = self.inputs.apply_to_files + self.outputs.normalized_source = self.inputs.source elif 'write' in self.inputs.jobtype: if isdefined(self.inputs.write_preserve) and self.inputs.write_preserve: prefixNorm = ''.join(['m', self.inputs.out_prefix]) else: prefixNorm = self.inputs.out_prefix - outputs['normalized_files'] = [] + self.outputs.normalized_files = [] if isdefined(self.inputs.apply_to_files): filelist = filename_to_list(self.inputs.apply_to_files) for f in filelist: @@ -508,14 +501,13 @@ def _list_outputs(self): run = [fname_presuffix(in_f, prefix=prefixNorm) for in_f in f] else: run = [fname_presuffix(f, prefix=prefixNorm)] - outputs['normalized_files'].extend(run) + self.outputs.normalized_files.extend(run) if isdefined(self.inputs.source): - outputs['normalized_source'] = [] + self.outputs.normalized_source = [] for imgf in filename_to_list(self.inputs.source): - outputs['normalized_source'].append(fname_presuffix(imgf, + self.outputs.normalized_source.append(fname_presuffix(imgf, prefix=prefixNorm)) - return outputs class Normalize12InputSpec(SPMCommandInputSpec): @@ -624,10 +616,10 @@ def _format_arg(self, opt, spec, val): raise ValueError('%s must have 5 elements' % opt) return super(Normalize12, self)._format_arg(opt, spec, val) - def _parse_inputs(self, skip=()): + def parse_args(self, skip=()): """validate spm normalize options if set to None ignore """ - einputs = super(Normalize12, self)._parse_inputs(skip=('jobtype', + einputs = super(Normalize12, self).parse_args(skip=('jobtype', 'apply_to_files')) if isdefined(self.inputs.apply_to_files): inputfiles = deepcopy(self.inputs.apply_to_files) @@ -641,24 +633,23 @@ def _parse_inputs(self, skip=()): einputs[0]['subj']['resample'] = scans_for_fname(self.inputs.image_to_align) return [{'%s' % (jobtype): einputs[0]}] - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): jobtype = self.inputs.jobtype if jobtype.startswith('est'): - outputs['deformation_field'] = [] + self.outputs.deformation_field = [] for imgf in filename_to_list(self.inputs.image_to_align): - outputs['deformation_field'].append(fname_presuffix(imgf, + self.outputs.deformation_field.append(fname_presuffix(imgf, prefix='y_')) - outputs['deformation_field'] = list_to_filename(outputs['deformation_field']) + self.outputs.deformation_field = list_to_filename(self.outputs.deformation_field) if self.inputs.jobtype == "estimate": if isdefined(self.inputs.apply_to_files): - outputs['normalized_files'] = self.inputs.apply_to_files - outputs['normalized_image'] = fname_presuffix(self.inputs.image_to_align, + self.outputs.normalized_files = self.inputs.apply_to_files + self.outputs.normalized_image = fname_presuffix(self.inputs.image_to_align, prefix='w') elif 'write' in self.inputs.jobtype: - outputs['normalized_files'] = [] + self.outputs.normalized_files = [] if isdefined(self.inputs.apply_to_files): filelist = filename_to_list(self.inputs.apply_to_files) for f in filelist: @@ -666,12 +657,11 @@ def _list_outputs(self): run = [fname_presuffix(in_f, prefix='w') for in_f in f] else: run = [fname_presuffix(f, prefix='w')] - outputs['normalized_files'].extend(run) + self.outputs.normalized_files.extend(run) if isdefined(self.inputs.image_to_align): - outputs['normalized_image'] = fname_presuffix(self.inputs.image_to_align, + self.outputs.normalized_image = fname_presuffix(self.inputs.image_to_align, prefix='w') - return outputs class SegmentInputSpec(SPMCommandInputSpec): @@ -797,8 +787,7 @@ def _format_arg(self, opt, spec, val): return clean_masks_dict[val] return super(Segment, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): f = self.inputs.data[0] for tidx, tissue in enumerate(['gm', 'wm', 'csf']): @@ -809,17 +798,16 @@ def _list_outputs(self): ('native', '')]): if getattr(self.inputs, outtype)[idx]: outfield = '%s_%s_image' % (image, tissue) - outputs[outfield] = fname_presuffix(f, + setattr(self.outputs, outfield, fname_presuffix(f, prefix='%sc%d' % (prefix, - tidx + 1)) + tidx + 1))) if isdefined(self.inputs.save_bias_corrected) and \ self.inputs.save_bias_corrected: - outputs['bias_corrected_image'] = fname_presuffix(f, prefix='m') + self.outputs.bias_corrected_image = fname_presuffix(f, prefix='m') t_mat = fname_presuffix(f, suffix='_seg_sn.mat', use_ext=False) - outputs['transformation_mat'] = t_mat + self.outputs.transformation_mat = t_mat invt_mat = fname_presuffix(f, suffix='_seg_inv_sn.mat', use_ext=False) - outputs['inverse_transformation_mat'] = invt_mat - return outputs + self.outputs.inverse_transformation_mat = invt_mat class NewSegmentInputSpec(SPMCommandInputSpec): @@ -937,56 +925,54 @@ def _format_arg(self, opt, spec, val): else: return super(NewSegment, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['native_class_images'] = [] - outputs['dartel_input_images'] = [] - outputs['normalized_class_images'] = [] - outputs['modulated_class_images'] = [] - outputs['transformation_mat'] = [] - outputs['bias_corrected_images'] = [] - outputs['bias_field_images'] = [] - outputs['inverse_deformation_field'] = [] - outputs['forward_deformation_field'] = [] + def _post_run(self): + self.outputs.native_class_images = [] + self.outputs.dartel_input_images = [] + self.outputs.normalized_class_images = [] + self.outputs.modulated_class_images = [] + self.outputs.transformation_mat = [] + self.outputs.bias_corrected_images = [] + self.outputs.bias_field_images = [] + self.outputs.inverse_deformation_field = [] + self.outputs.forward_deformation_field = [] n_classes = 5 if isdefined(self.inputs.tissues): n_classes = len(self.inputs.tissues) for i in range(n_classes): - outputs['native_class_images'].append([]) - outputs['dartel_input_images'].append([]) - outputs['normalized_class_images'].append([]) - outputs['modulated_class_images'].append([]) + self.outputs.native_class_images.append([]) + self.outputs.dartel_input_images.append([]) + self.outputs.normalized_class_images.append([]) + self.outputs.modulated_class_images.append([]) for filename in self.inputs.channel_files: pth, base, ext = split_filename(filename) if isdefined(self.inputs.tissues): for i, tissue in enumerate(self.inputs.tissues): if tissue[2][0]: - outputs['native_class_images'][i].append(os.path.join(pth, "c%d%s.nii" % (i + 1, base))) + self.outputs.native_class_images[i].append(os.path.join(pth, "c%d%s.nii" % (i + 1, base))) if tissue[2][1]: - outputs['dartel_input_images'][i].append(os.path.join(pth, "rc%d%s.nii" % (i + 1, base))) + self.outputs.dartel_input_images[i].append(os.path.join(pth, "rc%d%s.nii" % (i + 1, base))) if tissue[3][0]: - outputs['normalized_class_images'][i].append(os.path.join(pth, "wc%d%s.nii" % (i + 1, base))) + self.outputs.normalized_class_images[i].append(os.path.join(pth, "wc%d%s.nii" % (i + 1, base))) if tissue[3][1]: - outputs['modulated_class_images'][i].append(os.path.join(pth, "mwc%d%s.nii" % (i + 1, base))) + self.outputs.modulated_class_images[i].append(os.path.join(pth, "mwc%d%s.nii" % (i + 1, base))) else: for i in range(n_classes): - outputs['native_class_images'][i].append(os.path.join(pth, "c%d%s.nii" % (i + 1, base))) - outputs['transformation_mat'].append(os.path.join(pth, "%s_seg8.mat" % base)) + self.outputs.native_class_images[i].append(os.path.join(pth, "c%d%s.nii" % (i + 1, base))) + self.outputs.transformation_mat.append(os.path.join(pth, "%s_seg8.mat" % base)) if isdefined(self.inputs.write_deformation_fields): if self.inputs.write_deformation_fields[0]: - outputs['inverse_deformation_field'].append(os.path.join(pth, "iy_%s.nii" % base)) + self.outputs.inverse_deformation_field.append(os.path.join(pth, "iy_%s.nii" % base)) if self.inputs.write_deformation_fields[1]: - outputs['forward_deformation_field'].append(os.path.join(pth, "y_%s.nii" % base)) + self.outputs.forward_deformation_field.append(os.path.join(pth, "y_%s.nii" % base)) if isdefined(self.inputs.channel_info): if self.inputs.channel_info[2][0]: - outputs['bias_corrected_images'].append(os.path.join(pth, "m%s.nii" % (base))) + self.outputs.bias_corrected_images.append(os.path.join(pth, "m%s.nii" % (base))) if self.inputs.channel_info[2][1]: - outputs['bias_field_images'].append(os.path.join(pth, "BiasField_%s.nii" % (base))) - return outputs + self.outputs.bias_field_images.append(os.path.join(pth, "BiasField_%s.nii" % (base))) class SmoothInputSpec(SPMCommandInputSpec): @@ -1042,13 +1028,11 @@ def _format_arg(self, opt, spec, val): return super(Smooth, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['smoothed_files'] = [] + def _post_run(self): + self.outputs.smoothed_files = [] for imgf in filename_to_list(self.inputs.in_files): - outputs['smoothed_files'].append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) - return outputs + self.outputs.smoothed_files.append(fname_presuffix(imgf, prefix=self.inputs.out_prefix)) class DARTELInputSpec(SPMCommandInputSpec): @@ -1142,19 +1126,17 @@ def _format_arg(self, opt, spec, val): else: return super(DARTEL, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['template_files'] = [] + def _post_run(self): + self.outputs.template_files = [] for i in range(6): - outputs['template_files'].append(os.path.realpath('%s_%d.nii' % (self.inputs.template_prefix, i + 1))) - outputs['final_template_file'] = os.path.realpath('%s_6.nii' % self.inputs.template_prefix) - outputs['dartel_flow_fields'] = [] + self.outputs.template_files.append(os.path.realpath('%s_%d.nii' % (self.inputs.template_prefix, i + 1))) + self.outputs.final_template_file = os.path.realpath('%s_6.nii' % self.inputs.template_prefix) + self.outputs.dartel_flow_fields = [] for filename in self.inputs.image_files[0]: pth, base, ext = split_filename(filename) - outputs['dartel_flow_fields'].append(os.path.realpath('u_%s_%s%s' % (base, + self.outputs.dartel_flow_fields.append(os.path.realpath('u_%s_%s%s' % (base, self.inputs.template_prefix, ext))) - return outputs class DARTELNorm2MNIInputSpec(SPMCommandInputSpec): @@ -1231,11 +1213,10 @@ def _format_arg(self, opt, spec, val): else: return super(DARTELNorm2MNI, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): pth, base, ext = split_filename(self.inputs.template_file) - outputs['normalization_parameter_file'] = os.path.realpath(base + '_2mni.mat') - outputs['normalized_files'] = [] + self.outputs.normalization_parameter_file = os.path.realpath(base + '_2mni.mat') + self.outputs.normalized_files = [] prefix = "w" if isdefined(self.inputs.modulate) and self.inputs.modulate: prefix = 'm' + prefix @@ -1243,11 +1224,10 @@ def _list_outputs(self): prefix = 's' + prefix for filename in self.inputs.apply_to_files: pth, base, ext = split_filename(filename) - outputs['normalized_files'].append(os.path.realpath('%s%s%s' % (prefix, + self.outputs.normalized_files.append(os.path.realpath('%s%s%s' % (prefix, base, ext))) - return outputs class CreateWarpedInputSpec(SPMCommandInputSpec): @@ -1306,18 +1286,16 @@ def _format_arg(self, opt, spec, val): else: return super(CreateWarped, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['warped_files'] = [] + def _post_run(self): + self.outputs.warped_files = [] for filename in self.inputs.image_files: pth, base, ext = split_filename(filename) if isdefined(self.inputs.modulate) and self.inputs.modulate: - outputs['warped_files'].append(os.path.realpath('mw%s%s' % (base, + self.outputs.warped_files.append(os.path.realpath('mw%s%s' % (base, ext))) else: - outputs['warped_files'].append(os.path.realpath('w%s%s' % (base, + self.outputs.warped_files.append(os.path.realpath('w%s%s' % (base, ext))) - return outputs class ApplyDeformationFieldInputSpec(SPMCommandInputSpec): @@ -1354,13 +1332,11 @@ def _format_arg(self, opt, spec, val): else: return super(ApplyDeformations, self)._format_arg(opt, spec, val) - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_files'] = [] + def _post_run(self): + self.outputs.out_files = [] for filename in self.inputs.in_files: _, fname = os.path.split(filename) - outputs['out_files'].append(os.path.realpath('w%s' % fname)) - return outputs + self.outputs.out_files.append(os.path.realpath('w%s' % fname)) class VBMSegmentInputSpec(SPMCommandInputSpec): @@ -1525,94 +1501,92 @@ class VBMSegment(SPMCommand): _jobtype = 'tools' _jobname = 'vbm8' - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): do_dartel = self.inputs.spatial_normalization dartel_px = '' if do_dartel: dartel_px = 'r' - outputs['native_class_images'] = [[], [], []] - outputs['dartel_input_images'] = [[], [], []] - outputs['normalized_class_images'] = [[], [], []] - outputs['modulated_class_images'] = [[], [], []] + self.outputs.native_class_images = [[], [], []] + self.outputs.dartel_input_images = [[], [], []] + self.outputs.normalized_class_images = [[], [], []] + self.outputs.modulated_class_images = [[], [], []] - outputs['transformation_mat'] = [] + self.outputs.transformation_mat = [] - outputs['bias_corrected_images'] = [] - outputs['normalized_bias_corrected_images'] = [] + self.outputs.bias_corrected_images = [] + self.outputs.normalized_bias_corrected_images = [] - outputs['inverse_deformation_field'] = [] - outputs['forward_deformation_field'] = [] - outputs['jacobian_determinant_images'] = [] + self.outputs.inverse_deformation_field = [] + self.outputs.forward_deformation_field = [] + self.outputs.jacobian_determinant_images = [] - outputs['pve_label_native_images'] = [] - outputs['pve_label_normalized_images'] = [] - outputs['pve_label_registered_images'] = [] + self.outputs.pve_label_native_images = [] + self.outputs.pve_label_normalized_images = [] + self.outputs.pve_label_registered_images = [] for filename in self.inputs.in_files: pth, base, ext = split_filename(filename) - outputs['transformation_mat'].append( + self.outputs.transformation_mat.append( os.path.join(pth, "%s_seg8.mat" % base)) for i, tis in enumerate(['gm', 'wm', 'csf']): # native space if getattr(self.inputs, '%s_native' % tis): - outputs['native_class_images'][i].append( + self.outputs.native_class_images[i].append( os.path.join(pth, "p%d%s.nii" % (i + 1, base))) if getattr(self.inputs, '%s_dartel' % tis) == 1: - outputs['dartel_input_images'][i].append( + self.outputs.dartel_input_images[i].append( os.path.join(pth, "rp%d%s.nii" % (i + 1, base))) elif getattr(self.inputs, '%s_dartel' % tis) == 2: - outputs['dartel_input_images'][i].append( + self.outputs.dartel_input_images[i].append( os.path.join(pth, "rp%d%s_affine.nii" % (i + 1, base))) # normalized space if getattr(self.inputs, '%s_normalized' % tis): - outputs['normalized_class_images'][i].append( + self.outputs.normalized_class_images[i].append( os.path.join(pth, "w%sp%d%s.nii" % (dartel_px, i + 1, base))) if getattr(self.inputs, '%s_modulated_normalized' % tis) == 1: - outputs['modulated_class_images'][i].append(os.path.join( + self.outputs.modulated_class_images[i].append(os.path.join( pth, "mw%sp%d%s.nii" % (dartel_px, i + 1, base))) elif getattr(self.inputs, '%s_modulated_normalized' % tis) == 2: - outputs['normalized_class_images'][i].append(os.path.join( + self.outputs.normalized_class_images[i].append(os.path.join( pth, "m0w%sp%d%s.nii" % (dartel_px, i + 1, base))) if self.inputs.pve_label_native: - outputs['pve_label_native_images'].append( + self.outputs.pve_label_native_images.append( os.path.join(pth, "p0%s.nii" % (base))) if self.inputs.pve_label_normalized: - outputs['pve_label_normalized_images'].append( + self.outputs.pve_label_normalized_images.append( os.path.join(pth, "w%sp0%s.nii" % (dartel_px, base))) if self.inputs.pve_label_dartel == 1: - outputs['pve_label_registered_images'].append( + self.outputs.pve_label_registered_images.append( os.path.join(pth, "rp0%s.nii" % (base))) elif self.inputs.pve_label_dartel == 2: - outputs['pve_label_registered_images'].append( + self.outputs.pve_label_registered_images.append( os.path.join(pth, "rp0%s_affine.nii" % (base))) if self.inputs.bias_corrected_native: - outputs['bias_corrected_images'].append( + self.outputs.bias_corrected_images.append( os.path.join(pth, "m%s.nii" % (base))) if self.inputs.bias_corrected_normalized: - outputs['normalized_bias_corrected_images'].append( + self.outputs.normalized_bias_corrected_images.append( os.path.join(pth, "wm%s%s.nii" % (dartel_px, base))) if self.inputs.deformation_field[0]: - outputs['forward_deformation_field'].append( + self.outputs.forward_deformation_field.append( os.path.join(pth, "y_%s%s.nii" % (dartel_px, base))) if self.inputs.deformation_field[1]: - outputs['inverse_deformation_field'].append( + self.outputs.inverse_deformation_field.append( os.path.join(pth, "iy_%s%s.nii" % (dartel_px, base))) if self.inputs.jacobian_determinant and do_dartel: - outputs['jacobian_determinant_images'].append( + self.outputs.jacobian_determinant_images.append( os.path.join(pth, "jac_wrp1%s.nii" % (base))) - return outputs def _format_arg(self, opt, spec, val): """Convert input to appropriate format for spm @@ -1629,11 +1603,11 @@ def _format_arg(self, opt, spec, val): else: return super(VBMSegment, self)._format_arg(opt, spec, val) - def _parse_inputs(self): + def parse_args(self): if self.inputs.spatial_normalization == 'low': - einputs = super(VBMSegment, self)._parse_inputs( + einputs = super(VBMSegment, self).parse_args( skip=('spatial_normalization', 'dartel_template')) einputs[0]['estwrite']['extopts']['dartelwarp'] = {'normlow': 1} return einputs else: - return super(VBMSegment, self)._parse_inputs(skip=('spatial_normalization')) + return super(VBMSegment, self).parse_args(skip=('spatial_normalization')) diff --git a/nipype/interfaces/spm/tests/test_auto_SPMCommand.py b/nipype/interfaces/spm/tests/test_auto_SPMCommand.py index 76676dd1ab..b0df440742 100644 --- a/nipype/interfaces/spm/tests/test_auto_SPMCommand.py +++ b/nipype/interfaces/spm/tests/test_auto_SPMCommand.py @@ -22,3 +22,11 @@ def test_SPMCommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_SPMCommand_outputs(): + output_map = dict() + outputs = SPMCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/spm/tests/test_base.py b/nipype/interfaces/spm/tests/test_base.py index 73e8ad9c12..ec376113a6 100644 --- a/nipype/interfaces/spm/tests/test_base.py +++ b/nipype/interfaces/spm/tests/test_base.py @@ -163,10 +163,10 @@ class TestClass(spm.SPMCommand): _jobname = 'jobname' dc = TestClass() # dc = derived_class dc.inputs.test_in = True - out = dc._make_matlab_command(dc._parse_inputs()) + out = dc._make_matlab_command(dc.parse_args()) yield assert_equal, out.find('jobs{1}.spm.jobtype.jobname.testfield = 1;') > 0, 1 dc.inputs.use_v8struct = False - out = dc._make_matlab_command(dc._parse_inputs()) + out = dc._make_matlab_command(dc.parse_args()) yield assert_equal, out.find('jobs{1}.jobtype{1}.jobname{1}.testfield = 1;') > 0, 1 diff --git a/nipype/interfaces/spm/utils.py b/nipype/interfaces/spm/utils.py index 452727fa44..7077876897 100644 --- a/nipype/interfaces/spm/utils.py +++ b/nipype/interfaces/spm/utils.py @@ -35,10 +35,8 @@ def _make_matlab_command(self, _): return script - def _list_outputs(self): - outputs = self._outputs().get() - outputs['nifti_file'] = self.output_name - return outputs + def _post_run(self): + self.outputs.nifti_file = self.output_name class CalcCoregAffineInputSpec(SPMCommandInputSpec): @@ -115,11 +113,9 @@ def _make_matlab_command(self, _): self.inputs.invmat) return script - def _list_outputs(self): - outputs = self._outputs().get() - outputs['mat'] = os.path.abspath(self.inputs.mat) - outputs['invmat'] = os.path.abspath(self.inputs.invmat) - return outputs + def _post_run(self): + self.outputs.mat = os.path.abspath(self.inputs.mat) + self.outputs.invmat = os.path.abspath(self.inputs.invmat) class ApplyTransformInputSpec(SPMCommandInputSpec): @@ -154,7 +150,7 @@ class ApplyTransform(SPMCommand): def _make_matlab_command(self, _): """checks for SPM, generates script""" outputs = self._list_outputs() - self.inputs.out_file = outputs['out_file'] + self.inputs.out_file = self.outputs.out_file script = """ infile = '%s'; outfile = '%s' @@ -174,13 +170,12 @@ def _make_matlab_command(self, _): # spm_get_space(infile, transform.M * img_space); return script - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): + if not isdefined(self.inputs.out_file): - outputs['out_file'] = os.path.abspath(self._gen_outfilename()) + self.outputs.out_file = os.path.abspath(self._gen_outfilename()) else: - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs + self.outputs.out_file = os.path.abspath(self.inputs.out_file) def _gen_outfilename(self): _, name, _ = split_filename(self.inputs.in_file) @@ -228,10 +223,8 @@ def _make_matlab_command(self, _): self.inputs.in_file) return script - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_file'] = os.path.abspath(self.inputs.out_file) - return outputs + def _post_run(self): + self.outputs.out_file = os.path.abspath(self.inputs.out_file) class ApplyInverseDeformationInput(SPMCommandInputSpec): @@ -267,6 +260,19 @@ class ApplyInverseDeformationInput(SPMCommandInputSpec): minlen=3, maxlen=3, desc='3-element list (opt)') + def _format_arg(self, opt, spec, val): + """Convert input to appropriate format for spm + """ + if opt == 'in_files': + return scans_for_fnames(filename_to_list(val)) + if opt == 'target': + return scans_for_fname(filename_to_list(val)) + if opt == 'deformation': + return np.array([list_to_filename(val)], dtype=object) + if opt == 'deformation_field': + return np.array([list_to_filename(val)], dtype=object) + return val + class ApplyInverseDeformationOutput(TraitedSpec): out_files = OutputMultiPath(File(exists=True), @@ -294,26 +300,11 @@ class ApplyInverseDeformation(SPMCommand): _jobtype = 'util' _jobname = 'defs' - def _format_arg(self, opt, spec, val): - """Convert input to appropriate format for spm - """ - if opt == 'in_files': - return scans_for_fnames(filename_to_list(val)) - if opt == 'target': - return scans_for_fname(filename_to_list(val)) - if opt == 'deformation': - return np.array([list_to_filename(val)], dtype=object) - if opt == 'deformation_field': - return np.array([list_to_filename(val)], dtype=object) - return val - - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_files'] = [] + def _post_run(self): + self.outputs.out_files = [] for filename in self.inputs.in_files: _, fname = os.path.split(filename) - outputs['out_files'].append(os.path.realpath('w%s' % fname)) - return outputs + self.outputs.out_files.append(os.path.realpath('w%s' % fname)) class ResliceToReferenceInput(SPMCommandInputSpec): @@ -377,13 +368,11 @@ def _format_arg(self, opt, spec, val): return np.array([list_to_filename(val)], dtype=object) return val - def _list_outputs(self): - outputs = self._outputs().get() - outputs['out_files'] = [] + def _post_run(self): + self.outputs.out_files = [] for filename in self.inputs.in_files: _, fname = os.path.split(filename) - outputs['out_files'].append(os.path.realpath('w%s' % fname)) - return outputs + self.outputs.out_files.append(os.path.realpath('w%s' % fname)) class DicomImportInputSpec(SPMCommandInputSpec): @@ -415,6 +404,21 @@ class DicomImportInputSpec(SPMCommandInputSpec): exactly the same file names.') + def _format_arg(self, opt, spec, val): + """Convert input to appropriate format for spm + """ + if opt == 'in_files': + return np.array(val, dtype=object) + if opt == 'output_dir': + return np.array([val], dtype=object) + if opt == 'output_dir': + return os.path.abspath(val) + if opt == 'icedims': + if val: + return 1 + return 0 + return super(DicomImportInputSpec, self)._format_arg(opt, spec, val) + class DicomImportOutputSpec(TraitedSpec): out_files = OutputMultiPath(File(exists=True), desc='converted files') @@ -438,39 +442,22 @@ class DicomImport(SPMCommand): _jobtype = 'util' _jobname = 'dicom' - def _format_arg(self, opt, spec, val): - """Convert input to appropriate format for spm - """ - if opt == 'in_files': - return np.array(val, dtype=object) - if opt == 'output_dir': - return np.array([val], dtype=object) - if opt == 'output_dir': - return os.path.abspath(val) - if opt == 'icedims': - if val: - return 1 - return 0 - return super(DicomImport, self)._format_arg(opt, spec, val) - def _run_interface(self, runtime): od = os.path.abspath(self.inputs.output_dir) if not os.path.isdir(od): os.mkdir(od) return super(DicomImport, self)._run_interface(runtime) - def _list_outputs(self): + def _post_run(self): from glob import glob - outputs = self._outputs().get() od = os.path.abspath(self.inputs.output_dir) - ext = self.inputs.format if self.inputs.output_dir_struct == "flat": - outputs['out_files'] = glob(os.path.join(od, '*.%s' % ext)) + self.outputs.out_files = glob(os.path.join(od, '*.%s' % ext)) elif self.inputs.output_dir_struct == 'series': - outputs['out_files'] = glob(os.path.join(od, os.path.join('*', '*.%s' % ext))) + self.outputs.out_files = glob(os.path.join(od, os.path.join('*', '*.%s' % ext))) elif self.inputs.output_dir_struct in ['patid', 'date_time', 'patname']: - outputs['out_files'] = glob(os.path.join(od, os.path.join('*', '*', '*.%s' % ext))) + self.outputs.out_files = glob(os.path.join(od, os.path.join('*', '*', '*.%s' % ext))) elif self.inputs.output_dir_struct == 'patid_date': - outputs['out_files'] = glob(os.path.join(od, os.path.join('*', '*', '*', '*.%s' % ext))) - return outputs + self.outputs.out_files = glob(os.path.join(od, os.path.join('*', '*', '*', '*.%s' % ext))) + diff --git a/nipype/interfaces/tests/test_auto_AssertEqual.py b/nipype/interfaces/tests/test_auto_AssertEqual.py index 4a1d763e43..ffc246f825 100644 --- a/nipype/interfaces/tests/test_auto_AssertEqual.py +++ b/nipype/interfaces/tests/test_auto_AssertEqual.py @@ -18,3 +18,11 @@ def test_AssertEqual_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_AssertEqual_outputs(): + output_map = dict() + outputs = AssertEqual.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_BaseInterface.py b/nipype/interfaces/tests/test_auto_BaseInterface.py index 5851add1da..2b5c863729 100644 --- a/nipype/interfaces/tests/test_auto_BaseInterface.py +++ b/nipype/interfaces/tests/test_auto_BaseInterface.py @@ -14,3 +14,11 @@ def test_BaseInterface_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_BaseInterface_outputs(): + output_map = dict() + outputs = BaseInterface.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_CommandLine.py b/nipype/interfaces/tests/test_auto_CommandLine.py index 9ea4f08937..1ebbd2c135 100644 --- a/nipype/interfaces/tests/test_auto_CommandLine.py +++ b/nipype/interfaces/tests/test_auto_CommandLine.py @@ -21,3 +21,11 @@ def test_CommandLine_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_CommandLine_outputs(): + output_map = dict() + outputs = CommandLine.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_Dcm2nii.py b/nipype/interfaces/tests/test_auto_Dcm2nii.py index c322f76149..5ff104d5c3 100644 --- a/nipype/interfaces/tests/test_auto_Dcm2nii.py +++ b/nipype/interfaces/tests/test_auto_Dcm2nii.py @@ -40,7 +40,7 @@ def test_Dcm2nii_inputs(): usedefault=True, ), output_dir=dict(argstr='-o %s', - genfile=True, + usedefault=True, ), protocol_in_filename=dict(argstr='-p', usedefault=True, diff --git a/nipype/interfaces/tests/test_auto_IOBase.py b/nipype/interfaces/tests/test_auto_IOBase.py index 548b613986..bbdff9f5f5 100644 --- a/nipype/interfaces/tests/test_auto_IOBase.py +++ b/nipype/interfaces/tests/test_auto_IOBase.py @@ -14,3 +14,11 @@ def test_IOBase_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_IOBase_outputs(): + output_map = dict() + outputs = IOBase.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_MatlabCommand.py b/nipype/interfaces/tests/test_auto_MatlabCommand.py index bfc24cb064..66b9d5335f 100644 --- a/nipype/interfaces/tests/test_auto_MatlabCommand.py +++ b/nipype/interfaces/tests/test_auto_MatlabCommand.py @@ -50,3 +50,11 @@ def test_MatlabCommand_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_MatlabCommand_outputs(): + output_map = dict() + outputs = MatlabCommand.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_MpiCommandLine.py b/nipype/interfaces/tests/test_auto_MpiCommandLine.py index 57d1611f4d..0137c6ccf5 100644 --- a/nipype/interfaces/tests/test_auto_MpiCommandLine.py +++ b/nipype/interfaces/tests/test_auto_MpiCommandLine.py @@ -24,3 +24,11 @@ def test_MpiCommandLine_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_MpiCommandLine_outputs(): + output_map = dict() + outputs = MpiCommandLine.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_MySQLSink.py b/nipype/interfaces/tests/test_auto_MySQLSink.py index 7b4ff10c0c..15193ac116 100644 --- a/nipype/interfaces/tests/test_auto_MySQLSink.py +++ b/nipype/interfaces/tests/test_auto_MySQLSink.py @@ -28,3 +28,11 @@ def test_MySQLSink_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_MySQLSink_outputs(): + output_map = dict() + outputs = MySQLSink.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_NiftiGeneratorBase.py b/nipype/interfaces/tests/test_auto_NiftiGeneratorBase.py index 762c862ed8..86476c3d30 100644 --- a/nipype/interfaces/tests/test_auto_NiftiGeneratorBase.py +++ b/nipype/interfaces/tests/test_auto_NiftiGeneratorBase.py @@ -14,3 +14,11 @@ def test_NiftiGeneratorBase_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_NiftiGeneratorBase_outputs(): + output_map = dict() + outputs = NiftiGeneratorBase.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_SEMLikeCommandLine.py b/nipype/interfaces/tests/test_auto_SEMLikeCommandLine.py index 8afc2cdec2..da5784fce6 100644 --- a/nipype/interfaces/tests/test_auto_SEMLikeCommandLine.py +++ b/nipype/interfaces/tests/test_auto_SEMLikeCommandLine.py @@ -21,3 +21,11 @@ def test_SEMLikeCommandLine_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_SEMLikeCommandLine_outputs(): + output_map = dict() + outputs = SEMLikeCommandLine.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_SQLiteSink.py b/nipype/interfaces/tests/test_auto_SQLiteSink.py index f215e3e424..c92ac25985 100644 --- a/nipype/interfaces/tests/test_auto_SQLiteSink.py +++ b/nipype/interfaces/tests/test_auto_SQLiteSink.py @@ -18,3 +18,11 @@ def test_SQLiteSink_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_SQLiteSink_outputs(): + output_map = dict() + outputs = SQLiteSink.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_StdOutCommandLine.py b/nipype/interfaces/tests/test_auto_StdOutCommandLine.py index 6c91c5de40..da5bbd3cc9 100644 --- a/nipype/interfaces/tests/test_auto_StdOutCommandLine.py +++ b/nipype/interfaces/tests/test_auto_StdOutCommandLine.py @@ -13,8 +13,8 @@ def test_StdOutCommandLine_inputs(): usedefault=True, ), out_file=dict(argstr='> %s', - genfile=True, position=-1, + usedefault=True, ), terminal_output=dict(nohash=True, ), @@ -25,3 +25,12 @@ def test_StdOutCommandLine_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_StdOutCommandLine_outputs(): + output_map = dict(out_file=dict(), + ) + outputs = StdOutCommandLine.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_auto_XNATSink.py b/nipype/interfaces/tests/test_auto_XNATSink.py index dd681af29f..a774d1e13d 100644 --- a/nipype/interfaces/tests/test_auto_XNATSink.py +++ b/nipype/interfaces/tests/test_auto_XNATSink.py @@ -38,3 +38,11 @@ def test_XNATSink_inputs(): for metakey, value in list(metadata.items()): yield assert_equal, getattr(inputs.traits()[key], metakey), value + +def test_XNATSink_outputs(): + output_map = dict() + outputs = XNATSink.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + yield assert_equal, getattr(outputs.traits()[key], metakey), value diff --git a/nipype/interfaces/tests/test_base.py b/nipype/interfaces/tests/test_base.py index d186f64b6b..5c18bcfcd7 100644 --- a/nipype/interfaces/tests/test_base.py +++ b/nipype/interfaces/tests/test_base.py @@ -408,12 +408,12 @@ def __init__(self): yield assert_raises, NotImplementedError, nif.run yield assert_raises, NotImplementedError, nif.aggregate_outputs yield assert_raises, NotImplementedError, nif._list_outputs - yield assert_raises, NotImplementedError, nif._get_filecopy_info + yield assert_raises, NotImplementedError, nif.inputs.get_filecopy_info def test_BaseInterface(): yield assert_equal, nib.BaseInterface.help(), None - yield assert_equal, nib.BaseInterface._get_filecopy_info(), [] + yield assert_equal, nib.BaseInterface.inputs.get_filecopy_info(), [] class InputSpec(nib.TraitedSpec): foo = nib.traits.Int(desc='a random int') @@ -432,10 +432,10 @@ class DerivedInterface(nib.BaseInterface): yield assert_equal, DerivedInterface.help(), None yield assert_true, 'moo' in ''.join(DerivedInterface._inputs_help()) yield assert_equal, DerivedInterface()._outputs(), None - yield assert_equal, DerivedInterface._get_filecopy_info()[0]['key'], 'woo' - yield assert_true, DerivedInterface._get_filecopy_info()[0]['copy'] - yield assert_equal, DerivedInterface._get_filecopy_info()[1]['key'], 'zoo' - yield assert_false, DerivedInterface._get_filecopy_info()[1]['copy'] + yield assert_equal, DerivedInterface.inputs.get_filecopy_info()[0]['key'], 'woo' + yield assert_true, DerivedInterface.inputs.get_filecopy_info()[0]['copy'] + yield assert_equal, DerivedInterface.inputs.get_filecopy_info()[1]['key'], 'zoo' + yield assert_false, DerivedInterface.inputs.get_filecopy_info()[1]['copy'] yield assert_equal, DerivedInterface().inputs.foo, Undefined yield assert_raises, ValueError, DerivedInterface()._check_mandatory_inputs yield assert_equal, DerivedInterface(goo=1)._check_mandatory_inputs(), None @@ -568,7 +568,7 @@ class DerivedInterface1(nib.BaseInterface): def _run_interface(self, runtime): return runtime - def _list_outputs(self): + def _post_run(self): return {'foo': 1} obj = DerivedInterface1() yield assert_raises, KeyError, obj.run @@ -605,20 +605,20 @@ class CommandLineInputSpec1(nib.CommandLineInputSpec): ci4.inputs.noo = 0 ci4.inputs.roo = 'hello' ci4.inputs.soo = False - cmd = ci4._parse_inputs() + cmd = ci4.parse_args() yield assert_equal, cmd[0], '-g' yield assert_equal, cmd[-1], '-i 1 -i 2 -i 3' yield assert_true, 'hello' not in ' '.join(cmd) yield assert_true, '-soo' not in ' '.join(cmd) ci4.inputs.soo = True - cmd = ci4._parse_inputs() + cmd = ci4.parse_args() yield assert_true, '-soo' in ' '.join(cmd) class CommandLineInputSpec2(nib.CommandLineInputSpec): foo = nib.File(argstr='%s', desc='a str', genfile=True) nib.CommandLine.input_spec = CommandLineInputSpec2 ci5 = nib.CommandLine(command='cmd') - yield assert_raises, NotImplementedError, ci5._parse_inputs + yield assert_raises, NotImplementedError, ci5.parse_args class DerivedClass(nib.CommandLine): input_spec = CommandLineInputSpec2 @@ -627,7 +627,7 @@ def _gen_filename(self, name): return 'filename' ci6 = DerivedClass(command='cmd') - yield assert_equal, ci6._parse_inputs()[0], 'filename' + yield assert_equal, ci6.parse_args()[0], 'filename' nib.CommandLine.input_spec = nib.CommandLineInputSpec diff --git a/nipype/interfaces/tests/test_io.py b/nipype/interfaces/tests/test_io.py index c1f4ec35f5..0455b1b47f 100644 --- a/nipype/interfaces/tests/test_io.py +++ b/nipype/interfaces/tests/test_io.py @@ -187,9 +187,9 @@ def test_datasink(): # Make dummy input file def _make_dummy_input(): - ''' + """ Function to create a dummy file - ''' + """ # Import packages import tempfile @@ -210,10 +210,10 @@ def _make_dummy_input(): # Test datasink writes to s3 properly @skipif(noboto3 or not fakes3) def test_datasink_to_s3(): - ''' + """ This function tests to see if the S3 functionality of a DataSink works properly - ''' + """ # Import packages import hashlib @@ -272,10 +272,10 @@ def test_datasink_to_s3(): # Test AWS creds read from env vars @skipif(noboto3 or not fakes3) def test_aws_keys_from_env(): - ''' + """ Function to ensure the DataSink can successfully read in AWS credentials from the environment variables - ''' + """ # Import packages import os @@ -300,10 +300,10 @@ def test_aws_keys_from_env(): # Test the local copy attribute def test_datasink_localcopy(): - ''' + """ Function to validate DataSink will make local copy via local_copy attribute - ''' + """ # Import packages import hashlib diff --git a/nipype/interfaces/traits_extension.py b/nipype/interfaces/traits_extension.py index 49af1db164..049af41cb6 100644 --- a/nipype/interfaces/traits_extension.py +++ b/nipype/interfaces/traits_extension.py @@ -16,7 +16,11 @@ """ import os +import re +import itertools as itools +from ast import literal_eval +from ..external.six import string_types # perform all external trait imports here import traits if traits.__version__ < '3.7.0': @@ -26,6 +30,10 @@ from traits.trait_errors import TraitError from traits.trait_base import _Undefined +from ..utils.filemanip import split_filename + +from .. import logging +IFLOGGER = logging.getLogger('interface') class BaseFile (traits.BaseStr): """ Defines a trait whose value must be the name of a file. @@ -114,6 +122,448 @@ def __init__(self, value='', filter=None, auto_set=False, super(File, self).__init__(value, filter, auto_set, entries, exists, **metadata) + +class GenFile(File): + """ A file which default name is automatically generated from other + traits. + + >>> # The traits start undefined + >>> from nipype.interfaces.base import GenFile, Undefined + >>> class A(TraitedSpec): + ... src = File(exists=False) + ... foo = GenFile(template='{src}_foo') + >>> a = A() + >>> a.src + + >>> a.foo + + + >>> # If the source trait is set, foo can be sourced ... + >>> a.src = '/software/temp/src.txt' + >>> a.foo + 'src_foo.txt' + + >>> # ... and updates with the update of src ... + >>> a.src = '/software/temp/foo.txt' + >>> a.foo + 'foo_foo.txt' + + >>> # ... util it is explicitly set. + >>> a.foo = '/software/temp/goo.txt' + >>> a.foo + '/software/temp/goo.txt' + + >>> # Setting it Undefined will restore the sourcing behavior + >>> a.foo = Undefined + >>> a.foo + 'foo_foo.txt' + + """ + + def __init__(self, template=None, keep_extension=False, value='', + filter=None, auto_set=False, entries=0, exists=False, **metadata): + """ Creates a GenFile trait. """ + + if template is None or not isinstance(template, string_types): + raise TraitError('GenFile requires a valid template argument') + + self.name_source = list(_parse_name_source(template)) + # Remove range indexing tokens (not allowed by string.Formatter) + for _, itoken, _ in self.name_source: + if itoken: + template = template.replace(itoken, '') + + self.template = template + self.keep_ext = keep_extension + super(GenFile, self).__init__(value, filter, auto_set, entries, exists, + **metadata) + + + def validate(self, object, name, value): + """ Validates that a specified value is valid for this trait. + + Note: The 'fast validator' version performs this check in C. + """ + # Allow unsetting the input + if not isdefined(value): + return value + + validated_value = super(GenFile, self).validate(object, name, value) + if not self.exists: + return validated_value + elif os.path.isfile(value): + return validated_value + + self.error(object, name, value) + + def get(self, obj, name): + # Compute expected name iff trait is not set + template = self.template + if self.value is None: + srcvals = {} + ext = '' + final_nsrcs = [] + for nsrc_list, indexing, fstr in self.name_source: + for nel in nsrc_list: + srcvalue = getattr(obj, nel) + if isdefined(srcvalue): + nsrc = nel + break + + if not isdefined(srcvalue): + return Undefined + + template = template.replace('|'.join(nsrc_list), nsrc) + IFLOGGER.debug('replacing %s with %s. Result=%s', '|'.join(nsrc_list), nsrc, template) + final_nsrcs.append(nsrc) + + if isinstance(srcvalue, string_types): + vallist = [srcvalue] + else: + vallist = list(srcvalue) + + outvals = [] + + isfile = obj.trait(nsrc).is_trait_type(( + File, MultiPath, GenMultiFile)) + for val in vallist: + if isfile: + _, val, ext = split_filename(val) + elif indexing: + # eval should be safe since we only + # accept indexing elements with format [n:n] + val = eval('val%s' % indexing) # pylint: disable=W0123 + if isdefined(val): + outvals.append(val) + + if not outvals: + continue + + if isinstance(srcvalue, string_types): + srcvals.update({nsrc: outvals[0]}) + elif isinstance(srcvalue, tuple): + srcvals.update({nsrc: tuple(outvals)}) + else: + srcvals.update({nsrc: outvals}) + + # Check that no source is missing + IFLOGGER.debug('Final sources: %s and values %s', final_nsrcs, srcvals) + missing = list(set(final_nsrcs) - set(srcvals.keys())) + if not missing: + retval = template.format(**srcvals) + if self.keep_ext: + retval += ext + return retval + else: + return Undefined + return self.get_value(obj, name) + + def set(self, obj, name, value): + self.set_value(obj, name, value) + + +class MultiPath(traits.List): + """ Abstract class - shared functionality of input and output MultiPath + """ + + def validate(self, obj, name, value): + if not isdefined(value) or \ + (isinstance(value, list) and len(value) == 0): + return Undefined + newvalue = value + + if not isinstance(value, list) \ + or (self.inner_traits() and + isinstance(self.inner_traits()[0].trait_type, + traits.List) and not + isinstance(self.inner_traits()[0].trait_type, + InputMultiPath) and + isinstance(value, list) and + value and not + isinstance(value[0], list)): + newvalue = [value] + value = super(MultiPath, self).validate(obj, name, newvalue) + + if len(value) > 0: + return value + + self.error(obj, name, value) + + +class GenMultiFile(traits.List): + """ Traits to generate lists of files. + + >>> # The traits start undefined + >>> from nipype.interfaces.base import GenFile, Undefined, traits + >>> class A(TraitedSpec): + ... src = InputMultiPath(File(exists=False)) + ... foo = GenMultiFile(template='{src}_foo') + >>> a = A() + >>> a.src + + >>> a.foo + + + >>> # If the source trait is set, foo can be sourced ... + >>> a.src = ['/software/temp/src1.txt', '/software/temp/src2.txt'] + >>> a.foo + ['src1_foo.txt', 'src2_foo.txt'] + + >>> # ... and updates with the update of src ... + >>> a.src = ['/software/temp/foo1.txt', '/software/temp/foo2.txt'] + >>> a.foo + ['foo1_foo.txt', 'foo2_foo.txt'] + + >>> # ... util it is explicitly set. + >>> a.foo = ['/software/temp/goo1.txt', '/software/temp/goo2.txt'] + >>> a.foo + ['/software/temp/goo1.txt', '/software/temp/goo2.txt'] + + >>> # Setting it Undefined will restore the sourcing behavior + >>> a.foo = Undefined + >>> a.foo + ['foo1_foo.txt', 'foo2_foo.txt'] + + >>> # It works with several replacements and defining ranges + >>> class B(TraitedSpec): + ... src = File(exists=False) + ... num = traits.Int() + ... foo = GenMultiFile(template='{src}_foo_{num:03d}', range_source='num') + >>> a.src = '/software/temp/source.txt' + >>> a.num = 3 + >>> a.foo + ['source_foo_000.txt', 'source_foo_001.txt', 'source_foo_002.txt'] + + >>> # And altogether with InputMultiPaths + >>> class B(TraitedSpec): + ... src = InputMultiPath(File(exists=False)) + ... num = traits.Int() + ... foo = GenMultiFile(template='{src}_foo_{num:03d}', range_source='num') + >>> a.src = ['/software/temp/source.txt', '/software/temp/alt.txt'] + >>> a.num = 2 + >>> a.foo + ['source_foo_000.txt', 'alt_foo_000.txt', 'source_foo_001.txt', 'alt_foo_001.txt'] + + + """ + def __init__(self, template=None, keep_extension=False, range_source=None, **metadata): + if template is None or not isinstance(template, string_types): + raise TraitError('GenMultiFile requires a valid template argument') + + self.name_source = list(_parse_name_source(template)) + # Remove range indexing tokens (not allowed by string.Formatter) + for _, itoken, _ in self.name_source: + if itoken: + template = template.replace(itoken, '') + self.template = template + self.keep_ext = keep_extension + self.range_source = None + if range_source is not None: + if not isinstance(range_source, string_types): + raise TraitError( + 'range_source is not valid (found %s).' % range_source) + + try: + range_source, offset = range_source.split('+') + self.offset = int(offset) + except ValueError: + self.offset = 0 + + if range_source not in [n for nsrc in self.name_source for n in nsrc[0]]: + raise TraitError( + 'range_source field should also be found in the' + ' template (valid fields = %s).' % self.name_source) + self.range_source = range_source + + super(GenMultiFile, self).__init__(**metadata) + + def validate(self, obj, name, value): + if not isdefined(value) or \ + (isinstance(value, list) and len(value) == 0): + return Undefined + newvalue = value + + if not isinstance(value, list) \ + or (self.inner_traits() and + isinstance(self.inner_traits()[0].trait_type, + traits.List) and not + isinstance(self.inner_traits()[0].trait_type, + InputMultiPath) and + isinstance(value, list) and + value and not + isinstance(value[0], list)): + newvalue = [value] + value = super(GenMultiFile, self).validate(obj, name, newvalue) + + if len(value) > 0: + return value + + self.error(obj, name, value) + + def get(self, obj, name): + # Compute expected name iff trait is not set + value = self.get_value(obj, name) + template = self.template + if not isdefined(value) or not value: + srcvals = {} + ext = '' + + final_nsrcs = [] + for nsrc_list, indexing, fstr in self.name_source: + for nel in nsrc_list: + srcvalue = getattr(obj, nel) + if isdefined(srcvalue): + nsrc = nel + break + + if not isdefined(srcvalue): + return Undefined + + template = template.replace('|'.join(nsrc_list), nsrc) + IFLOGGER.debug('replacing %s with %s. Result=%s', '|'.join(nsrc_list), nsrc, template) + final_nsrcs.append(nsrc) + + IFLOGGER.debug('Autogenerating output for: %s (%s=%s)', name, nsrc, srcvalue) + IFLOGGER.debug('range_source=%s', self.range_source) + if self.range_source is not None and nsrc == self.range_source: + srcvalue = range(self.offset, int(srcvalue) + self.offset) + vallist = srcvalue + IFLOGGER.debug('Generating range of outputs: %s', vallist) + + if isinstance(srcvalue, string_types): + vallist = [srcvalue] + else: + vallist = list(srcvalue) + + outvals = [] + + isfile = obj.trait(nsrc).is_trait_type(( + File, MultiPath, GenMultiFile)) + for val in vallist: + if isfile: + _, val, ext = split_filename(val) + + if isdefined(val): + outvals.append(val) + + if outvals: + srcvals.update({nsrc: outvals}) + + IFLOGGER.debug('Final sources: %s and values %s', final_nsrcs, srcvals) + # Check that no source is missing + missing = list(set(final_nsrcs) - set(srcvals.keys())) + if not missing: + results = [] + combs = list(itools.product(*tuple(srcvals[k] for k in final_nsrcs))) + + # Get the formatting dictionaries ready + dlist = [{final_nsrcs[i]: v for i, v in enumerate(kvalues)} + for kvalues in combs] + # ... and create a formatted entry for each of them + for fmtdict in dlist: + retval = template.format(**fmtdict) + if self.keep_ext: + retval += ext + results.append(retval) + + if results: + if len(results) == 1: + return results[0] + return results + + return Undefined + + if len(value) == 0: + return Undefined + elif len(value) == 1: + return value[0] + else: + return value + + def set(self, obj, name, value): + self.set_value(obj, name, value) + + +class OutputMultiPath(MultiPath): + """ Implements a user friendly traits that accepts one or more + paths to files or directories. This is the output version which + return a single string whenever possible (when it was set to a + single value or a list of length 1). Default value of this trait + is _Undefined. It does not accept empty lists. + + XXX This should only be used as a final resort. We should stick to + established Traits to the extent possible. + + XXX This needs to be vetted by somebody who understands traits + + >>> from nipype.interfaces.base import OutputMultiPath + >>> class A(TraitedSpec): + ... foo = OutputMultiPath(File(exists=False)) + >>> a = A() + >>> a.foo + + + >>> a.foo = '/software/temp/foo.txt' + >>> a.foo + '/software/temp/foo.txt' + + >>> a.foo = ['/software/temp/foo.txt'] + >>> a.foo + '/software/temp/foo.txt' + + >>> a.foo = ['/software/temp/foo.txt', '/software/temp/goo.txt'] + >>> a.foo + ['/software/temp/foo.txt', '/software/temp/goo.txt'] + + """ + + def get(self, obj, name): + value = self.get_value(obj, name) + if len(value) == 0: + return Undefined + elif len(value) == 1: + return value[0] + else: + return value + + def set(self, obj, name, value): + self.set_value(obj, name, value) + + +class InputMultiPath(MultiPath): + """ Implements a user friendly traits that accepts one or more + paths to files or directories. This is the input version which + always returns a list. Default value of this trait + is _Undefined. It does not accept empty lists. + + XXX This should only be used as a final resort. We should stick to + established Traits to the extent possible. + + XXX This needs to be vetted by somebody who understands traits + + >>> from nipype.interfaces.base import InputMultiPath + >>> class A(TraitedSpec): + ... foo = InputMultiPath(File(exists=False)) + >>> a = A() + >>> a.foo + + + >>> a.foo = '/software/temp/foo.txt' + >>> a.foo + ['/software/temp/foo.txt'] + + >>> a.foo = ['/software/temp/foo.txt'] + >>> a.foo + ['/software/temp/foo.txt'] + + >>> a.foo = ['/software/temp/foo.txt', '/software/temp/goo.txt'] + >>> a.foo + ['/software/temp/foo.txt', '/software/temp/goo.txt'] + + """ + pass + + # ------------------------------------------------------------------------------- # 'BaseDirectory' and 'Directory' traits: # ------------------------------------------------------------------------------- @@ -235,9 +685,9 @@ def isdefined(object): def has_metadata(trait, metadata, value=None, recursive=True): - ''' + """ Checks if a given trait has a metadata (and optionally if it is set to particular value) - ''' + """ count = 0 if hasattr(trait, "_metadata") and metadata in list(trait._metadata.keys()) and (trait._metadata[metadata] == value or value is None): count += 1 @@ -250,3 +700,18 @@ def has_metadata(trait, metadata, value=None, recursive=True): count += has_metadata(handler, metadata, recursive) return count > 0 + +def _parse_name_source(name_source): + """Parse template strings""" + format_str = [i[1:-1] for i in re.findall(r'\{.*?\}', name_source)] + + for fchunk in format_str: + indexing = [i for i in re.findall(r'\[[0-9]*:[0-9]*\]', fchunk)] + # Only one complex indexing replacement is allowed + if indexing: + indexing = indexing[0] + + name = fchunk.split('.')[0].split('!')[0].split(':')[0].split('[')[0] + yield (name.split('|'), indexing, fchunk) + + diff --git a/nipype/interfaces/utility.py b/nipype/interfaces/utility.py index 37883d4e5c..5cd117fccf 100644 --- a/nipype/interfaces/utility.py +++ b/nipype/interfaces/utility.py @@ -21,9 +21,9 @@ import numpy as np import nibabel as nb -from .base import (traits, TraitedSpec, DynamicTraitedSpec, File, - Undefined, isdefined, OutputMultiPath, - InputMultiPath, BaseInterface, BaseInterfaceInputSpec) +from .base import (traits, Undefined, File, isdefined, InputMultiPath, + OutputMultiPath, TraitedSpec, DynamicTraitedSpec, + BaseInterfaceInputSpec, BaseInterface) from .io import IOBase, add_traits from ..external.six import string_types from ..testing import assert_equal @@ -83,7 +83,7 @@ def _add_output_traits(self, base): base.trait_set(trait_change_notify=False, **undefined_traits) return base - def _list_outputs(self): + def _post_run(self): # manual mandatory inputs check if self._fields and self._mandatory_inputs: for key in self._fields: @@ -94,12 +94,10 @@ def _list_outputs(self): (self.__class__.__name__, key) raise ValueError(msg) - outputs = self._outputs().get() for key in self._fields: val = getattr(self.inputs, key) if isdefined(val): - outputs[key] = val - return outputs + setattr(self.outputs, key, val) class MergeInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): @@ -136,8 +134,7 @@ def __init__(self, numinputs=0, **inputs): self._numinputs = numinputs add_traits(self.inputs, ['in%d' % (i + 1) for i in range(numinputs)]) - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): out = [] if self.inputs.axis == 'vstack': for idx in range(self._numinputs): @@ -153,8 +150,7 @@ def _list_outputs(self): for j in range(self._numinputs): out[i].append(filename_to_list(getattr(self.inputs, 'in%d' % (j + 1)))[i]) if out: - outputs['out'] = out - return outputs + self.outputs.out = out class RenameInputSpec(DynamicTraitedSpec): @@ -257,10 +253,8 @@ def _run_interface(self, runtime): self._rename())) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = os.path.join(os.getcwd(), self._rename()) - return outputs + def _post_run(self): + self.outputs.out_file = os.path.join(os.getcwd(), self._rename()) class SplitInputSpec(BaseInterfaceInputSpec): @@ -299,8 +293,7 @@ def _add_output_traits(self, base): base.trait_set(trait_change_notify=False, **undefined_traits) return base - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): if isdefined(self.inputs.splits): if sum(self.inputs.splits) != len(self.inputs.inlist): raise RuntimeError('sum of splits != num of list elements') @@ -311,8 +304,7 @@ def _list_outputs(self): val = np.array(self.inputs.inlist)[splits[i]:splits[i + 1]].tolist() if self.inputs.squeeze and len(val) == 1: val = val[0] - outputs['out%d' % (i + 1)] = val - return outputs + setattr(self.outputs, 'out%d' % (i + 1), val) class SelectInputSpec(BaseInterfaceInputSpec): @@ -349,12 +341,8 @@ class Select(IOBase): input_spec = SelectInputSpec output_spec = SelectOutputSpec - def _list_outputs(self): - outputs = self._outputs().get() - out = np.array(self.inputs.inlist)[np.array(self.inputs.index)].tolist() - outputs['out'] = out - return outputs - + def _post_run(self): + self.outputs.out = np.array(self.inputs.inlist)[np.array(self.inputs.index)].tolist() class FunctionInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): function_str = traits.Str(mandatory=True, desc='code for function') @@ -463,11 +451,9 @@ def _run_interface(self, runtime): return runtime - def _list_outputs(self): - outputs = self._outputs().get() + def _post_run(self): for key in self._output_names: - outputs[key] = self._out[key] - return outputs + setattr(self.outputs, key, self._out[key]) class AssertEqualInputSpec(BaseInterfaceInputSpec): @@ -527,7 +513,6 @@ class CSVReader(BaseInterface): def _append_entry(self, outputs, entry): for key, value in zip(self._outfields, entry): outputs[key].append(value) - return outputs def _parse_line(self, line): line = line.replace('\n', '') @@ -553,11 +538,10 @@ def _outputs(self): def _add_output_traits(self, base): return add_traits(base, self._get_outfields()) - def _list_outputs(self): - outputs = self.output_spec().get() + def _post_run(self): isHeader = True for key in self._outfields: - outputs[key] = [] # initialize outfields + setattr(self.outputs, key, []) # initialize outfields with open(self.inputs.in_file, 'r') as fid: for line in fid.readlines(): if self.inputs.header and isHeader: # skip header line @@ -565,4 +549,3 @@ def _list_outputs(self): continue entry = self._parse_line(line) outputs = self._append_entry(outputs, entry) - return outputs diff --git a/nipype/pipeline/engine/base.py b/nipype/pipeline/engine/base.py index 148db2f271..5491573399 100644 --- a/nipype/pipeline/engine/base.py +++ b/nipype/pipeline/engine/base.py @@ -27,8 +27,7 @@ from copy import deepcopy import re import numpy as np -from ...interfaces.traits_extension import traits, Undefined -from ...interfaces.base import DynamicTraitedSpec +from ...interfaces.specs import DynamicTraitedSpec from ...utils.filemanip import loadpkl, savepkl from ... import logging diff --git a/nipype/pipeline/engine/nodes.py b/nipype/pipeline/engine/nodes.py index 9f9165e3b2..e06ea9b92c 100644 --- a/nipype/pipeline/engine/nodes.py +++ b/nipype/pipeline/engine/nodes.py @@ -47,30 +47,27 @@ import numpy as np import networkx as nx +from ...external.six import string_types from ...utils.misc import package_check, str2bool -package_check('networkx', '1.3') - from ... import config, logging -logger = logging.getLogger('workflow') -from ...interfaces.base import (traits, InputMultiPath, CommandLine, - Undefined, TraitedSpec, DynamicTraitedSpec, - Bunch, InterfaceResult, md5, Interface, - TraitDictObject, TraitListObject, isdefined) -from ...utils.misc import (getsource, create_function_from_source, - flatten, unflatten) -from ...utils.filemanip import (save_json, FileNotFoundError, - filename_to_list, list_to_filename, - copyfiles, fnames_presuffix, loadpkl, - split_filename, load_json, savepkl, - write_rst_header, write_rst_dict, - write_rst_list) -from ...external.six import string_types -from .utils import (generate_expanded_graph, modify_paths, - export_graph, make_output_dir, write_workflow_prov, + +from ...utils.misc import flatten, unflatten +from ...utils.filemanip import md5, save_json, FileNotFoundError, filename_to_list, \ + list_to_filename, copyfiles, fnames_presuffix, loadpkl, split_filename, load_json, \ + savepkl, write_rst_header, write_rst_dict, write_rst_list + +from ...interfaces.base import (traits, Undefined, isdefined, + InputMultiPath, DynamicTraitedSpec, + CommandLine, Bunch, InterfaceResult, Interface) + +from .utils import (modify_paths, make_output_dir, write_workflow_prov, clean_working_directory, format_dot, topological_sort, get_print_name, merge_dict, evaluate_connect_function) from .base import EngineBase +package_check('networkx', '1.3') +logger = logging.getLogger('workflow') + class Node(EngineBase): """Wraps interface objects for use in pipeline @@ -210,7 +207,7 @@ def inputs(self): @property def outputs(self): """Return the output fields of the underlying interface""" - return self._interface._outputs() + return self._interface.outputs def output_dir(self): """Return the location of the output directory for the node""" @@ -661,14 +658,14 @@ def _strip_temp(self, files, wd): def _copyfiles_to_wd(self, outdir, execute, linksonly=False): """ copy files over and change the inputs""" - if hasattr(self._interface, '_get_filecopy_info'): + if hasattr(self.inputs, 'get_filecopy_info'): logger.debug('copying files to wd [execute=%s, linksonly=%s]' % (str(execute), str(linksonly))) if execute and linksonly: olddir = outdir outdir = op.join(outdir, '_tempinput') os.makedirs(outdir) - for info in self._interface._get_filecopy_info(): + for info in self.inputs.get_filecopy_info(): files = self.inputs.get().get(info['key']) if not isdefined(files): continue diff --git a/nipype/pipeline/engine/tests/test_engine.py b/nipype/pipeline/engine/tests/test_engine.py index 5eaaa81fbf..c7e6392374 100644 --- a/nipype/pipeline/engine/tests/test_engine.py +++ b/nipype/pipeline/engine/tests/test_engine.py @@ -17,11 +17,10 @@ from ....interfaces import base as nib -class InputSpec(nib.TraitedSpec): +class InputSpec(nib.BaseInterfaceInputSpec): input1 = nib.traits.Int(desc='a random int') input2 = nib.traits.Int(desc='a random int') - class OutputSpec(nib.TraitedSpec): output1 = nib.traits.List(nib.traits.Int, desc='outputs') @@ -32,14 +31,9 @@ class TestInterface(nib.BaseInterface): def _run_interface(self, runtime): runtime.returncode = 0 + self.outputs.output1 = [1, self.inputs.input1] return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - - def test_init(): yield assert_raises, Exception, pe.Workflow pipe = pe.Workflow(name='pipe') @@ -391,7 +385,7 @@ def test_doubleconnect(): yield assert_raises, Exception, x -''' +""" Test for order of iterables import nipype.pipeline.engine as pe @@ -421,19 +415,19 @@ def test_doubleconnect(): wf1.run(inseries=True, createdirsonly=True) wf1.write_graph(graph2use='exec') -''' +""" -''' +""" import nipype.pipeline.engine as pe import nipype.interfaces.spm as spm import os from nipype.external.six import StringIO from nipype.utils.config import config -config.readfp(StringIO(""" +config.readfp(StringIO(''' [execution] remove_unnecessary_outputs = true -""")) +''')) segment = pe.Node(interface=spm.Segment(), name="segment") @@ -459,7 +453,7 @@ def test_doubleconnect(): workflow.run() workflow.run() -''' +""" # Node diff --git a/nipype/pipeline/engine/tests/test_join.py b/nipype/pipeline/engine/tests/test_join.py index b0882de91e..2933406144 100644 --- a/nipype/pipeline/engine/tests/test_join.py +++ b/nipype/pipeline/engine/tests/test_join.py @@ -31,10 +31,8 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = self.inputs.in_files[0] - return outputs + def _post_run(self): + self.outputs.output1 = self.inputs.in_files[0] class IncrementInputSpec(nib.TraitedSpec): @@ -54,10 +52,8 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = self.inputs.input1 + self.inputs.inc - return outputs + def _post_run(self): + self.outputs.output1 = self.inputs.input1 + self.inputs.inc _sums = [] @@ -81,15 +77,13 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): + def _post_run(self): global _sum global _sum_operands - outputs = self._outputs().get() - outputs['operands'] = self.inputs.input1 - _sum_operands.append(outputs['operands']) - outputs['output1'] = sum(self.inputs.input1) - _sums.append(outputs['output1']) - return outputs + self.outputs.operands = self.inputs.input1 + _sum_operands.append(self.outputs.operands) + self.outputs.output1 = sum(self.inputs.input1) + _sums.append(self.outputs.output1) _set_len = None @@ -112,11 +106,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): + def _post_run(self): global _set_len - outputs = self._outputs().get() - _set_len = outputs['output1'] = len(self.inputs.input1) - return outputs + _set_len = self.outputs.output1 = len(self.inputs.input1) _products = [] @@ -140,12 +132,10 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): + def _post_run(self): global _products - outputs = self._outputs().get() - outputs['output1'] = self.inputs.input1 * self.inputs.input2 - _products.append(outputs['output1']) - return outputs + self.outputs.output1 = self.inputs.input1 * self.inputs.input2 + _products.append(self.outputs.output1) def test_join_expansion(): diff --git a/nipype/pipeline/engine/tests/test_utils.py b/nipype/pipeline/engine/tests/test_utils.py index 8420f587c2..8865249b39 100644 --- a/nipype/pipeline/engine/tests/test_utils.py +++ b/nipype/pipeline/engine/tests/test_utils.py @@ -148,11 +148,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1] + def test_inputs_removal(): out_dir = mkdtemp() diff --git a/nipype/pipeline/engine/utils.py b/nipype/pipeline/engine/utils.py index 440400c8e0..b60e46db8a 100644 --- a/nipype/pipeline/engine/utils.py +++ b/nipype/pipeline/engine/utils.py @@ -36,8 +36,8 @@ from ...utils.filemanip import (fname_presuffix, FileNotFoundError, filename_to_list, get_related_files) from ...utils.misc import create_function_from_source, str2bool -from ...interfaces.base import (CommandLine, isdefined, Undefined, - InterfaceResult) +from ...interfaces.traits_extension import isdefined, Undefined +from ...interfaces.base import CommandLine, InterfaceResult from ...interfaces.utility import IdentityInterface from ...utils.provenance import ProvStore, pm, nipype_ns, get_id @@ -1159,8 +1159,7 @@ def clean_working_directory(outputs, cwd, inputs, needed_outputs, config, for key in outputs.copyable_trait_names(): if key not in outputs_to_keep: setattr(outputs, key, Undefined) - return outputs - + def merge_dict(d1, d2, merge=lambda x, y: y): """ diff --git a/nipype/pipeline/engine/workflows.py b/nipype/pipeline/engine/workflows.py index b14d73a307..26fd3ae883 100644 --- a/nipype/pipeline/engine/workflows.py +++ b/nipype/pipeline/engine/workflows.py @@ -48,31 +48,22 @@ import networkx as nx from ...utils.misc import package_check, str2bool -package_check('networkx', '1.3') - from ... import config, logging -logger = logging.getLogger('workflow') -from ...interfaces.base import (traits, InputMultiPath, CommandLine, - Undefined, TraitedSpec, DynamicTraitedSpec, - Bunch, InterfaceResult, md5, Interface, - TraitDictObject, TraitListObject, isdefined) + +from ...interfaces.specs import TraitedSpec +from ...interfaces.traits_extension import traits, TraitListObject, TraitDictObject from ...utils.misc import (getsource, create_function_from_source, flatten, unflatten) -from ...utils.filemanip import (save_json, FileNotFoundError, - filename_to_list, list_to_filename, - copyfiles, fnames_presuffix, loadpkl, - split_filename, load_json, savepkl, - write_rst_header, write_rst_dict, - write_rst_list) +from ...utils.filemanip import save_json from ...external.six import string_types -from .utils import (generate_expanded_graph, modify_paths, - export_graph, make_output_dir, write_workflow_prov, - clean_working_directory, format_dot, topological_sort, - get_print_name, merge_dict, evaluate_connect_function, - _write_inputs, format_node) - +from .utils import (generate_expanded_graph, export_graph, make_output_dir, + write_workflow_prov, format_dot, topological_sort, + get_print_name, merge_dict, format_node) from .base import EngineBase -from .nodes import Node, MapNode +from .nodes import MapNode + +logger = logging.getLogger('workflow') +package_check('networkx', '1.3') class Workflow(EngineBase): diff --git a/nipype/pipeline/plugins/slurm.py b/nipype/pipeline/plugins/slurm.py index 4b22f853bd..99c6366372 100644 --- a/nipype/pipeline/plugins/slurm.py +++ b/nipype/pipeline/plugins/slurm.py @@ -1,10 +1,10 @@ -''' +""" Created on Aug 2, 2013 @author: chadcumba Parallel workflow execution with SLURM -''' +""" import os import re @@ -17,7 +17,7 @@ class SLURMPlugin(SGELikeBatchManagerBase): - ''' + """ Execute using SLURM The plugin_args input to run can be used to control the SLURM execution. @@ -28,7 +28,7 @@ class SLURMPlugin(SGELikeBatchManagerBase): - sbatch_args: arguments to pass prepend to the sbatch call - ''' + """ def __init__(self, **kwargs): diff --git a/nipype/pipeline/plugins/tests/test_base.py b/nipype/pipeline/plugins/tests/test_base.py index 243ae195c2..6801d5bb01 100644 --- a/nipype/pipeline/plugins/tests/test_base.py +++ b/nipype/pipeline/plugins/tests/test_base.py @@ -16,7 +16,7 @@ def test_scipy_sparse(): goo[goo.nonzero()] = 0 yield assert_equal, foo[0, 1], 0 -''' +""" Can use the following code to test that a mapnode crash continues successfully Need to put this into a nose-test with a timeout @@ -39,4 +39,4 @@ def func(arg1): wf.base_dir = '/tmp' wf.run(plugin='MultiProc') -''' +""" diff --git a/nipype/pipeline/plugins/tests/test_debug.py b/nipype/pipeline/plugins/tests/test_debug.py index f15fc62939..4a8484cbd1 100644 --- a/nipype/pipeline/plugins/tests/test_debug.py +++ b/nipype/pipeline/plugins/tests/test_debug.py @@ -24,11 +24,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + def callme(node, graph): pass diff --git a/nipype/pipeline/plugins/tests/test_linear.py b/nipype/pipeline/plugins/tests/test_linear.py index a59c7c1981..1b049771e3 100644 --- a/nipype/pipeline/plugins/tests/test_linear.py +++ b/nipype/pipeline/plugins/tests/test_linear.py @@ -24,11 +24,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + def test_run_in_series(): cur_dir = os.getcwd() diff --git a/nipype/pipeline/plugins/tests/test_multiproc.py b/nipype/pipeline/plugins/tests/test_multiproc.py index efa9ec4161..9cba7d1efe 100644 --- a/nipype/pipeline/plugins/tests/test_multiproc.py +++ b/nipype/pipeline/plugins/tests/test_multiproc.py @@ -24,11 +24,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + def test_run_multiproc(): cur_dir = os.getcwd() diff --git a/nipype/pipeline/plugins/tests/test_multiproc_nondaemon.py b/nipype/pipeline/plugins/tests/test_multiproc_nondaemon.py index 89336c2026..6b2c209c77 100644 --- a/nipype/pipeline/plugins/tests/test_multiproc_nondaemon.py +++ b/nipype/pipeline/plugins/tests/test_multiproc_nondaemon.py @@ -9,9 +9,9 @@ def mytestFunction(insum=0): - ''' + """ Run a multiprocessing job and spawn child processes. - ''' + """ # need to import here since this is executed as an external process import multiprocessing @@ -31,9 +31,9 @@ def mytestFunction(insum=0): f = [None] * numberOfThreads def dummyFunction(filename): - ''' + """ This function writes the value 45 to the given filename. - ''' + """ j = 0 for i in range(0, 10): j += i @@ -83,9 +83,9 @@ def dummyFunction(filename): def run_multiproc_nondaemon_with_flag(nondaemon_flag): - ''' + """ Start a pipe with two nodes using the multiproc plugin and passing the nondaemon_flag. - ''' + """ cur_dir = os.getcwd() temp_dir = mkdtemp(prefix='test_engine_') @@ -124,13 +124,13 @@ def run_multiproc_nondaemon_with_flag(nondaemon_flag): def test_run_multiproc_nondaemon_false(): - ''' + """ This is the entry point for the test. Two times a pipe of several multiprocessing jobs gets executed. First, without the nondaemon flag. Second, with the nondaemon flag. Since the processes of the pipe start child processes, the execution only succeeds when the non_daemon flag is on. - ''' + """ shouldHaveFailed = False try: # with nondaemon_flag = False, the execution should fail diff --git a/nipype/pipeline/plugins/tests/test_oar.py b/nipype/pipeline/plugins/tests/test_oar.py index a5ef97fee3..59d6e45452 100644 --- a/nipype/pipeline/plugins/tests/test_oar.py +++ b/nipype/pipeline/plugins/tests/test_oar.py @@ -24,11 +24,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + @skipif(True) def test_run_oar(): diff --git a/nipype/pipeline/plugins/tests/test_pbs.py b/nipype/pipeline/plugins/tests/test_pbs.py index 8aa52e1163..5fbaa1b7e8 100644 --- a/nipype/pipeline/plugins/tests/test_pbs.py +++ b/nipype/pipeline/plugins/tests/test_pbs.py @@ -25,11 +25,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + @skipif(True) def test_run_pbsgraph(): diff --git a/nipype/pipeline/plugins/tests/test_somaflow.py b/nipype/pipeline/plugins/tests/test_somaflow.py index 27b2e30a83..0025d214c3 100644 --- a/nipype/pipeline/plugins/tests/test_somaflow.py +++ b/nipype/pipeline/plugins/tests/test_somaflow.py @@ -27,11 +27,9 @@ def _run_interface(self, runtime): runtime.returncode = 0 return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs['output1'] = [1, self.inputs.input1] - return outputs - + def _post_run(self): + self.outputs.output1 = [1, self.inputs.input1] + @skipif(soma_not_loaded) def test_run_somaflow(): diff --git a/nipype/pkg_info.py b/nipype/pkg_info.py index 04ea874f7d..f32043ee61 100644 --- a/nipype/pkg_info.py +++ b/nipype/pkg_info.py @@ -14,7 +14,7 @@ def pkg_commit_hash(pkg_path): - ''' Get short form of commit hash given directory `pkg_path` + """ Get short form of commit hash given directory `pkg_path` There should be a file called 'COMMIT_INFO.txt' in `pkg_path`. This is a file in INI file format, with at least one section: ``commit hash``, and two @@ -42,7 +42,7 @@ def pkg_commit_hash(pkg_path): Where we got the hash from - description hash_str : str short form of hash - ''' + """ # Try and get commit from written commit text file pth = os.path.join(pkg_path, COMMIT_INFO_FNAME) if not os.path.isfile(pth): @@ -67,7 +67,7 @@ def pkg_commit_hash(pkg_path): def get_pkg_info(pkg_path): - ''' Return dict describing the context of this package + """ Return dict describing the context of this package Parameters ---------- @@ -78,7 +78,7 @@ def get_pkg_info(pkg_path): ------- context : dict with named parameters of interest - ''' + """ src, hsh = pkg_commit_hash(pkg_path) import networkx import nibabel diff --git a/nipype/utils/config.py b/nipype/utils/config.py index bd7ab032ef..a4885c8f18 100644 --- a/nipype/utils/config.py +++ b/nipype/utils/config.py @@ -1,13 +1,13 @@ # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: -''' +""" Created on 20 Apr 2010 logging options : INFO, DEBUG hash_method : content, timestamp @author: Chris Filo Gorgolewski -''' +""" from future import standard_library standard_library.install_aliases() from builtins import object diff --git a/nipype/utils/errors.py b/nipype/utils/errors.py new file mode 100644 index 0000000000..0026e84652 --- /dev/null +++ b/nipype/utils/errors.py @@ -0,0 +1,23 @@ +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +""" +Define custom errors +""" + +class InterfaceError(Exception): + """Error raised in nipype interfaces""" + def __init__(self, value): + self.value = value + super(InterfaceError, self).__init__(value) + + def __str__(self): + return repr(self.value) + +class InterfaceInputsError(InterfaceError): + """Error raised in nipype interfaces""" + def __init__(self, value): + self.value = value + super(InterfaceInputsError, self).__init__(value) + + def __str__(self): + return repr(self.value) diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py index de6f6760f5..c5a83e86b7 100644 --- a/nipype/utils/filemanip.py +++ b/nipype/utils/filemanip.py @@ -3,25 +3,23 @@ """Miscellaneous file manipulation functions """ - -from future import standard_library -standard_library.install_aliases() - +import sys +import os +import re +import shutil import pickle import gzip import hashlib from hashlib import md5 + +from future import standard_library +standard_library.install_aliases() import simplejson -import os -import re -import shutil import numpy as np from .misc import is_container -from .config import mkdir_p from ..external.six import string_types -from ..interfaces.traits_extension import isdefined from .. import logging, config fmlogger = logging.getLogger("filemanip") @@ -86,22 +84,12 @@ def split_filename(fname): """ - special_extensions = [".nii.gz", ".tar.gz"] - pth = os.path.dirname(fname) fname = os.path.basename(fname) - - ext = None - for special_ext in special_extensions: - ext_len = len(special_ext) - if (len(fname) > ext_len) and \ - (fname[-ext_len:].lower() == special_ext.lower()): - ext = fname[-ext_len:] - fname = fname[:-ext_len] - break - if not ext: - fname, ext = os.path.splitext(fname) - + fname, ext = os.path.splitext(fname) + if ext == '.gz': + fname, ext2 = os.path.splitext(fname) + ext = ext2 + ext return pth, fname, ext @@ -135,7 +123,7 @@ def fname_presuffix(fname, prefix='', suffix='', newpath=None, use_ext=True): pth, fname, ext = split_filename(fname) if not use_ext: ext = '' - if newpath and isdefined(newpath): + if newpath is not None: pth = os.path.abspath(newpath) return os.path.join(pth, prefix + fname + suffix + ext) @@ -169,10 +157,19 @@ def check_forhash(filename): else: return False, None +def auto_hash(afile, hash_method=None, chunk_len=8192, crypto=hashlib.md5): + """Checks the hash method and calls the appropriate function""" + if hash_method is None: + hash_method = config.get('execution', 'hash_method').lower() -def hash_infile(afile, chunk_len=8192, crypto=hashlib.md5): + if hash_method not in ['content', 'timestamp']: + raise ValueError("Unknown hash method: %s" % hash_method) + func = getattr(sys.modules[__name__], 'hash_' + hash_method) + return func(afile, chunk_len, crypto) + +def hash_content(afile, chunk_len=8192, crypto=hashlib.md5): """ Computes hash of a file using 'crypto' module""" - hex = None + hashhex = None if os.path.isfile(afile): crypto_obj = crypto() with open(afile, 'rb') as fp: @@ -181,11 +178,10 @@ def hash_infile(afile, chunk_len=8192, crypto=hashlib.md5): if not data: break crypto_obj.update(data) - hex = crypto_obj.hexdigest() - return hex - + hashhex = crypto_obj.hexdigest() + return hashhex -def hash_timestamp(afile): +def hash_timestamp(afile, **kwargs): # pylint: disable=W0613 """ Computes md5 hash of the timestamp of a file """ md5hex = None if os.path.isfile(afile): @@ -198,7 +194,7 @@ def hash_timestamp(afile): def copyfile(originalfile, newfile, copy=False, create_new=False, - hashmethod=None, use_hardlink=False): + hash_method=None, use_hardlink=False): """Copy or symlink ``originalfile`` to ``newfile``. Parameters @@ -232,14 +228,14 @@ def copyfile(originalfile, newfile, copy=False, create_new=False, fname += "_c%04d" % i newfile = base + os.sep + fname + ext - if hashmethod is None: - hashmethod = config.get('execution', 'hash_method').lower() + if hash_method is None: + hash_method = config.get('execution', 'hash_method').lower() elif os.path.exists(newfile): - if hashmethod == 'timestamp': + if hash_method == 'timestamp': newhash = hash_timestamp(newfile) - elif hashmethod == 'content': - newhash = hash_infile(newfile) + elif hash_method == 'content': + newhash = hash_content(newfile) fmlogger.debug("File: %s already exists,%s, copy:%d" % (newfile, newhash, copy)) # the following seems unnecessary @@ -249,10 +245,10 @@ def copyfile(originalfile, newfile, copy=False, create_new=False, # newhash = None if os.name is 'posix' and not copy: if os.path.lexists(newfile): - if hashmethod == 'timestamp': + if hash_method == 'timestamp': orighash = hash_timestamp(originalfile) - elif hashmethod == 'content': - orighash = hash_infile(originalfile) + elif hash_method == 'content': + orighash = hash_content(originalfile) fmlogger.debug('Original hash: %s, %s' % (originalfile, orighash)) if newhash != orighash: os.unlink(newfile) @@ -260,10 +256,10 @@ def copyfile(originalfile, newfile, copy=False, create_new=False, os.symlink(originalfile, newfile) else: if newhash: - if hashmethod == 'timestamp': + if hash_method == 'timestamp': orighash = hash_timestamp(originalfile) - elif hashmethod == 'content': - orighash = hash_infile(originalfile) + elif hash_method == 'content': + orighash = hash_content(originalfile) if (newhash is None) or (newhash != orighash): try: fmlogger.debug("Copying File: %s->%s" % diff --git a/nipype/utils/matlabtools.py b/nipype/utils/matlabtools.py index e272288b75..b32a013f43 100644 --- a/nipype/utils/matlabtools.py +++ b/nipype/utils/matlabtools.py @@ -13,7 +13,7 @@ def fltcols(vals): - ''' Trivial little function to make 1xN float vector ''' + """ Trivial little function to make 1xN float vector """ return np.atleast_2d(np.array(vals, dtype=float)) diff --git a/nipype/utils/nipype2boutiques.py b/nipype/utils/nipype2boutiques.py index 49fc1d755d..60a17b48ba 100644 --- a/nipype/utils/nipype2boutiques.py +++ b/nipype/utils/nipype2boutiques.py @@ -51,14 +51,14 @@ def main(argv): def generate_boutiques_descriptor(module, interface_name, ignored_template_inputs, docker_image, docker_index, verbose, ignore_template_numbers): - ''' + """ Returns a JSON string containing a JSON Boutiques description of a Nipype interface. Arguments: * module: module where the Nipype interface is declared. * interface: Nipype interface. * ignored_template_inputs: a list of input names that should be ignored in the generation of output path templates. * ignore_template_numbers: True if numbers must be ignored in output path creations. - ''' + """ if not module: raise Exception("Undefined module.") @@ -206,10 +206,10 @@ def get_boutiques_output(name, interface, tool_inputs, verbose=False): def get_type_from_spec_info(spec_info): - ''' + """ Returns an input type from the spec info. There must be a better way to get an input type in Nipype than to parse the spec info. - ''' + """ if ("an existing file name" in spec_info) or ("input volumes" in spec_info): return "File" elif ("an integer" in spec_info or "a float" in spec_info): @@ -220,21 +220,21 @@ def get_type_from_spec_info(spec_info): def is_list(spec_info): - ''' + """ Returns True if the spec info looks like it describes a list parameter. There must be a better way in Nipype to check if an input is a list. - ''' + """ if "a list" in spec_info: return True return False def get_unique_value(type, id): - ''' + """ Returns a unique value of type 'type', for input with id 'id', assuming id is unique. - ''' + """ return { "File": os.path.abspath(create_tempfile()), "Boolean": True, @@ -244,9 +244,9 @@ def get_unique_value(type, id): def create_tempfile(): - ''' + """ Creates a temp file and returns its name. - ''' + """ fileTemp = tempfile.NamedTemporaryFile(delete=False) fileTemp.write("hello") fileTemp.close() @@ -254,7 +254,7 @@ def create_tempfile(): def must_generate_value(name, type, ignored_template_inputs, spec_info, spec, ignore_template_numbers): - ''' + """ Return True if a temporary value must be generated for this input. Arguments: * name: input name. @@ -262,7 +262,7 @@ def must_generate_value(name, type, ignored_template_inputs, spec_info, spec, ig * ignored_template_inputs: a list of inputs names for which no value must be generated. * spec_info: spec info of the Nipype input * ignore_template_numbers: True if numbers must be ignored. - ''' + """ # Return false when type is number and numbers must be ignored. if ignore_template_numbers and type == "Number": return False diff --git a/nipype/utils/provenance.py b/nipype/utils/provenance.py index 028200573e..6d461e15cf 100644 --- a/nipype/utils/provenance.py +++ b/nipype/utils/provenance.py @@ -20,7 +20,7 @@ from ..external.six import string_types, text_type from .. import get_info -from .filemanip import (md5, hashlib, hash_infile) +from .filemanip import (md5, hashlib, hash_content) from .. import logging iflogger = logging.getLogger('interface') @@ -102,7 +102,7 @@ def _get_sorteddict(object, dictwithhash=False): out = tuple(out) else: if isinstance(object, string_types) and os.path.isfile(object): - hash = hash_infile(object) + hash = hash_content(object) if dictwithhash: out = (object, hash) else: @@ -226,7 +226,7 @@ def prov_encode(graph, value, create_container=True): if isinstance(value, string_types) and os.path.exists(value): attr.update({pm.PROV['location']: encoded_literal}) if not os.path.isdir(value): - sha512 = hash_infile(value, crypto=hashlib.sha512) + sha512 = hash_content(value, crypto=hashlib.sha512) attr.update({crypto['sha512']: pm.Literal(sha512, pm.XSD['string'])}) id = get_attr_id(attr, skip=[pm.PROV['location'], diff --git a/setup.py b/setup.py index 2cae86461a..fc0c9e354d 100755 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ from distutils.core import setup # Commit hash writing, and dependency checking -''' Distutils / setuptools helpers from nibabel.nisext''' +""" Distutils / setuptools helpers from nibabel.nisext""" import os from os.path import join as pjoin @@ -89,7 +89,7 @@ def get_comrec_build(pkg_dir, build_cmd=build_py): package for an example. """ class MyBuildPy(build_cmd): - ''' Subclass to write commit data into installation tree ''' + """ Subclass to write commit data into installation tree """ def run(self): build_cmd.run(self) import subprocess @@ -129,7 +129,7 @@ def package_check(pkg_name, version=None, messages=None, setuptools_args=None ): - ''' Check if package `pkg_name` is present and has good enough version + """ Check if package `pkg_name` is present and has good enough version Has two modes of operation. If `setuptools_args` is None (the default), raise an error for missing non-optional dependencies and log warnings for @@ -171,7 +171,7 @@ def package_check(pkg_name, version=None, If None, raise errors / warnings for missing non-optional / optional dependencies. If dict fill key values ``install_requires`` and ``extras_require`` for non-optional and optional dependencies. - ''' + """ setuptools_mode = setuptools_args is not None optional_tf = bool(optional) if version_getter is None: diff --git a/tools/apigen.py b/tools/apigen.py index dba2ce0a37..6e2f645377 100644 --- a/tools/apigen.py +++ b/tools/apigen.py @@ -28,8 +28,8 @@ # Functions and classes class ApiDocWriter(object): - ''' Class for automatic detection and parsing of API docs - to Sphinx-parsable reST format''' + """ Class for automatic detection and parsing of API docs + to Sphinx-parsable reST format""" # only separating first two levels rst_section_levels = ['*', '=', '-', '~', '^'] @@ -40,7 +40,7 @@ def __init__(self, package_skip_patterns=None, module_skip_patterns=None, ): - ''' Initialize package for parsing + """ Initialize package for parsing Parameters ---------- @@ -65,7 +65,7 @@ def __init__(self, ``.util.console`` If is None, gives default. Default is: ['\.setup$', '\._'] - ''' + """ if package_skip_patterns is None: package_skip_patterns = ['\\.tests$'] if module_skip_patterns is None: @@ -79,7 +79,7 @@ def get_package_name(self): return self._package_name def set_package_name(self, package_name): - ''' Set package_name + """ Set package_name >>> docwriter = ApiDocWriter('sphinx') >>> import sphinx @@ -89,7 +89,7 @@ def set_package_name(self, package_name): >>> import docutils >>> docwriter.root_path == docutils.__path__[0] True - ''' + """ # It's also possible to imagine caching the module parsing here self._package_name = package_name self.root_module = __import__(package_name) @@ -100,7 +100,7 @@ def set_package_name(self, package_name): 'get/set package_name') def _get_object_name(self, line): - ''' Get second token in line + """ Get second token in line >>> docwriter = ApiDocWriter('sphinx') >>> docwriter._get_object_name(" def func(): ") 'func' @@ -108,14 +108,14 @@ def _get_object_name(self, line): 'Klass' >>> docwriter._get_object_name(" class Klass: ") 'Klass' - ''' + """ name = line.split()[1].split('(')[0].strip() # in case we have classes which are not derived from object # ie. old style classes return name.rstrip(':') def _uri2path(self, uri): - ''' Convert uri to absolute filepath + """ Convert uri to absolute filepath Parameters ---------- @@ -141,7 +141,7 @@ def _uri2path(self, uri): True >>> docwriter._uri2path('sphinx.does_not_exist') - ''' + """ if uri == self.package_name: return os.path.join(self.root_path, '__init__.py') path = uri.replace('.', os.path.sep) @@ -157,14 +157,14 @@ def _uri2path(self, uri): return path def _path2uri(self, dirpath): - ''' Convert directory path to uri ''' + """ Convert directory path to uri """ relpath = dirpath.replace(self.root_path, self.package_name) if relpath.startswith(os.path.sep): relpath = relpath[1:] return relpath.replace(os.path.sep, '.') def _parse_module(self, uri): - ''' Parse module defined in *uri* ''' + """ Parse module defined in *uri* """ filename = self._uri2path(uri) if filename is None: # nothing that we could handle here. @@ -175,7 +175,7 @@ def _parse_module(self, uri): return functions, classes def _parse_lines(self, linesource): - ''' Parse lines of text for functions and classes ''' + """ Parse lines of text for functions and classes """ functions = [] classes = [] for line in linesource: @@ -196,7 +196,7 @@ def _parse_lines(self, linesource): return functions, classes def generate_api_doc(self, uri): - '''Make autodoc documentation template string for a module + """Make autodoc documentation template string for a module Parameters ---------- @@ -207,7 +207,7 @@ def generate_api_doc(self, uri): ------- S : string Contents of API doc - ''' + """ # get the names of all classes and functions functions, classes = self._parse_module(uri) if not len(functions) and not len(classes): @@ -271,7 +271,7 @@ def generate_api_doc(self, uri): return ad def _survives_exclude(self, matchstr, match_type): - ''' Returns True if *matchstr* does not match patterns + """ Returns True if *matchstr* does not match patterns ``self.package_name`` removed from front of string if present @@ -290,7 +290,7 @@ def _survives_exclude(self, matchstr, match_type): >>> dw.module_skip_patterns.append('^\\.badmod$') >>> dw._survives_exclude('sphinx.badmod', 'module') False - ''' + """ if match_type == 'module': patterns = self.module_skip_patterns elif match_type == 'package': @@ -314,7 +314,7 @@ def _survives_exclude(self, matchstr, match_type): return True def discover_modules(self): - ''' Return module sequence discovered from ``self.package_name`` + """ Return module sequence discovered from ``self.package_name`` Parameters @@ -336,7 +336,7 @@ def discover_modules(self): >>> 'sphinx.util' in dw.discover_modules() False >>> - ''' + """ modules = [] # raw directory parsing for dirpath, dirnames, filenames in os.walk(self.root_path): diff --git a/tools/checkspecs.py b/tools/checkspecs.py index 8974428780..24ae1f9e1c 100644 --- a/tools/checkspecs.py +++ b/tools/checkspecs.py @@ -28,7 +28,7 @@ def __init__(self, module_skip_patterns=None, class_skip_patterns=None ): - ''' Initialize package for parsing + """ Initialize package for parsing Parameters ---------- @@ -55,7 +55,7 @@ def __init__(self, Sequence of strings giving classes to be excluded Default is: None - ''' + """ if package_skip_patterns is None: package_skip_patterns = ['\\.tests$'] if module_skip_patterns is None: @@ -117,14 +117,14 @@ def _uri2path(self, uri): return path def _path2uri(self, dirpath): - ''' Convert directory path to uri ''' + """ Convert directory path to uri """ relpath = dirpath.replace(self.root_path, self.package_name) if relpath.startswith(os.path.sep): relpath = relpath[1:] return relpath.replace(os.path.sep, '.') def _parse_module(self, uri): - ''' Parse module defined in *uri* ''' + """ Parse module defined in *uri* """ filename = self._uri2path(uri) if filename is None: # nothing that we could handle here. @@ -135,7 +135,7 @@ def _parse_module(self, uri): return functions, classes def _parse_lines(self, linesource, module): - ''' Parse lines of text for functions and classes ''' + """ Parse lines of text for functions and classes """ functions = [] classes = [] for line in linesource: @@ -293,7 +293,7 @@ def test_specs(self, uri): return bad_specs def _survives_exclude(self, matchstr, match_type): - ''' Returns True if *matchstr* does not match patterns + """ Returns True if *matchstr* does not match patterns ``self.package_name`` removed from front of string if present @@ -312,7 +312,7 @@ def _survives_exclude(self, matchstr, match_type): >>> dw.module_skip_patterns.append('^\\.badmod$') >>> dw._survives_exclude('sphinx.badmod', 'module') False - ''' + """ if match_type == 'module': patterns = self.module_skip_patterns elif match_type == 'package': @@ -336,7 +336,7 @@ def _survives_exclude(self, matchstr, match_type): return True def discover_modules(self): - ''' Return module sequence discovered from ``self.package_name`` + """ Return module sequence discovered from ``self.package_name`` Parameters @@ -350,7 +350,7 @@ def discover_modules(self): Examples -------- - ''' + """ modules = [self.package_name] # raw directory parsing for dirpath, dirnames, filenames in os.walk(self.root_path): diff --git a/tools/gitwash_dumper.py b/tools/gitwash_dumper.py index 8803786c8c..7e7e85d4b8 100755 --- a/tools/gitwash_dumper.py +++ b/tools/gitwash_dumper.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -''' Checkout gitwash repo into directory and do search replace on name ''' +""" Checkout gitwash repo into directory and do search replace on name """ from __future__ import print_function import os @@ -52,9 +52,9 @@ def cp_files(in_path, globs, out_path): def filename_search_replace(sr_pairs, filename, backup=False): - ''' Search and replace for expressions in files + """ Search and replace for expressions in files - ''' + """ in_txt = open(filename, 'rt').read(-1) out_txt = in_txt[:] for in_exp, out_exp in sr_pairs: @@ -153,13 +153,13 @@ def make_link_targets(proj_name, out_links.close() -USAGE = ''' +USAGE = """ If not set with options, the repository name is the same as the If not set with options, the main github user is the same as the -repository name.''' +repository name.""" GITWASH_CENTRAL = 'git://github.com/matthew-brett/gitwash.git' diff --git a/tools/interfacedocgen.py b/tools/interfacedocgen.py index eda0c6a8b5..745f7945e1 100644 --- a/tools/interfacedocgen.py +++ b/tools/interfacedocgen.py @@ -40,8 +40,8 @@ class InterfaceHelpWriter(object): - ''' Class for automatic detection and parsing of API docs - to Sphinx-parsable reST format''' + """ Class for automatic detection and parsing of API docs + to Sphinx-parsable reST format""" # only separating first two levels rst_section_levels = ['*', '=', '-', '~', '^'] @@ -53,7 +53,7 @@ def __init__(self, module_skip_patterns=None, class_skip_patterns=None ): - ''' Initialize package for parsing + """ Initialize package for parsing Parameters ---------- @@ -82,7 +82,7 @@ def __init__(self, Sequence of strings giving classes to be excluded Default is: None - ''' + """ if package_skip_patterns is None: package_skip_patterns = ['\\.tests$'] if module_skip_patterns is None: @@ -100,7 +100,7 @@ def get_package_name(self): return self._package_name def set_package_name(self, package_name): - ''' Set package_name + """ Set package_name >>> docwriter = ApiDocWriter('sphinx') >>> import sphinx @@ -110,7 +110,7 @@ def set_package_name(self, package_name): >>> import docutils >>> docwriter.root_path == docutils.__path__[0] True - ''' + """ # It's also possible to imagine caching the module parsing here self._package_name = package_name self.root_module = __import__(package_name) @@ -121,7 +121,7 @@ def set_package_name(self, package_name): 'get/set package_name') def _get_object_name(self, line): - ''' Get second token in line + """ Get second token in line >>> docwriter = ApiDocWriter('sphinx') >>> docwriter._get_object_name(" def func(): ") 'func' @@ -129,14 +129,14 @@ def _get_object_name(self, line): 'Klass' >>> docwriter._get_object_name(" class Klass: ") 'Klass' - ''' + """ name = line.split()[1].split('(')[0].strip() # in case we have classes which are not derived from object # ie. old style classes return name.rstrip(':') def _uri2path(self, uri): - ''' Convert uri to absolute filepath + """ Convert uri to absolute filepath Parameters ---------- @@ -162,7 +162,7 @@ def _uri2path(self, uri): True >>> docwriter._uri2path('sphinx.does_not_exist') - ''' + """ if uri == self.package_name: return os.path.join(self.root_path, '__init__.py') path = uri.replace('.', os.path.sep) @@ -178,14 +178,14 @@ def _uri2path(self, uri): return path def _path2uri(self, dirpath): - ''' Convert directory path to uri ''' + """ Convert directory path to uri """ relpath = dirpath.replace(self.root_path, self.package_name) if relpath.startswith(os.path.sep): relpath = relpath[1:] return relpath.replace(os.path.sep, '.') def _parse_module(self, uri): - ''' Parse module defined in *uri* ''' + """ Parse module defined in *uri* """ filename = self._uri2path(uri) if filename is None: # nothing that we could handle here. @@ -196,7 +196,7 @@ def _parse_module(self, uri): return functions, classes def _parse_lines(self, linesource, module): - ''' Parse lines of text for functions and classes ''' + """ Parse lines of text for functions and classes """ functions = [] classes = [] for line in linesource: @@ -231,7 +231,7 @@ def _write_graph_section(self, fname, title): return ad def generate_api_doc(self, uri): - '''Make autodoc documentation template string for a module + """Make autodoc documentation template string for a module Parameters ---------- @@ -242,7 +242,7 @@ def generate_api_doc(self, uri): ------- S : string Contents of API doc - ''' + """ # get the names of all classes and functions functions, classes = self._parse_module(uri) workflows = [] @@ -343,7 +343,7 @@ def generate_api_doc(self, uri): return ad def _survives_exclude(self, matchstr, match_type): - ''' Returns True if *matchstr* does not match patterns + """ Returns True if *matchstr* does not match patterns ``self.package_name`` removed from front of string if present @@ -362,7 +362,7 @@ def _survives_exclude(self, matchstr, match_type): >>> dw.module_skip_patterns.append('^\\.badmod$') >>> dw._survives_exclude('sphinx.badmod', 'module') False - ''' + """ if match_type == 'module': patterns = self.module_skip_patterns elif match_type == 'package': @@ -386,7 +386,7 @@ def _survives_exclude(self, matchstr, match_type): return True def discover_modules(self): - ''' Return module sequence discovered from ``self.package_name`` + """ Return module sequence discovered from ``self.package_name`` Parameters @@ -408,7 +408,7 @@ def discover_modules(self): >>> 'sphinx.util' in dw.discover_modules() False >>> - ''' + """ modules = [self.package_name] # raw directory parsing for dirpath, dirnames, filenames in os.walk(self.root_path):