From 60de5ad9ed43838d5ba71c4e800feea5cafdeeea Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Tue, 24 Apr 2018 08:06:45 -0400 Subject: [PATCH 1/4] FIX: Do not generate filename when required fields are missing --- nipype/interfaces/base/core.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 73f8d0751e..5a0210a227 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -1074,8 +1074,13 @@ def _filename_from_source(self, name, chain=None): return retval # Do not generate filename when excluded by other inputs - if trait_spec.xor and any(isdefined(getattr(self.inputs, field)) - for field in trait_spec.xor): + if any(isdefined(getattr(self.inputs, field)) + for field in trait_spec.xor or ()): + return retval + + # Do not generate filename when required fields are missing + if not all(isdefined(getattr(self.inputs, field)) + for field in trait_spec.requires or ()): return retval if isdefined(retval) and "%s" in retval: From ce065cf53c753824afc9262225c70bad4529de74 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Tue, 24 Apr 2018 09:36:54 -0400 Subject: [PATCH 2/4] FIX: Only find paths for created files --- nipype/interfaces/base/core.py | 5 +++-- nipype/interfaces/fsl/tests/test_preprocess.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 5a0210a227..5f8c1bce9e 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -1149,8 +1149,9 @@ def _list_outputs(self): 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)) + fname = self._filename_from_source(name) + if isdefined(fname): + outputs[out_name] = os.path.abspath(fname) return outputs def _parse_inputs(self, skip=None): diff --git a/nipype/interfaces/fsl/tests/test_preprocess.py b/nipype/interfaces/fsl/tests/test_preprocess.py index 955549f801..288a5555a0 100644 --- a/nipype/interfaces/fsl/tests/test_preprocess.py +++ b/nipype/interfaces/fsl/tests/test_preprocess.py @@ -313,6 +313,7 @@ def test_flirt(setup_flirt): os.path.join(os.getcwd(), flirter.inputs.out_file) assert outs['out_matrix_file'] == \ os.path.join(os.getcwd(), flirter.inputs.out_matrix_file) + assert not isdefined(flirter.inputs.out_log) # Mcflirt From 1d2a5c5bd1972974ae64dc18a4bfc4d23877edc5 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Tue, 24 Apr 2018 10:44:40 -0400 Subject: [PATCH 3/4] FIX: Remove -mcmap argument in absence of -nodv --- nipype/interfaces/niftyfit/dwi.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/nipype/interfaces/niftyfit/dwi.py b/nipype/interfaces/niftyfit/dwi.py index f8f93c7d12..23b73aea90 100644 --- a/nipype/interfaces/niftyfit/dwi.py +++ b/nipype/interfaces/niftyfit/dwi.py @@ -66,13 +66,11 @@ class FitDwiInputSpec(CommandLineInputSpec): argstr='-nodiff %s') # Output options, with templated output names based on the source image - desc = 'Filename of multi-compartment model parameter map \ -(-ivim,-ball,-nod)' - mcmap_file = traits.File( name_source=['source_file'], name_template='%s_mcmap.nii.gz', - desc=desc, + desc='Filename of multi-compartment model parameter map ' + '(-ivim,-ball,-nod)', argstr='-mcmap %s', requires=['nodv_flag']) @@ -239,10 +237,7 @@ class FitDwiInputSpec(CommandLineInputSpec): class FitDwiOutputSpec(TraitedSpec): """ Output Spec for FitDwi. """ - desc = 'Filename of multi-compartment model parameter map \ -(-ivim,-ball,-nod)' - mcmap_file = traits.File(desc=desc) error_file = traits.File(desc='Filename of parameter error maps') res_file = traits.File(desc='Filename of model residual map') syn_file = traits.File(desc='Filename of synthetic image') @@ -253,10 +248,9 @@ class FitDwiOutputSpec(TraitedSpec): rgbmap_file = traits.File(desc='Filename of colour FA map') tenmap_file = traits.File(desc='Filename of tensor map') tenmap2_file = traits.File(desc='Filename of tensor map [lower tri]') - desc = 'Filename of multi-compartment model parameter map \ -(-ivim,-ball,-nod).' - mcmap_file = traits.File(desc=desc) + mcmap_file = traits.File(desc='Filename of multi-compartment model ' + 'parameter map (-ivim,-ball,-nod).') mcout = traits.File(desc='Filename of mc samples (ascii text file)') @@ -281,10 +275,10 @@ class FitDwi(NiftyFitCommand): >>> fit_dwi.inputs.rgbmap_file = 'rgb.nii.gz' >>> fit_dwi.cmdline 'fit_dwi -source dwi.nii.gz -bval bvals -bvec bvecs -dti \ --error dwi_error.nii.gz -famap dwi_famap.nii.gz -mcmap dwi_mcmap.nii.gz \ --mcout dwi_mcout.txt -mdmap dwi_mdmap.nii.gz -nodiff dwi_no_diff.nii.gz \ --res dwi_resmap.nii.gz -rgbmap rgb.nii.gz -syn dwi_syn.nii.gz \ --tenmap2 dwi_tenmap2.nii.gz -v1map dwi_v1map.nii.gz' +-error dwi_error.nii.gz -famap dwi_famap.nii.gz -mcout dwi_mcout.txt \ +-mdmap dwi_mdmap.nii.gz -nodiff dwi_no_diff.nii.gz -res dwi_resmap.nii.gz \ +-rgbmap rgb.nii.gz -syn dwi_syn.nii.gz -tenmap2 dwi_tenmap2.nii.gz \ +-v1map dwi_v1map.nii.gz' """ _cmd = get_custom_path('fit_dwi', env_dir='NIFTYFITDIR') From 0b07124c5eb6eae39ec564d2513b08012de8d340 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 25 Apr 2018 10:47:34 -0400 Subject: [PATCH 4/4] TEST: Test xor, requires constraints on name_source traits --- nipype/interfaces/base/core.py | 3 ++ nipype/interfaces/base/tests/test_specs.py | 49 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/nipype/interfaces/base/core.py b/nipype/interfaces/base/core.py index 5f8c1bce9e..8cea616314 100644 --- a/nipype/interfaces/base/core.py +++ b/nipype/interfaces/base/core.py @@ -1123,6 +1123,9 @@ def _filename_from_source(self, name, chain=None): base = self._filename_from_source(ns, chain) if isdefined(base): _, _, source_ext = split_filename(base) + else: + # Do not generate filename when required fields are missing + return retval chain = None retval = name_template % base diff --git a/nipype/interfaces/base/tests/test_specs.py b/nipype/interfaces/base/tests/test_specs.py index 86b70f40dc..2586fc7b6a 100644 --- a/nipype/interfaces/base/tests/test_specs.py +++ b/nipype/interfaces/base/tests/test_specs.py @@ -274,6 +274,55 @@ class TestCycle(nib.CommandLine): assert '%s_generated_mootpl' % nme in res +def test_namesource_constraints(setup_file): + tmp_infile = setup_file + tmpd, nme, ext = split_filename(tmp_infile) + + class constrained_spec(nib.CommandLineInputSpec): + in_file = nib.File(argstr="%s", position=1) + threshold = traits.Float( + argstr="%g", + xor=['mask_file'], + position=2) + mask_file = nib.File( + argstr="%s", + name_source=['in_file'], + name_template='%s_mask', + keep_extension=True, + xor=['threshold'], + position=2) + out_file1 = nib.File( + argstr="%s", + name_source=['in_file'], + name_template='%s_out1', + keep_extension=True, + position=3) + out_file2 = nib.File( + argstr="%s", + name_source=['in_file'], + name_template='%s_out2', + keep_extension=True, + requires=['threshold'], + position=4) + + class TestConstrained(nib.CommandLine): + _cmd = "mycommand" + input_spec = constrained_spec + + tc = TestConstrained() + + # name_source undefined, so template traits remain undefined + assert tc.cmdline == 'mycommand' + + # mask_file and out_file1 enabled by name_source definition + tc.inputs.in_file = os.path.basename(tmp_infile) + assert tc.cmdline == 'mycommand foo.txt foo_mask.txt foo_out1.txt' + + # mask_file disabled by threshold, out_file2 enabled by threshold + tc.inputs.threshold = 10. + assert tc.cmdline == 'mycommand foo.txt 10 foo_out1.txt foo_out2.txt' + + def test_TraitedSpec_withFile(setup_file): tmp_infile = setup_file tmpd, nme = os.path.split(tmp_infile)