Skip to content

Commit fcaafaf

Browse files
author
Jakub Kaczmarzyk
committed
Merge remote-tracking branch 'upstream/master' into enh/neurodocker
2 parents 2648de6 + d83b06d commit fcaafaf

File tree

23 files changed

+192
-67
lines changed

23 files changed

+192
-67
lines changed

.zenodo.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,9 @@
320320
"name": "Ghayoor, Ali"
321321
},
322322
{
323-
"name": "Liem, Franz"
323+
"affiliation": "University of Zurich",
324+
"name": "Liem, Franz",
325+
"orcid": "0000-0003-0646-4810"
324326
},
325327
{
326328
"name": "Millman, Jarrod"

doc/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
'sphinx.ext.autosummary',
5151
'numpydoc',
5252
'matplotlib.sphinxext.plot_directive',
53-
'matplotlib.sphinxext.only_directives',
53+
#'matplotlib.sphinxext.only_directives',
5454
'nipype.sphinxext.plot_workflow',
5555
#'IPython.sphinxext.ipython_directive',
5656
#'IPython.sphinxext.ipython_console_highlighting'

nipype/info.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def get_nipype_gitversion():
108108
PYTEST_MIN_VERSION = '3.0'
109109
FUTURE_MIN_VERSION = '0.16.0'
110110
SIMPLEJSON_MIN_VERSION = '3.8.0'
111-
PROV_VERSION = '1.5.0'
111+
PROV_VERSION = '1.5.2'
112112
CLICK_MIN_VERSION = '6.6.0'
113113
PYDOT_MIN_VERSION = '1.2.3'
114114

@@ -139,7 +139,8 @@ def get_nipype_gitversion():
139139
'traits>=%s' % TRAITS_MIN_VERSION,
140140
'future>=%s' % FUTURE_MIN_VERSION,
141141
'simplejson>=%s' % SIMPLEJSON_MIN_VERSION,
142-
'prov==%s' % PROV_VERSION,
142+
'prov>=%s' % PROV_VERSION,
143+
'neurdflib',
143144
'click>=%s' % CLICK_MIN_VERSION,
144145
'funcsigs',
145146
'pytest>=%s' % PYTEST_MIN_VERSION,

nipype/interfaces/afni/preprocess.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2618,6 +2618,14 @@ class TShiftInputSpec(AFNICommandInputSpec):
26182618
desc='time offsets from the volume acquisition onset for each slice',
26192619
argstr='-tpattern @%s',
26202620
xor=['tpattern'])
2621+
slice_encoding_direction = traits.Enum(
2622+
'k', 'k-',
2623+
usedefault=True,
2624+
desc='Direction in which slice_timing is specified (default: k). If negative,'
2625+
'slice_timing is defined in reverse order, that is, the first entry '
2626+
'corresponds to the slice with the largest index, and the final entry '
2627+
'corresponds to slice index zero. Only in effect when slice_timing is '
2628+
'passed as list, not when it is passed as file.',)
26212629
rlt = traits.Bool(
26222630
desc='Before shifting, remove the mean and linear trend',
26232631
argstr='-rlt')
@@ -2660,6 +2668,17 @@ class TShift(AFNICommand):
26602668
>>> tshift._list_outputs()['timing_file'] # doctest: +ELLIPSIS
26612669
'.../slice_timing.1D'
26622670
2671+
>>> np.loadtxt(tshift._list_outputs()['timing_file']).tolist()[:5]
2672+
[0.0, 0.4, 0.8, 1.2, 1.6]
2673+
2674+
If ``slice_encoding_direction`` is set to ``'k-'``, the slice timing is reversed:
2675+
2676+
>>> tshift.inputs.slice_encoding_direction = 'k-'
2677+
>>> tshift.cmdline
2678+
'3dTshift -prefix functional_tshift -tpattern @slice_timing.1D -TR 2.5s -tzero 0.0 functional.nii'
2679+
>>> np.loadtxt(tshift._list_outputs()['timing_file']).tolist()[:5]
2680+
[15.6, 15.2, 14.8, 14.4, 14.0]
2681+
26632682
This method creates a ``slice_timing.1D`` file to be passed to ``3dTshift``.
26642683
A pre-existing slice-timing file may be used in the same way:
26652684
@@ -2723,9 +2742,13 @@ def _format_arg(self, name, trait_spec, value):
27232742
return super(TShift, self)._format_arg(name, trait_spec, value)
27242743

