Skip to content

Commit 97ce446

Browse files
committed
Merge remote-tracking branch 'upstream/maint/1.5.x'
2 parents f2a2034 + 1d6b91e commit 97ce446

23 files changed

+227
-131
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ install:
7171
script:
7272
- |
7373
if [ "$CHECK_TYPE" = "test" ]; then
74-
py.test -v --cov nipype --cov-config .coveragerc --cov-report xml:cov.xml -c nipype/pytest.ini --doctest-modules nipype -n auto
74+
py.test -sv --cov nipype --cov-config .coveragerc --cov-report xml:cov.xml -c nipype/pytest.ini --doctest-modules nipype -n 2
7575
fi
7676
- |
7777
if [ "$CHECK_TYPE" = "specs" ]; then

.zenodo.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -115,16 +115,16 @@
115115
"name": "Hamalainen, Carlo",
116116
"orcid": "0000-0001-7655-3830"
117117
},
118-
{
119-
"affiliation": "Stanford University",
120-
"name": "\u0106iri\u0107 , Rastko",
121-
"orcid": "0000-0001-6347-7939"
122-
},
123118
{
124119
"affiliation": "Institute for Biomedical Engineering, ETH and University of Zurich",
125120
"name": "Christian, Horea",
126121
"orcid": "0000-0001-7037-2449"
127122
},
123+
{
124+
"affiliation": "Stanford University",
125+
"name": "\u0106iri\u0107 , Rastko",
126+
"orcid": "0000-0001-6347-7939"
127+
},
128128
{
129129
"name": "Dubois, Mathieu"
130130
},
@@ -225,6 +225,11 @@
225225
{
226226
"name": "Millman, Jarrod"
227227
},
228+
{
229+
"affiliation": "University College London",
230+
"name": "Mancini, Matteo",
231+
"orcid": "0000-0001-7194-4568"
232+
},
228233
{
229234
"affiliation": "National Institute of Mental Health",
230235
"name": "Nielson, Dylan M.",
@@ -241,11 +246,6 @@
241246
{
242247
"name": "Mordom, David"
243248
},
244-
{
245-
"affiliation": "University College London",
246-
"name": "Mancini, Matteo",
247-
"orcid": "0000-0001-7194-4568"
248-
},
249249
{
250250
"affiliation": "ARAMIS LAB, Brain and Spine Institute (ICM), Paris, France.",
251251
"name": "Guillon, Je\u0301re\u0301my",

doc/changelog/1.X.X-changelog.rst

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
1+
1.5.1 (August 16, 2020)
2+
=======================
3+
4+
Bug-fix release in the 1.5.x series.
5+
6+
This release includes small updates to ANTs utilities that lie somewhere
7+
between bug fixes and enhancements.
8+
9+
(`Full changelog <https://github.com/nipy/nipype/milestone/1.5.1?closed=1>`__)
10+
11+
* FIX: Warn for min/max_ver traits when tool version can't be parsed (https://github.com/nipy/nipype/pull/3241)
12+
* FIX: Serialize all interface arguments when exporting workflows (https://github.com/nipy/nipype/pull/3240)
13+
* FIX: Permit identity transforms in list of transforms given to ants.ApplyTransforms (https://github.com/nipy/nipype/pull/3237)
14+
* FIX: ANTs' utilities revision - bug fixes and add more operations to ``ants.ImageMath`` (https://github.com/nipy/nipype/pull/3236)
15+
* DOC: Skip BIDSDataGrabber doctest if pybids is missing (https://github.com/nipy/nipype/pull/3224)
16+
117
1.5.0 (June 03, 2020)
2-
=========================
18+
=====================
319

420
New feature release in the 1.5.x series.
521

doc/interfaces.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Interfaces and Workflows
88
:Release: |version|
99
:Date: |today|
1010

11-
Previous versions: `1.4.2 <http://nipype.readthedocs.io/en/1.4.2/>`_ `1.4.1 <http://nipype.readthedocs.io/en/1.4.1/>`_
11+
Previous versions: `1.5.1 <http://nipype.readthedocs.io/en/1.5.1/>`_ `1.5.0 <http://nipype.readthedocs.io/en/1.5.0/>`_
1212

1313
Workflows
1414
---------

nipype/conftest.py

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
def add_np(doctest_namespace):
1818
doctest_namespace["np"] = numpy
1919
doctest_namespace["os"] = os
20+
doctest_namespace["pytest"] = pytest
2021
doctest_namespace["datadir"] = data_dir
2122

2223

nipype/interfaces/ants/registration.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,8 @@ class RegistrationOutputSpec(TraitedSpec):
592592
File(exists=True), desc="List of output transforms for forward registration"
593593
)
594594
reverse_forward_transforms = traits.List(
595-
File(exists=True), desc="List of output transforms for forward registration reversed for antsApplyTransform"
595+
File(exists=True),
596+
desc="List of output transforms for forward registration reversed for antsApplyTransform",
596597
)
597598
reverse_transforms = traits.List(
598599
File(exists=True), desc="List of output transforms for reverse registration"
@@ -601,7 +602,8 @@ class RegistrationOutputSpec(TraitedSpec):
601602
traits.Bool(), desc="List of flags corresponding to the forward transforms"
602603
)
603604
reverse_forward_invert_flags = traits.List(
604-
traits.Bool(), desc="List of flags corresponding to the forward transforms reversed for antsApplyTransform"
605+
traits.Bool(),
606+
desc="List of flags corresponding to the forward transforms reversed for antsApplyTransform",
605607
)
606608
reverse_invert_flags = traits.List(
607609
traits.Bool(), desc="List of flags corresponding to the reverse transforms"

nipype/interfaces/ants/resampling.py

+39-31
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55

66
from .base import ANTSCommand, ANTSCommandInputSpec
7-
from ..base import TraitedSpec, File, traits, isdefined, InputMultiPath
7+
from ..base import TraitedSpec, File, traits, isdefined, InputMultiObject
88
from ...utils.filemanip import split_filename
99

1010

@@ -52,7 +52,7 @@ class WarpTimeSeriesImageMultiTransformInputSpec(ANTSCommandInputSpec):
5252
use_bspline = traits.Bool(
5353
argstr="--use-Bspline", desc="Use 3rd order B-Spline interpolation"
5454
)
55-
transformation_series = InputMultiPath(
55+
transformation_series = InputMultiObject(
5656
File(exists=True),
5757
argstr="%s",
5858
desc="transformation file(s) to be applied",
@@ -204,7 +204,7 @@ class WarpImageMultiTransformInputSpec(ANTSCommandInputSpec):
204204
use_bspline = traits.Bool(
205205
argstr="--use-BSpline", desc="Use 3rd order B-Spline interpolation"
206206
)
207-
transformation_series = InputMultiPath(
207+
transformation_series = InputMultiObject(
208208
File(exists=True),
209209
argstr="%s",
210210
desc="transformation file(s) to be applied",
@@ -369,15 +369,14 @@ class ApplyTransformsInputSpec(ANTSCommandInputSpec):
369369
traits.Float(), traits.Float() # Gaussian/MultiLabel (sigma, alpha)
370370
),
371371
)
372-
transforms = traits.Either(
373-
InputMultiPath(File(exists=True)),
374-
"identity",
372+
transforms = InputMultiObject(
373+
traits.Either(File(exists=True), "identity"),
375374
argstr="%s",
376375
mandatory=True,
377376
desc="transform files: will be applied in reverse order. For "
378377
"example, the last specified transform will be applied first.",
379378
)
380-
invert_transform_flags = InputMultiPath(traits.Bool())
379+
invert_transform_flags = InputMultiObject(traits.Bool())
381380
default_value = traits.Float(0.0, argstr="--default-value %g", usedefault=True)
382381
print_out_composite_warp_file = traits.Bool(
383382
False,
@@ -411,7 +410,7 @@ class ApplyTransforms(ANTSCommand):
411410
>>> at.cmdline
412411
'antsApplyTransforms --default-value 0 --float 0 --input moving1.nii \
413412
--interpolation Linear --output moving1_trans.nii \
414-
--reference-image fixed1.nii -t identity'
413+
--reference-image fixed1.nii --transform identity'
415414
416415
>>> at = ApplyTransforms()
417416
>>> at.inputs.dimension = 3
@@ -421,11 +420,11 @@ class ApplyTransforms(ANTSCommand):
421420
>>> at.inputs.interpolation = 'Linear'
422421
>>> at.inputs.default_value = 0
423422
>>> at.inputs.transforms = ['ants_Warp.nii.gz', 'trans.mat']
424-
>>> at.inputs.invert_transform_flags = [False, False]
423+
>>> at.inputs.invert_transform_flags = [False, True]
425424
>>> at.cmdline
426425
'antsApplyTransforms --default-value 0 --dimensionality 3 --float 0 --input moving1.nii \
427426
--interpolation Linear --output deformed_moving1.nii --reference-image fixed1.nii \
428-
--transform [ ants_Warp.nii.gz, 0 ] --transform [ trans.mat, 0 ]'
427+
--transform ants_Warp.nii.gz --transform [ trans.mat, 1 ]'
429428
430429
>>> at1 = ApplyTransforms()
431430
>>> at1.inputs.dimension = 3
@@ -440,7 +439,23 @@ class ApplyTransforms(ANTSCommand):
440439
>>> at1.cmdline
441440
'antsApplyTransforms --default-value 0 --dimensionality 3 --float 0 --input moving1.nii \
442441
--interpolation BSpline[ 5 ] --output deformed_moving1.nii --reference-image fixed1.nii \
443-
--transform [ ants_Warp.nii.gz, 0 ] --transform [ trans.mat, 0 ]'
442+
--transform ants_Warp.nii.gz --transform trans.mat'
443+
444+
Identity transforms may be used as part of a chain:
445+
446+
>>> at2 = ApplyTransforms()
447+
>>> at2.inputs.dimension = 3
448+
>>> at2.inputs.input_image = 'moving1.nii'
449+
>>> at2.inputs.reference_image = 'fixed1.nii'
450+
>>> at2.inputs.output_image = 'deformed_moving1.nii'
451+
>>> at2.inputs.interpolation = 'BSpline'
452+
>>> at2.inputs.interpolation_parameters = (5,)
453+
>>> at2.inputs.default_value = 0
454+
>>> at2.inputs.transforms = ['identity', 'ants_Warp.nii.gz', 'trans.mat']
455+
>>> at2.cmdline
456+
'antsApplyTransforms --default-value 0 --dimensionality 3 --float 0 --input moving1.nii \
457+
--interpolation BSpline[ 5 ] --output deformed_moving1.nii --reference-image fixed1.nii \
458+
--transform identity --transform ants_Warp.nii.gz --transform trans.mat'
444459
"""
445460

446461
_cmd = "antsApplyTransforms"
@@ -458,25 +473,20 @@ def _gen_filename(self, name):
458473

459474
def _get_transform_filenames(self):
460475
retval = []
461-
for ii in range(len(self.inputs.transforms)):
462-
if isdefined(self.inputs.invert_transform_flags):
463-
if len(self.inputs.transforms) == len(
464-
self.inputs.invert_transform_flags
465-
):
466-
invert_code = 1 if self.inputs.invert_transform_flags[ii] else 0
467-
retval.append(
468-
"--transform [ %s, %d ]"
469-
% (self.inputs.transforms[ii], invert_code)
470-
)
471-
else:
472-
raise Exception(
473-
(
474-
"ERROR: The useInverse list must have the same number "
475-
"of entries as the transformsFileName list."
476-
)
477-
)
476+
invert_flags = self.inputs.invert_transform_flags
477+
if not isdefined(invert_flags):
478+
invert_flags = [False] * len(self.inputs.transforms)
479+
elif len(self.inputs.transforms) != len(invert_flags):
480+
raise ValueError(
481+
"ERROR: The invert_transform_flags list must have the same number "
482+
"of entries as the transforms list."
483+
)
484+
485+
for transform, invert in zip(self.inputs.transforms, invert_flags):
486+
if invert:
487+
retval.append(f"--transform [ {transform}, 1 ]")
478488
else:
479-
retval.append("--transform %s" % self.inputs.transforms[ii])
489+
retval.append(f"--transform {transform}")
480490
return " ".join(retval)
481491

482492
def _get_output_warped_filename(self):
@@ -492,8 +502,6 @@ def _format_arg(self, opt, spec, val):
492502
if opt == "output_image":
493503
return self._get_output_warped_filename()
494504
elif opt == "transforms":
495-
if val == "identity":
496-
return "-t identity"
497505
return self._get_transform_filenames()
498506
elif opt == "interpolation":
499507
if self.inputs.interpolation in [

nipype/interfaces/ants/segmentation.py

+1
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ def _list_outputs(self):
543543
outputs["bias_image"] = os.path.abspath(self._out_bias_file)
544544
return outputs
545545

546+
546547
class CorticalThicknessInputSpec(ANTSCommandInputSpec):
547548
dimension = traits.Enum(
548549
3, 2, argstr="-d %d", usedefault=True, desc="image dimension (2 or 3)"

nipype/interfaces/ants/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -138,18 +138,18 @@ class ImageMath(ANTSCommand, CopyHeaderInterface):
138138

139139
def __init__(self, **inputs):
140140
super(ImageMath, self).__init__(**inputs)
141-
if self.inputs.operation in ("PadImage", ):
141+
if self.inputs.operation in ("PadImage",):
142142
self.inputs.copy_header = False
143143

144144
self.inputs.on_trait_change(self._operation_update, "operation")
145145
self.inputs.on_trait_change(self._copyheader_update, "copy_header")
146146

147147
def _operation_update(self):
148-
if self.inputs.operation in ("PadImage", ):
148+
if self.inputs.operation in ("PadImage",):
149149
self.inputs.copy_header = False
150150

151151
def _copyheader_update(self):
152-
if self.inputs.copy_header and self.inputs.operation in ("PadImage", ):
152+
if self.inputs.copy_header and self.inputs.operation in ("PadImage",):
153153
warn("copy_header cannot be updated to True with PadImage as operation.")
154154
self.inputs.copy_header = False
155155

nipype/interfaces/base/core.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,18 @@ def _check_version_requirements(self, trait_object, raise_exception=True):
276276
version = LooseVersion(str(self.version))
277277
for name in names:
278278
min_ver = LooseVersion(str(trait_object.traits()[name].min_ver))
279-
if min_ver > version:
279+
try:
280+
too_old = min_ver > version
281+
except TypeError as err:
282+
msg = (
283+
f"Nipype cannot validate the package version {version!r} for "
284+
f"{self.__class__.__name__}. Trait {name} requires version >={min_ver}."
285+
)
286+
iflogger.warning(f"{msg}. Please verify validity.")
287+
if config.getboolean("execution", "stop_on_unknown_version"):
288+
raise ValueError(msg) from err
289+
continue
290+
if too_old:
280291
unavailable_traits.append(name)
281292
if not isdefined(getattr(trait_object, name)):
282293
continue
@@ -293,7 +304,18 @@ def _check_version_requirements(self, trait_object, raise_exception=True):
293304
version = LooseVersion(str(self.version))
294305
for name in names:
295306
max_ver = LooseVersion(str(trait_object.traits()[name].max_ver))
296-
if max_ver < version:
307+
try:
308+
too_new = max_ver < version
309+
except TypeError as err:
310+
msg = (
311+
f"Nipype cannot validate the package version {version!r} for "
312+
f"{self.__class__.__name__}. Trait {name} requires version <={max_ver}."
313+
)
314+
iflogger.warning(f"{msg}. Please verify validity.")
315+
if config.getboolean("execution", "stop_on_unknown_version"):
316+
raise ValueError(msg) from err
317+
continue
318+
if too_new:
297319
unavailable_traits.append(name)
298320
if not isdefined(getattr(trait_object, name)):
299321
continue

nipype/interfaces/base/tests/test_core.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# vi: set ft=python sts=4 ts=4 sw=4 et:
44
import os
55
import simplejson as json
6+
import logging
67

78
import pytest
89
from unittest import mock
@@ -236,6 +237,41 @@ class DerivedInterface1(nib.BaseInterface):
236237
obj._check_version_requirements(obj.inputs)
237238

238239

240+
def test_input_version_missing(caplog):
241+
class DerivedInterface(nib.BaseInterface):
242+
class input_spec(nib.TraitedSpec):
243+
foo = nib.traits.Int(min_ver="0.9")
244+
bar = nib.traits.Int(max_ver="0.9")
245+
246+
_version = "misparsed-garbage"
247+
248+
obj = DerivedInterface()
249+
obj.inputs.foo = 1
250+
obj.inputs.bar = 1
251+
with caplog.at_level(logging.WARNING, logger="nipype.interface"):
252+
obj._check_version_requirements(obj.inputs)
253+
assert len(caplog.records) == 2
254+
255+
256+
def test_input_version_missing_error():
257+
from nipype import config
258+
259+
class DerivedInterface(nib.BaseInterface):
260+
class input_spec(nib.TraitedSpec):
261+
foo = nib.traits.Int(min_ver="0.9")
262+
bar = nib.traits.Int(max_ver="0.9")
263+
264+
_version = "misparsed-garbage"
265+
266+
with mock.patch.object(config, "getboolean", return_value=True):
267+
obj = DerivedInterface(foo=1)
268+
with pytest.raises(ValueError):
269+
obj._check_version_requirements(obj.inputs)
270+
obj = DerivedInterface(bar=1)
271+
with pytest.raises(ValueError):
272+
obj._check_version_requirements(obj.inputs)
273+
274+
239275
def test_output_version():
240276
class InputSpec(nib.TraitedSpec):
241277
foo = nib.traits.Int(desc="a random int")
@@ -457,7 +493,7 @@ def test_global_CommandLine_output(tmpdir):
457493
ci = BET()
458494
assert ci.terminal_output == "stream" # default case
459495

460-
with mock.patch.object(nib.CommandLine, '_terminal_output'):
496+
with mock.patch.object(nib.CommandLine, "_terminal_output"):
461497
nib.CommandLine.set_default_terminal_output("allatonce")
462498
ci = nib.CommandLine(command="ls -l")
463499
assert ci.terminal_output == "allatonce"

nipype/interfaces/freesurfer/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def looseversion(cls):
7676
vstr = "6.0.0-dev" + githash
7777
elif vinfo[5][0] == "v":
7878
vstr = vinfo[5][1:]
79-
elif len([1 for val in vinfo[3] if val == '.']) == 2:
79+
elif len([1 for val in vinfo[3] if val == "."]) == 2:
8080
"version string: freesurfer-linux-centos7_x86_64-7.1.0-20200511-813297b"
8181
vstr = vinfo[3]
8282
else:

0 commit comments

Comments
 (0)