Skip to content

Commit 569f8ee

Browse files
committed
Merge remote-tracking branch 'upstream/master' into maint/strip-help-from-interfaces
2 parents 1512cc3 + 11f8eb2 commit 569f8ee

File tree

7 files changed

+90
-45
lines changed

7 files changed

+90
-45
lines changed

nipype/interfaces/base/core.py

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939

4040
from .traits_extension import traits, isdefined, TraitError
4141
from .specs import (BaseInterfaceInputSpec, CommandLineInputSpec,
42-
StdOutCommandLineInputSpec, MpiCommandLineInputSpec)
42+
StdOutCommandLineInputSpec, MpiCommandLineInputSpec,
43+
get_filecopy_info)
44+
4345
from .support import (Bunch, InterfaceResult, NipypeInterfaceError,
4446
format_help)
4547

@@ -116,11 +118,15 @@ def _list_outputs(self):
116118
""" List expected outputs"""
117119
raise NotImplementedError
118120

119-
def _get_filecopy_info(self):
120-
""" Provides information about file inputs to copy or link to cwd.
121-
Necessary for pipeline operation
121+
@classmethod
122+
def _get_filecopy_info(cls):
123+
"""Provides information about file inputs to copy or link to cwd.
124+
Necessary for pipeline operation
122125
"""
123-
raise NotImplementedError
126+
iflogger.warning(
127+
'_get_filecopy_info member of Interface was deprecated '
128+
'in nipype-1.1.6 and will be removed in 1.2.0')
129+
return get_filecopy_info(cls)
124130

125131

126132
class BaseInterface(Interface):
@@ -188,19 +194,6 @@ def _outputs(self):
188194

189195
return outputs
190196

191-
@classmethod
192-
def _get_filecopy_info(cls):
193-
""" Provides information about file inputs to copy or link to cwd.
194-
Necessary for pipeline operation
195-
"""
196-
info = []
197-
if cls.input_spec is None:
198-
return info
199-
metadata = dict(copyfile=lambda t: t is not None)
200-
for name, spec in sorted(cls.input_spec().traits(**metadata).items()):
201-
info.append(dict(key=name, copy=spec.copyfile))
202-
return info
203-
204197
def _check_requires(self, spec, name, value):
205198
""" check if required inputs are satisfied
206199
"""

nipype/interfaces/base/specs.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
absolute_import)
1414

1515
import os
16+
from inspect import isclass
1617
from copy import deepcopy
1718
from warnings import warn
1819
from builtins import str, bytes
@@ -375,3 +376,21 @@ class MpiCommandLineInputSpec(CommandLineInputSpec):
375376
n_procs = traits.Int(desc="Num processors to specify to mpiexec. Do not "
376377
"specify if this is managed externally (e.g. through "
377378
"SGE)")
379+
380+
381+
def get_filecopy_info(cls):
382+
"""Provides information about file inputs to copy or link to cwd.
383+
Necessary for pipeline operation
384+
"""
385+
if cls.input_spec is None:
386+
return None
387+
388+
# normalize_filenames is not a classmethod, hence check first
389+
if not isclass(cls) and hasattr(cls, 'normalize_filenames'):
390+
cls.normalize_filenames()
391+
info = []
392+
inputs = cls.input_spec() if isclass(cls) else cls.inputs
393+
metadata = dict(copyfile=lambda t: t is not None)
394+
for name, spec in sorted(inputs.traits(**metadata).items()):
395+
info.append(dict(key=name, copy=spec.copyfile))
396+
return info

nipype/interfaces/base/tests/test_core.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,12 @@ def __init__(self):
5555
nif.aggregate_outputs()
5656
with pytest.raises(NotImplementedError):
5757
nif._list_outputs()
58-
with pytest.raises(NotImplementedError):
59-
nif._get_filecopy_info()
6058

6159

6260
def test_BaseInterface():
6361
config.set('monitoring', 'enable', '0')
6462

6563
assert nib.BaseInterface.help() is None
66-
assert nib.BaseInterface._get_filecopy_info() == []
6764

6865
class InputSpec(nib.TraitedSpec):
6966
foo = nib.traits.Int(desc='a random int')
@@ -83,10 +80,6 @@ class DerivedInterface(nib.BaseInterface):
8380
assert DerivedInterface.help() is None
8481
assert 'moo' in ''.join(_inputs_help(DerivedInterface))
8582
assert DerivedInterface()._outputs() is None
86-
assert DerivedInterface._get_filecopy_info()[0]['key'] == 'woo'
87-
assert DerivedInterface._get_filecopy_info()[0]['copy']
88-
assert DerivedInterface._get_filecopy_info()[1]['key'] == 'zoo'
89-
assert not DerivedInterface._get_filecopy_info()[1]['copy']
9083
assert DerivedInterface().inputs.foo == nib.Undefined
9184
with pytest.raises(ValueError):
9285
DerivedInterface()._check_mandatory_inputs()

nipype/interfaces/base/tests/test_specs.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ....interfaces import fsl
1515
from ...utility.wrappers import Function
1616
from ....pipeline import Node
17-
17+
from ..specs import get_filecopy_info
1818

1919
standard_library.install_aliases()
2020

@@ -55,8 +55,8 @@ def test_TraitedSpec_tab_completion():
5555
bet_nd = Node(fsl.BET(), name='bet')
5656
bet_interface = fsl.BET()
5757
bet_inputs = bet_nd.inputs.class_editable_traits()
58-
bet_outputs = bet_nd.outputs.class_editable_traits()
59-
58+
bet_outputs = bet_nd.outputs.class_editable_traits()
59+
6060
# Check __all__ for bet node and interface inputs
6161
assert set(bet_nd.inputs.__all__) == set(bet_inputs)
6262
assert set(bet_interface.inputs.__all__) == set(bet_inputs)
@@ -433,3 +433,45 @@ def test_ImageFile():
433433
with pytest.raises(nib.TraitError):
434434
x.nocompress = 'test.nii.gz'
435435
x.nocompress = 'test.mgh'
436+
437+
438+
def test_filecopy_info():
439+
class InputSpec(nib.TraitedSpec):
440+
foo = nib.traits.Int(desc='a random int')
441+
goo = nib.traits.Int(desc='a random int', mandatory=True)
442+
moo = nib.traits.Int(desc='a random int', mandatory=False)
443+
hoo = nib.traits.Int(desc='a random int', usedefault=True)
444+
zoo = nib.File(desc='a file', copyfile=False)
445+
woo = nib.File(desc='a file', copyfile=True)
446+
447+
class DerivedInterface(nib.BaseInterface):
448+
input_spec = InputSpec
449+
resource_monitor = False
450+
451+
def normalize_filenames(self):
452+
"""A mock normalize_filenames for freesurfer interfaces that have one"""
453+
self.inputs.zoo = 'normalized_filename.ext'
454+
455+
assert get_filecopy_info(nib.BaseInterface) == []
456+
457+
# Test on interface class, not instantiated
458+
info = get_filecopy_info(DerivedInterface)
459+
assert info[0]['key'] == 'woo'
460+
assert info[0]['copy']
461+
assert info[1]['key'] == 'zoo'
462+
assert not info[1]['copy']
463+
info = None
464+
465+
# Test with instantiated interface
466+
derived = DerivedInterface()
467+
# First check that zoo is not defined
468+
assert derived.inputs.zoo == Undefined
469+
# After the first call to get_filecopy_info zoo is defined
470+
info = get_filecopy_info(derived)
471+
# Ensure that normalize_filenames was called
472+
assert derived.inputs.zoo == 'normalized_filename.ext'
473+
# Check the results are consistent
474+
assert info[0]['key'] == 'woo'
475+
assert info[0]['copy']
476+
assert info[1]['key'] == 'zoo'
477+
assert not info[1]['copy']

nipype/interfaces/freesurfer/base.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,6 @@ class FSSurfaceCommand(FSCommand):
189189
including the full path in the filename, we can also avoid this behavior.
190190
"""
191191