27252744
def _write_slice_timing(self):
2745+
slice_timing = list(self.inputs.slice_timing)
2746+
if self.inputs.slice_encoding_direction.endswith("-"):
2747+
slice_timing.reverse()
2748+
27262749
fname = 'slice_timing.1D'
27272750
with open(fname, 'w') as fobj:
2728-
fobj.write('\t'.join(map(str, self.inputs.slice_timing)))
2751+
fobj.write('\t'.join(map(str, slice_timing)))
27292752
return fname
27302753

27312754
def _list_outputs(self):

nipype/interfaces/ants/segmentation.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ class LaplacianThicknessInputSpec(ANTSCommandInputSpec):
203203
desc='name of output file',
204204
argstr='%s',
205205
position=3,
206-
genfile=True,
206+
name_source=['input_wm'],
207+
name_template='%s_thickness',
208+
keep_extension=True,
207209
hash_files=False)
208210
smooth_param = traits.Float(argstr='smoothparam=%d', desc='', position=4)
209211
prior_thickness = traits.Float(
@@ -228,6 +230,9 @@ class LaplacianThickness(ANTSCommand):
228230
>>> cort_thick = LaplacianThickness()
229231
>>> cort_thick.inputs.input_wm = 'white_matter.nii.gz'
230232
>>> cort_thick.inputs.input_gm = 'gray_matter.nii.gz'
233+
>>> cort_thick.cmdline
234+
'LaplacianThickness white_matter.nii.gz gray_matter.nii.gz white_matter_thickness.nii.gz'
235+
231236
>>> cort_thick.inputs.output_image = 'output_thickness.nii.gz'
232237
>>> cort_thick.cmdline
233238
'LaplacianThickness white_matter.nii.gz gray_matter.nii.gz output_thickness.nii.gz'
@@ -238,22 +243,6 @@ class LaplacianThickness(ANTSCommand):
238243
input_spec = LaplacianThicknessInputSpec
239244
output_spec = LaplacianThicknessOutputSpec
240245

241-
def _gen_filename(self, name):
242-
if name == 'output_image':
243-
output = self.inputs.output_image
244-
if not isdefined(output):
245-
_, name, ext = split_filename(self.inputs.input_wm)
246-
output = name + '_thickness' + ext
247-
return output
248-
return None
249-
250-
def _list_outputs(self):
251-
outputs = self._outputs().get()
252-
_, name, ext = split_filename(os.path.abspath(self.inputs.input_wm))
253-
outputs['output_image'] = os.path.join(os.getcwd(), ''.join(
254-
(name, self.inputs.output_image, ext)))
255-
return outputs
256-
257246

258247
class N4BiasFieldCorrectionInputSpec(ANTSCommandInputSpec):
259248
dimension = traits.Enum(

nipype/interfaces/ants/tests/test_auto_LaplacianThickness.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ def test_LaplacianThickness_inputs():
3636
),
3737
output_image=dict(
3838
argstr='%s',
39-
genfile=True,
4039
hash_files=False,
40+
keep_extension=True,
41+
name_source=['input_wm'],
42+
name_template='%s_thickness',
4143
position=3,
4244
),
4345
prior_thickness=dict(

nipype/interfaces/base/specs.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ def get_hashval(self, hash_method=None):
234234
The md5 hash value of the traited spec
235235
236236
"""
237-
238237
list_withhash = []
239238
list_nofilename = []
240239
for name, val in sorted(self.trait_get().items()):
@@ -309,6 +308,10 @@ def _get_sorteddict(self,
309308
out = objekt
310309
return out
311310

311+
@property
312+
def __all__(self):
313+
return self.copyable_trait_names()
314+
312315

313316
class TraitedSpec(BaseTraitedSpec):
314317
""" Create a subclass with strict traits.

nipype/interfaces/base/tests/test_resource_monitor.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818
from ... import utility as niu
1919

2020
# Try to enable the resource monitor
21-
config.enable_resource_monitor()
2221
run_profile = config.resource_monitor
2322

2423

24+
@pytest.fixture(scope="module")
25+
def use_resource_monitor():
26+
config.enable_resource_monitor()
27+
yield
28+
config.disable_resource_monitor()
29+
30+
2531
class UseResourcesInputSpec(CommandLineInputSpec):
2632
mem_gb = traits.Float(
2733
desc='Number of GB of RAM to use', argstr='-g %f', mandatory=True)
@@ -51,7 +57,7 @@ class UseResources(CommandLine):
5157
os.getenv('CI_SKIP_TEST', False), reason='disabled in CI tests')
5258
@pytest.mark.parametrize("mem_gb,n_procs", [(0.5, 3), (2.2, 8), (0.8, 4),
5359
(1.5, 1)])
54-
def test_cmdline_profiling(tmpdir, mem_gb, n_procs):
60+
def test_cmdline_profiling(tmpdir, mem_gb, n_procs, use_resource_monitor):
5561
"""
5662
Test runtime profiler correctly records workflow RAM/CPUs consumption
5763
of a CommandLine-derived interface
@@ -73,7 +79,7 @@ def test_cmdline_profiling(tmpdir, mem_gb, n_procs):
7379
True, reason='test disabled temporarily, until funcion profiling works')
7480
@pytest.mark.parametrize("mem_gb,n_procs", [(0.5, 3), (2.2, 8), (0.8, 4),
7581
(1.5, 1)])
76-
def test_function_profiling(tmpdir, mem_gb, n_procs):
82+
def test_function_profiling(tmpdir, mem_gb, n_procs, use_resource_monitor):
7783
"""
7884
Test runtime profiler correctly records workflow RAM/CPUs consumption
7985
of a Function interface

nipype/interfaces/base/tests/test_specs.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
from ....utils.filemanip import split_filename
1212
from ... import base as nib
1313
from ...base import traits, Undefined
14+
from ....interfaces import fsl
15+
from ...utility.wrappers import Function
16+
from ....pipeline import Node
17+
1418

1519
standard_library.install_aliases()
1620

@@ -47,6 +51,20 @@ class spec(nib.TraitedSpec):
4751
assert infields.__repr__() == '\nfoo = 1\ngoo = 0.0\n'
4852

4953

54+
def test_TraitedSpec_tab_completion():
55+
bet_nd = Node(fsl.BET(), name='bet')
56+
bet_interface = fsl.BET()
57+
bet_inputs = bet_nd.inputs.class_editable_traits()
58+
bet_outputs = bet_nd.outputs.class_editable_traits()
59+
60+
# Check __all__ for bet node and interface inputs
61+
assert set(bet_nd.inputs.__all__) == set(bet_inputs)
62+
assert set(bet_interface.inputs.__all__) == set(bet_inputs)
63+
64+
# Check __all__ for bet node outputs
65+
assert set(bet_nd.outputs.__all__) == set(bet_outputs)
66+
67+
5068
@pytest.mark.skip
5169
def test_TraitedSpec_dynamic():
5270
from pickle import dumps, loads
@@ -63,6 +81,36 @@ def test_TraitedSpec_dynamic():
6381
assign_a_again
6482

6583

84+
def test_DynamicTraitedSpec_tab_completion():
85+
def extract_func(list_out):
86+
return list_out[0]
87+
88+
# Define interface
89+
func_interface = Function(input_names=["list_out"],
90+
output_names=["out_file", "another_file"],
91+
function=extract_func)
92+
# Define node
93+
list_extract = Node(Function(
94+
input_names=["list_out"], output_names=["out_file"],
95+
function=extract_func), name="list_extract")
96+
97+
# Check __all__ for interface inputs
98+
expected_input = set(list_extract.inputs.editable_traits())
99+
assert(set(func_interface.inputs.__all__) == expected_input)
100+
101+
# Check __all__ for node inputs
102+
assert(set(list_extract.inputs.__all__) == expected_input)
103+
104+
# Check __all__ for node outputs
105+
expected_output = set(list_extract.outputs.editable_traits())
106+
assert(set(list_extract.outputs.__all__) == expected_output)
107+
108+
# Add trait and retest
109+
list_extract._interface._output_names.append('added_out_trait')
110+
expected_output.add('added_out_trait')
111+
assert(set(list_extract.outputs.__all__) == expected_output)
112+
113+
66114
def test_TraitedSpec_logic():
67115
class spec3(nib.TraitedSpec):
68116
_xor_inputs = ('foo', 'bar')

nipype/interfaces/fsl/dti.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class DTIFitOutputSpec(TraitedSpec):
8585
'diffusion weighting'))
8686
tensor = File(
8787
exists=True, desc='path/name of file with the 4D tensor volume')
88+
sse = File(
89+
exists=True, desc='path/name of file with the summed squared error')
8890

