Skip to content

FIX: Resolving absolute to relative paths in output #2966

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion nipype/algorithms/tests/test_auto_AddCSVRow.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
def test_AddCSVRow_inputs():
input_map = dict(
_outputs=dict(usedefault=True, ),
in_file=dict(mandatory=True, ),
in_file=dict(
extensions=None,
mandatory=True,
),
)
inputs = AddCSVRow.input_spec()

Expand Down
20 changes: 7 additions & 13 deletions nipype/interfaces/afni/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ class ClipLevel(AFNICommandBase):
input_spec = ClipLevelInputSpec
output_spec = ClipLevelOutputSpec

def aggregate_outputs(self, runtime=None, needed_outputs=None):
def aggregate_outputs(self, runtime=None, needed_outputs=None, rebase_cwd=None):

outputs = self._outputs()

Expand Down Expand Up @@ -2115,21 +2115,15 @@ class Seg(AFNICommandBase):
input_spec = SegInputSpec
output_spec = AFNICommandOutputSpec

def aggregate_outputs(self, runtime=None, needed_outputs=None):

import glob

outputs = self._outputs()
def _list_outputs(self):
from nipype.utils.filemanip import Path

prefix = 'Segsy'
if isdefined(self.inputs.prefix):
outfile = os.path.join(os.getcwd(), self.inputs.prefix,
'Classes+*.BRIK')
else:
outfile = os.path.join(os.getcwd(), 'Segsy', 'Classes+*.BRIK')

outputs.out_file = glob.glob(outfile)[0]
prefix = self.inputs.prefix

return outputs
return {'out_file': str(
sorted(Path() / prefix).glob('Classes+*.BRIK')[0])}