192-
def _get_filecopy_info(self):
193-
self._normalize_filenames()
194-
return super(FSSurfaceCommand, self)._get_filecopy_info()
195-
196-
def _normalize_filenames(self):
197-
"""Filename normalization routine to perform only when run in Node
198-
context
199-
"""
200-
pass
201-
202192
@staticmethod
203193
def _associated_file(in_file, out_name):
204194
"""Based on MRIsBuildFileName in freesurfer/utils/mrisurf.c

nipype/interfaces/freesurfer/utils.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,9 +1289,11 @@ def _list_outputs(self):
12891289

12901290
return outputs
12911291

1292-
def _normalize_filenames(self):
1293-
""" In a Node context, interpret out_file as a literal path to
1294-
reduce surprise.
1292+
def normalize_filenames(self):
1293+
"""
1294+
Filename normalization routine to perform only when run in Node
1295+
context.
1296+
Interpret out_file as a literal path to reduce surprise.
12951297
"""
12961298
if isdefined(self.inputs.out_file):
12971299
self.inputs.out_file = os.path.abspath(self.inputs.out_file)
@@ -3837,8 +3839,11 @@ def _list_outputs(self):
38373839
self.inputs.out_name)
38383840
return outputs
38393841

3840-
def _normalize_filenames(self):
3841-
""" Find full paths for pial, thickness and sphere files for copying
3842+
def normalize_filenames(self):
3843+
"""
3844+
Filename normalization routine to perform only when run in Node
3845+
context.
3846+
Find full paths for pial, thickness and sphere files for copying.
38423847
"""
38433848
in_file = self.inputs.in_file
38443849

nipype/pipeline/engine/nodes.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from ...interfaces.base import (traits, InputMultiPath, CommandLine, Undefined,
3333
DynamicTraitedSpec, Bunch, InterfaceResult,
3434
Interface, isdefined)
35+
from ...interfaces.base.specs import get_filecopy_info
36+
3537
from .utils import (
3638
_parameterization_dir, save_hashfile as _save_hashfile, load_resultfile as
3739
_load_resultfile, save_resultfile as _save_resultfile, nodelist_runner as
@@ -656,7 +658,8 @@ def _run_command(self, execute, copyfiles=True):
656658

657659
def _copyfiles_to_wd(self, execute=True, linksonly=False):
658660
"""copy files over and change the inputs"""
659-
if not hasattr(self._interface, '_get_filecopy_info'):
661+
filecopy_info = get_filecopy_info(self.interface)
662+
if not filecopy_info:
660663
# Nothing to be done
661664
return
662665

@@ -669,7 +672,7 @@ def _copyfiles_to_wd(self, execute=True, linksonly=False):
669672
outdir = op.join(outdir, '_tempinput')
670673
makedirs(outdir, exist_ok=True)
671674

672-
for info in self._interface._get_filecopy_info():
675+
for info in filecopy_info:
673676
files = self.inputs.trait_get().get(info['key'])
674677
if not isdefined(files) or not files:
675678
continue

0 commit comments

Comments
 (0)