8991

9092
class DTIFit(FSLCommand):
@@ -111,13 +113,21 @@ class DTIFit(FSLCommand):
111113
output_spec = DTIFitOutputSpec
112114

113115
def _list_outputs(self):
116+
keys_to_ignore = {'outputtype', 'environ', 'args'}
117+
# Optional output: Map output name to input flag
118+
opt_output = {'tensor': self.inputs.save_tensor,
119+
'sse': self.inputs.sse}
120+
# Ignore optional output, whose corresponding input-flag is not defined
121+
# or set to False
122+
for output, input_flag in opt_output.items():
123+
if isdefined(input_flag) and input_flag:
124+
# this is wanted output, do not ignore
125+
continue
126+
keys_to_ignore.add(output)
127+
114128
outputs = self.output_spec().get()
115-
for k in list(outputs.keys()):
116-
if k not in ('outputtype', 'environ', 'args'):
117-
if k != 'tensor' or (isdefined(self.inputs.save_tensor)
118-
and self.inputs.save_tensor):
119-
outputs[k] = self._gen_fname(
120-
self.inputs.base_name, suffix='_' + k)
129+
for k in set(outputs.keys()) - keys_to_ignore:
130+
outputs[k] = self._gen_fname(self.inputs.base_name, suffix='_' + k)
121131
return outputs
122132