class SkullStripInputSpec(AFNICommandInputSpec):
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/afni/tests/test_auto_Allineate.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def test_Allineate_inputs():
),
out_weight_file=dict(
argstr='-wtprefix %s',
extensions=None,
xor=['allcostx'],
),
outputtype=dict(),
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/afni/tests/test_auto_ClipLevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def test_ClipLevel_inputs():
),
grad=dict(
argstr='-grad %s',
extensions=None,
position=3,
xor='doall',
),
Expand Down
7 changes: 6 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_LocalBistat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ def test_LocalBistat_inputs():
mandatory=True,
position=-1,
),
mask_file=dict(argstr='-mask %s', ),
mask_file=dict(
argstr='-mask %s',
extensions=None,
),
neighborhood=dict(
argstr="-nbhd '%s(%s)'",
mandatory=True,
Expand All @@ -37,6 +40,7 @@ def test_LocalBistat_inputs():
),
out_file=dict(
argstr='-prefix %s',
extensions=None,
keep_extension=True,
name_source='in_file1',
name_template='%s_bistat',
Expand All @@ -49,6 +53,7 @@ def test_LocalBistat_inputs():
),
weight_file=dict(
argstr='-weight %s',
extensions=None,
xor=['automask'],
),
)
Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_Localstat.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ def test_Localstat_inputs():
mandatory=True,
position=-1,
),
mask_file=dict(argstr='-mask %s', ),
mask_file=dict(
argstr='-mask %s',
extensions=None,
),
neighborhood=dict(
argstr="-nbhd '%s(%s)'",
mandatory=True,
Expand All @@ -33,6 +36,7 @@ def test_Localstat_inputs():
),
out_file=dict(
argstr='-prefix %s',
extensions=None,
keep_extension=True,
name_source='in_file',
name_template='%s_localstat',
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_NwarpApply.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ def test_NwarpApply_inputs():
usedefault=True,
),
inv_warp=dict(argstr='-iwarp', ),
master=dict(argstr='-master %s', ),
master=dict(
argstr='-master %s',
extensions=None,
),
out_file=dict(
argstr='-prefix %s',
extensions=None,
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/afni/tests/test_auto_OneDToolPy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ def test_OneDToolPy_inputs():
show_censor_count=dict(argstr='-show_censor_count', ),
show_cormat_warnings=dict(
argstr='-show_cormat_warnings |& tee %s',
extensions=None,
position=-1,
xor=['out_file'],
),
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_Qwarp.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ def test_Qwarp_inputs():
name_source=['in_file'],
name_template='ppp_%s',
),
out_weight_file=dict(argstr='-wtprefix %s', ),
out_weight_file=dict(
argstr='-wtprefix %s',
extensions=None,
),
outputtype=dict(),
overwrite=dict(argstr='-overwrite', ),
pblur=dict(argstr='-pblur %s', ),
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_QwarpPlusMinus.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,10 @@ def test_QwarpPlusMinus_inputs():
position=0,
usedefault=True,
),
out_weight_file=dict(argstr='-wtprefix %s', ),
out_weight_file=dict(
argstr='-wtprefix %s',
extensions=None,
),
outputtype=dict(),
overwrite=dict(argstr='-overwrite', ),
pblur=dict(argstr='-pblur %s', ),
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_ROIStats.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ def test_ROIStats_inputs():
position=-1,
),
quiet=dict(argstr='-quiet', ),
roisel=dict(argstr='-roisel %s', ),
roisel=dict(
argstr='-roisel %s',
extensions=None,
),
stat=dict(argstr='%s...', ),
zerofill=dict(
argstr='-zerofill %s',
Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_ReHo.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ def test_ReHo_inputs():
argstr='-in_rois %s',
extensions=None,
),
mask_file=dict(argstr='-mask %s', ),
mask_file=dict(
argstr='-mask %s',
extensions=None,
),
neighborhood=dict(
argstr='-nneigh %s',
xor=['sphere', 'ellipsoid'],
),
out_file=dict(
argstr='-prefix %s',
extensions=None,
keep_extension=True,
name_source='in_file',
name_template='%s_reho',
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/afni/tests/test_auto_Remlfit.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def test_Remlfit_inputs():
),
matim=dict(
argstr='-matim %s',
extensions=None,
xor=['matrix'],
),
matrix=dict(
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_Resample.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ def test_Resample_inputs():
mandatory=True,
position=-1,
),
master=dict(argstr='-master %s', ),
master=dict(
argstr='-master %s',
extensions=None,
),
num_threads=dict(
nohash=True,
usedefault=True,
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_TCorrMap.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,10 @@ def test_TCorrMap_inputs():
name_source='in_file',
suffix='_qmean',
),
regress_out_timeseries=dict(argstr='-ort %s', ),
regress_out_timeseries=dict(
argstr='-ort %s',
extensions=None,
),
seeds=dict(
argstr='-seed %s',
extensions=None,
Expand Down
1 change: 1 addition & 0 deletions nipype/interfaces/afni/tests/test_auto_Zeropad.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_Zeropad_inputs():
),
master=dict(
argstr='-master %s',
extensions=None,
xor=['I', 'S', 'A', 'P', 'L', 'R', 'z', 'RL', 'AP', 'IS', 'mm'],
),
mm=dict(
Expand Down
8 changes: 4 additions & 4 deletions nipype/interfaces/afni/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,9 @@ class Autobox(AFNICommand):
input_spec = AutoboxInputSpec
output_spec = AutoboxOutputSpec

def aggregate_outputs(self, runtime=None, needed_outputs=None):
outputs = super(Autobox, self).aggregate_outputs(
runtime, needed_outputs)
def aggregate_outputs(self, runtime=None, needed_outputs=None, rebase_cwd=None):
outputs = super(Autobox, self).aggregate_outputs(runtime, needed_outputs,
rebase_cwd=rebase_cwd)
pattern = 'x=(?P<x_min>-?\d+)\.\.(?P<x_max>-?\d+) '\
'y=(?P<y_min>-?\d+)\.\.(?P<y_max>-?\d+) '\
'z=(?P<z_min>-?\d+)\.\.(?P<z_max>-?\d+)'
Expand Down Expand Up @@ -286,7 +286,7 @@ class BrickStat(AFNICommandBase):
input_spec = BrickStatInputSpec
output_spec = BrickStatOutputSpec

def aggregate_outputs(self, runtime=None, needed_outputs=None):
def aggregate_outputs(self, runtime=None, needed_outputs=None, rebase_cwd=None):

outputs = self._outputs()

Expand Down
2 changes: 1 addition & 1 deletion nipype/interfaces/ants/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1487,7 +1487,7 @@ def _format_arg(self, opt, spec, val):
return self._mask_constructor()
return super(MeasureImageSimilarity, self)._format_arg(opt, spec, val)

def aggregate_outputs(self, runtime=None, needed_outputs=None):
def aggregate_outputs(self, runtime=None, needed_outputs=None, rebase_cwd=None):
outputs = self._outputs()
stdout = runtime.stdout.split('\n')
outputs.similarity = float(stdout[0])
Expand Down
77 changes: 42 additions & 35 deletions nipype/interfaces/base/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
from ... import config, logging, LooseVersion
from ...utils.provenance import write_provenance
from ...utils.misc import str2bool, rgetcwd
from ...utils.filemanip import (FileNotFoundError, split_filename,
which, get_dependencies)
from ...utils.filemanip import (split_filename, which, get_dependencies,
FileNotFoundError)
from ...utils.subprocess import run_command

from ...external.due import due

from .traits_extension import traits, isdefined
from .traits_extension import traits, isdefined, rebase_path_traits
from .specs import (BaseInterfaceInputSpec, CommandLineInputSpec,
StdOutCommandLineInputSpec, MpiCommandLineInputSpec,
get_filecopy_info)
Expand Down Expand Up @@ -110,7 +110,7 @@ def run(self):
"""Execute the command."""
raise NotImplementedError

def aggregate_outputs(self, runtime=None, needed_outputs=None):
def aggregate_outputs(self, runtime=None, needed_outputs=None, rebase_cwd=None):
"""Called to populate outputs"""
raise NotImplementedError

Expand Down Expand Up @@ -204,10 +204,10 @@ def _check_requires(self, spec, name, value):
]
if any(values) and isdefined(value):
if len(values) > 1:
fmt = ("%s requires values for inputs %s because '%s' is set. "
fmt = ("%s requires values for inputs %s because '%s' is set. "
"For a list of required inputs, see %s.help()")
else:
fmt = ("%s requires a value for input %s because '%s' is set. "
fmt = ("%s requires a value for input %s because '%s' is set. "
"For a list of required inputs, see %s.help()")
msg = fmt % (self.__class__.__name__,
', '.join("'%s'" % req for req in spec.requires),
Expand Down Expand Up @@ -299,7 +299,7 @@ def _duecredit_cite(self):
r['path'] = self.__module__
due.cite(**r)

def run(self, cwd=None, ignore_exception=None, **inputs):
def run(self, cwd=None, ignore_exception=None, rebase_cwd=None, **inputs):
"""Execute this interface.

This interface will not raise an exception if runtime.returncode is
Expand Down Expand Up @@ -327,6 +327,9 @@ def run(self, cwd=None, ignore_exception=None, **inputs):
if cwd is None:
cwd = syscwd

if rebase_cwd:
rebase_cwd = cwd

os.chdir(cwd) # Change to the interface wd

enable_rm = config.resource_monitor and self.resource_monitor
Expand Down Expand Up @@ -442,42 +445,46 @@ def run(self, cwd=None, ignore_exception=None, **inputs):
return results

def _list_outputs(self):
""" List the expected outputs
"""
"""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
"""
"""Collate expected outputs and check for existence."""
outputs = self._outputs() # Generate an output spec object
if needed_outputs is not None and not needed_outputs:
return outputs

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)
except TraitError as error:
if getattr(error, 'info',
'default').startswith('an existing'):
msg = ("File/Directory '%s' not found for %s output "
"'%s'." % (val, self.__class__.__name__, key))
raise FileNotFoundError(msg)
raise error
predicted_outputs = self._list_outputs() # Predictions from _list_outputs
if not predicted_outputs:
return outputs

# Precalculate the list of output trait names that should be
# aggregated
aggregate_names = set(predicted_outputs.keys())
if needed_outputs:
aggregate_names = set(needed_outputs).intersection(aggregate_names)

if outputs and aggregate_names:
_na_outputs = self._check_version_requirements(outputs)
na_names = aggregate_names.intersection(set(_na_outputs))
if na_names:
raise TypeError("""\
Output trait(s) %s not available in version %s of interface %s.\
""" % (', '.join(na_names), self.version, self.__class__.__name__))

for key in aggregate_names:
val = predicted_outputs[key]
try:
setattr(outputs, key, val)
except TraitError as error:
if 'an existing' in getattr(error, 'info', 'default'):
msg = "No such file or directory for output '%s' of a %s interface" % \
(key, self.__class__.__name__)
raise FileNotFoundError(val, message=msg)
raise error
return outputs

@property
Expand Down
Loading