123133

nipype/interfaces/fsl/tests/test_auto_DTIFit.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def test_DTIFit_outputs():
6666
V2=dict(),
6767
V3=dict(),
6868
tensor=dict(),
69+
sse=dict(),
6970
)
7071
outputs = DTIFit.output_spec()
7172

nipype/interfaces/fsl/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,9 @@ def _list_outputs(self):
551551
"""
552552
outputs = self._outputs().get()
553553
ext = Info.output_type_to_ext(self.inputs.output_type)
554-
outbase = 'vol*'
554+
outbase = 'vol[0-9]*'
555555
if isdefined(self.inputs.out_base_name):
556-
outbase = '%s*' % self.inputs.out_base_name
556+
outbase = '%s[0-9]*' % self.inputs.out_base_name
557557
outputs['out_files'] = sorted(
558558
glob(os.path.join(os.getcwd(), outbase + ext)))
559559
return outputs

nipype/interfaces/meshfix.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ class MeshFixInputSpec(CommandLineInputSpec):
3838
dont_clean = traits.Bool(argstr='--no-clean', desc="Don't Clean")
3939

4040
save_as_stl = traits.Bool(
41-
xor=['save_as_vmrl', 'save_as_freesurfer_mesh'],
41+
xor=['save_as_vrml', 'save_as_freesurfer_mesh'],
4242
argstr='--stl',
4343
desc="Result is saved in stereolithographic format (.stl)")
44-
save_as_vmrl = traits.Bool(
44+
save_as_vrml = traits.Bool(
4545
argstr='--wrl',
4646
xor=['save_as_stl', 'save_as_freesurfer_mesh'],
4747
desc="Result is saved in VRML1.0 format (.wrl)")
@@ -210,7 +210,7 @@ def _gen_outfilename(self):
210210
if self.inputs.save_as_stl or self.inputs.output_type == 'stl':
211211
self.inputs.output_type = 'stl'
212212
self.inputs.save_as_stl = True
213-
if self.inputs.save_as_vmrl or self.inputs.output_type == 'vmrl':
214-
self.inputs.output_type = 'vmrl'
215-
self.inputs.save_as_vmrl = True
213+
if self.inputs.save_as_vrml or self.inputs.output_type == 'vrml':
214+
self.inputs.output_type = 'vrml'
215+
self.inputs.save_as_vrml = True
216216
return name + '_fixed.' + self.inputs.output_type

nipype/interfaces/mrtrix3/base.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,39 @@
44
from __future__ import (print_function, division, unicode_literals,
55
absolute_import)
66

7-
from ... import logging
8-
from ..base import (CommandLineInputSpec, CommandLine, traits, File, isdefined)
7+
from ... import logging, LooseVersion
8+
from ...utils.filemanip import which
9+
from ..base import (CommandLineInputSpec, CommandLine, traits, File, isdefined, PackageInfo)
910
iflogger = logging.getLogger('nipype.interface')
1011

1112

13+
class Info(PackageInfo):
14+
version_cmd = 'mrconvert --version'
15+
16+
@staticmethod
17+
def parse_version(raw_info):
18+
# info is like: "== mrconvert 0.3.15-githash"
19+
for line in raw_info.splitlines():
20+
if line.startswith('== mrconvert '):
21+
v_string = line.split()[2]
22+
break
23+
else:
24+
return None
25+
26+
# -githash may or may not be appended
27+
v_string = v_string.split('-')[0]
28+
29+
return '.'.join(v_string.split('.')[:3])
30+
31+
@classmethod
32+
def looseversion(cls):
33+
""" Return a comparable version object
34+
35+
If no version found, use LooseVersion('0.0.0')
36+
"""
37+
return LooseVersion(cls.version() or '0.0.0')
38+
39+
1240
class MRTrix3BaseInputSpec(CommandLineInputSpec):
1341
nthreads = traits.Int(
1442
argstr='-nthreads %d',
@@ -78,3 +106,7 @@ def _parse_inputs(self, skip=None):
78106
pass
79107

80108
return super(MRTrix3Base, self)._parse_inputs(skip=skip)
109+
110+
@property
111+
def version(self):
112+
return Info.version()

0 commit comments

Comments
 (0)