From ca5cae3c48b8ca41d41bdc0063905564fa531dfc Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 16 Jul 2019 14:55:28 -0700 Subject: [PATCH 01/13] ENH: Modify ``Directory`` and ``File`` traits to get along with pathlib Closes #2959 --- nipype/interfaces/base/traits_extension.py | 9 ++++++--- nipype/interfaces/spm/base.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index de215beb96..fdb79f93bb 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -38,6 +38,12 @@ if USING_PATHLIB2: from future.types.newstr import newstr +try: + from pathlib import Path +except ImportError: + from pathlib2 import Path + + if traits_version < '3.7.0': raise ImportError('Traits version 3.7.0 or higher must be installed') @@ -153,7 +159,6 @@ def validate(self, objekt, name, value, return_pathlike=False): return value - class Directory(BasePath): """ Defines a trait whose value must be a directory path. @@ -194,14 +199,12 @@ class Directory(BasePath): >>> a.foo 'relative_dir' - >>> class A(TraitedSpec): ... foo = Directory('tmpdir') >>> a = A() >>> a.foo # doctest: +ELLIPSIS - >>> class A(TraitedSpec): ... foo = Directory('tmpdir', usedefault=True) >>> a = A() diff --git a/nipype/interfaces/spm/base.py b/nipype/interfaces/spm/base.py index fda02d40f1..08a31d8185 100644 --- a/nipype/interfaces/spm/base.py +++ b/nipype/interfaces/spm/base.py @@ -605,3 +605,4 @@ def __init__(self, value=NoDefaultSpecified, exists=False, resolve=False, **meta super(ImageFileSPM, self).__init__( value=value, exists=exists, types=['nifti1', 'nifti2'], allow_compressed=False, resolve=resolve, **metadata) + From 5af9dc309c1716b548ad8aee55a776fd75f1b3e0 Mon Sep 17 00:00:00 2001 From: oesteban Date: Thu, 18 Jul 2019 22:40:40 -0700 Subject: [PATCH 02/13] ENH: Add resolve/rebase ``BasePath`` traits methods & tests Two new methods ``resolve_path_traits`` and ``rebase_path_traits`` are being included. They take trait instances from a spec (selected via ``spec.trait('traitname')``, the value and a base path. These two functions will be usefull to progress towards #2944. --- .../base/tests/test_traits_extension.py | 152 ++++++++++++++++++ nipype/interfaces/base/traits_extension.py | 123 ++++++++++++++ 2 files changed, 275 insertions(+) create mode 100644 nipype/interfaces/base/tests/test_traits_extension.py diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py new file mode 100644 index 0000000000..4a2b884921 --- /dev/null +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- +# vi: set ft=python sts=4 ts=4 sw=4 et: +from __future__ import print_function, unicode_literals + +from ... import base as nib +from ..traits_extension import rebase_path_traits, resolve_path_traits, Path + + +class _test_spec(nib.TraitedSpec): + a = nib.traits.File() + b = nib.traits.Tuple(nib.File(), + nib.File()) + c = nib.traits.List(nib.File()) + d = nib.traits.Either(nib.File(), nib.traits.Float()) + e = nib.OutputMultiObject(nib.File()) + f = nib.traits.Dict(nib.Str, nib.File()) + g = nib.traits.Either(nib.File, nib.Str) + h = nib.Str + ee = nib.OutputMultiObject(nib.Str) + + +def test_rebase_path_traits(): + """Check rebase_path_traits.""" + spec = _test_spec() + + a = rebase_path_traits( + spec.trait('a'), '/some/path/f1.txt', '/some/path') + assert '%s' % a == 'f1.txt' + + b = rebase_path_traits( + spec.trait('b'), ('/some/path/f1.txt', '/some/path/f2.txt'), '/some/path') + assert b == (Path('f1.txt'), Path('f2.txt')) + + c = rebase_path_traits( + spec.trait('c'), ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'], + '/some/path') + assert c == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] + + d = rebase_path_traits( + spec.trait('d'), 2.0, '/some/path') + assert d == 2.0 + + d = rebase_path_traits( + spec.trait('d'), '/some/path/either.txt', '/some/path') + assert '%s' % d == 'either.txt' + + e = rebase_path_traits( + spec.trait('e'), ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'], + '/some/path') + assert e == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] + + e = rebase_path_traits( + spec.trait('e'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], + '/some/path') + assert e == [[Path('f1.txt'), Path('f2.txt')], [[Path('f3.txt')]]] + + f = rebase_path_traits( + spec.trait('f'), {'1': '/some/path/f1.txt'}, '/some/path') + assert f == {'1': Path('f1.txt')} + + g = rebase_path_traits( + spec.trait('g'), 'some/path/either.txt', '/some/path') + assert '%s' % g == 'some/path/either.txt' + + g = rebase_path_traits( + spec.trait('g'), '/some/path/either.txt', '/some') + assert '%s' % g == 'path/either.txt' + + g = rebase_path_traits(spec.trait('g'), 'string', '/some') + assert '%s' % g == 'string' + + g = rebase_path_traits(spec.trait('g'), '2', '/some/path') + assert g == '2' # You dont want this one to be a Path + + h = rebase_path_traits(spec.trait('h'), '2', '/some/path') + assert h == '2' + + ee = rebase_path_traits( + spec.trait('ee'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], + '/some/path') + assert ee == [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + + +def test_resolve_path_traits(): + """Check resolve_path_traits.""" + spec = _test_spec() + + a = resolve_path_traits( + spec.trait('a'), 'f1.txt', '/some/path') + assert a == Path('/some/path/f1.txt') + + b = resolve_path_traits( + spec.trait('b'), ('f1.txt', 'f2.txt'), '/some/path') + assert b == (Path('/some/path/f1.txt'), Path('/some/path/f2.txt')) + + c = resolve_path_traits( + spec.trait('c'), ['f1.txt', 'f2.txt', 'f3.txt'], + '/some/path') + assert c == [Path('/some/path/f1.txt'), Path('/some/path/f2.txt'), Path('/some/path/f3.txt')] + + d = resolve_path_traits( + spec.trait('d'), 2.0, '/some/path') + assert d == 2.0 + + d = resolve_path_traits( + spec.trait('d'), 'either.txt', '/some/path') + assert '%s' % d == '/some/path/either.txt' + + e = resolve_path_traits( + spec.trait('e'), ['f1.txt', 'f2.txt', 'f3.txt'], + '/some/path') + assert e == [Path('/some/path/f1.txt'), Path('/some/path/f2.txt'), Path('/some/path/f3.txt')] + + e = resolve_path_traits( + spec.trait('e'), [['f1.txt', 'f2.txt'], [['f3.txt']]], + '/some/path') + assert e == [[Path('/some/path/f1.txt'), Path('/some/path/f2.txt')], + [[Path('/some/path/f3.txt')]]] + + f = resolve_path_traits( + spec.trait('f'), {'1': 'path/f1.txt'}, '/some') + assert f == {'1': Path('/some/path/f1.txt')} + + g = resolve_path_traits( + spec.trait('g'), '/either.txt', '/some/path') + assert g == Path('/either.txt') + + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + # Commented out because in this implementation, strings take precedence + # g = resolve_path_traits( + # spec.trait('g'), 'path/either.txt', '/some') + # assert g == Path('/some/path/either.txt') + + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + g = resolve_path_traits(spec.trait('g'), 'string', '/some') + assert g == 'string' + + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + g = resolve_path_traits(spec.trait('g'), '2', '/some/path') + assert g == '2' # You dont want this one to be a Path + + h = resolve_path_traits(spec.trait('h'), '2', '/some/path') + assert h == '2' + + ee = resolve_path_traits( + spec.trait('ee'), [['f1.txt', 'f2.txt'], [['f3.txt']]], + '/some/path') + assert ee == [['f1.txt', 'f2.txt'], [['f3.txt']]] diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index fdb79f93bb..a2586564c6 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -30,6 +30,7 @@ import traits.api as traits from traits.trait_handlers import TraitType, NoDefaultSpecified from traits.trait_base import _Undefined +from traits.traits import _TraitMaker, trait_from from traits.api import Unicode from future import standard_library @@ -307,6 +308,11 @@ def validate(self, objekt, name, value, return_pathlike=False): return value +# Patch in traits these two new +traits.File = File +traits.Directory = Directory + + class ImageFile(File): """Defines a trait whose value must be a known neuroimaging file.""" @@ -468,3 +474,120 @@ class InputMultiObject(MultiObject): InputMultiPath = InputMultiObject OutputMultiPath = OutputMultiObject + + +class Tuple(traits.BaseTuple): + """Defines a new type of Tuple trait that reports inner types.""" + + def init_fast_validator(self, *args): + """Set up the C-level fast validator.""" + super(Tuple, self).init_fast_validator(*args) + self.fast_validate = args + + def inner_traits(self): + """Return the *inner trait* (or traits) for this trait.""" + return self.types + + +class PatchedEither(TraitType): + """Defines a trait whose value can be any of of a specified list of traits.""" + + def __init__(self, *traits, **metadata): + """Create a trait whose value can be any of of a specified list of traits.""" + metadata['alternatives'] = tuple(trait_from(t) for t in traits) + self.trait_maker = _TraitMaker( + metadata.pop("default", None), *traits, **metadata) + + def as_ctrait(self): + """Return a CTrait corresponding to the trait defined by this class.""" + return self.trait_maker.as_ctrait() + + +traits.Tuple = Tuple +traits.Either = PatchedEither + + +def _rebase_path(value, cwd): + if isinstance(value, list): + return [_rebase_path(v, cwd) for v in value] + + try: + value = Path(value) + except TypeError: + pass + else: + try: + value = Path(value).relative_to(cwd) + except ValueError: + pass + return value + + +def rebase_path_traits(thistrait, value, cwd): + """Rebase a BasePath-derived trait given an interface spec.""" + if thistrait.is_trait_type(BasePath): + value = _rebase_path(value, cwd) + elif thistrait.is_trait_type(traits.List): + innertrait, = thistrait.inner_traits + if not isinstance(value, (list, tuple)): + value = rebase_path_traits(innertrait, value, cwd) + else: + value = [rebase_path_traits(innertrait, v, cwd) + for v in value] + elif thistrait.is_trait_type(traits.Dict): + _, innertrait = thistrait.inner_traits + value = {k: rebase_path_traits(innertrait, v, cwd) + for k, v in value.items()} + elif thistrait.is_trait_type(Tuple): + value = tuple([rebase_path_traits(subtrait, v, cwd) + for subtrait, v in zip(thistrait.inner_traits, value)]) + elif thistrait.alternatives: + is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) + for f in thistrait.alternatives] + if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): + return value + for subtrait in thistrait.alternatives: + value = rebase_path_traits(subtrait, value, cwd) + return value + + +def _resolve_path(value, cwd): + if isinstance(value, list): + return [_resolve_path(v, cwd) for v in value] + + try: + value = Path(value) + except TypeError: + pass + else: + if not value.is_absolute(): + value = Path(cwd) / value + return value + + +def resolve_path_traits(thistrait, value, cwd): + """Resolve a BasePath-derived trait given an interface spec.""" + if thistrait.is_trait_type(BasePath): + value = _resolve_path(value, cwd) + elif thistrait.is_trait_type(traits.List): + innertrait, = thistrait.inner_traits + if not isinstance(value, (list, tuple)): + value = resolve_path_traits(innertrait, value, cwd) + else: + value = [resolve_path_traits(innertrait, v, cwd) + for v in value] + elif thistrait.is_trait_type(traits.Dict): + _, innertrait = thistrait.inner_traits + value = {k: resolve_path_traits(innertrait, v, cwd) + for k, v in value.items()} + elif thistrait.is_trait_type(Tuple): + value = tuple([resolve_path_traits(subtrait, v, cwd) + for subtrait, v in zip(thistrait.inner_traits, value)]) + elif thistrait.alternatives: + is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) + for f in thistrait.alternatives] + if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): + return value + for subtrait in thistrait.alternatives: + value = resolve_path_traits(subtrait, value, cwd) + return value From 5f74273237f96def205164a5f9ec5aeae796c447 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Fri, 19 Jul 2019 12:21:34 -0700 Subject: [PATCH 03/13] Apply suggestions from code review Co-Authored-By: Chris Markiewicz --- nipype/interfaces/base/traits_extension.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index a2586564c6..dbe37eefb1 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -517,7 +517,7 @@ def _rebase_path(value, cwd): pass else: try: - value = Path(value).relative_to(cwd) + value = value.relative_to(cwd) except ValueError: pass return value @@ -561,7 +561,7 @@ def _resolve_path(value, cwd): pass else: if not value.is_absolute(): - value = Path(cwd) / value + value = Path(cwd).absolute() / value return value From 8a2a4e35a7bc6af8e77818dbe7beda7b4399dc56 Mon Sep 17 00:00:00 2001 From: Oscar Esteban Date: Fri, 19 Jul 2019 12:22:04 -0700 Subject: [PATCH 04/13] Update nipype/interfaces/base/tests/test_traits_extension.py Co-Authored-By: Chris Markiewicz --- nipype/interfaces/base/tests/test_traits_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 4a2b884921..9cb6c12d86 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -26,7 +26,7 @@ def test_rebase_path_traits(): a = rebase_path_traits( spec.trait('a'), '/some/path/f1.txt', '/some/path') - assert '%s' % a == 'f1.txt' + assert a == Path('f1.txt') b = rebase_path_traits( spec.trait('b'), ('/some/path/f1.txt', '/some/path/f2.txt'), '/some/path') From cb66a49d215974764f13e300b15983d64e7c29e1 Mon Sep 17 00:00:00 2001 From: oesteban Date: Fri, 19 Jul 2019 15:05:45 -0700 Subject: [PATCH 05/13] fix: addressed @effigies' comments: - [x] Removed traits.api patches - [x] Deduplicated code with a _recurse_on_path_traits proxy (https://github.com/nipy/nipype/pull/2970#discussion_r305469775) - [x] Added the two proposed test cases - [x] Optimized loop (see https://github.com/nipy/nipype/pull/2970#discussion_r305466839) --- nipype/interfaces/base/__init__.py | 5 +- .../base/tests/test_traits_extension.py | 25 ++++--- nipype/interfaces/base/traits_extension.py | 67 +++++++------------ 3 files changed, 42 insertions(+), 55 deletions(-) diff --git a/nipype/interfaces/base/__init__.py b/nipype/interfaces/base/__init__.py index 30d44db56a..672b30178b 100644 --- a/nipype/interfaces/base/__init__.py +++ b/nipype/interfaces/base/__init__.py @@ -20,8 +20,9 @@ StdOutCommandLineInputSpec) from .traits_extension import ( - traits, Undefined, isdefined, - File, Directory, Str, DictStrStr, has_metadata, ImageFile, + traits, Undefined, isdefined, has_metadata, + File, ImageFile, Directory, + Tuple, Either, Str, DictStrStr, OutputMultiObject, InputMultiObject, OutputMultiPath, InputMultiPath) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 9cb6c12d86..832f8f9592 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -8,14 +8,13 @@ class _test_spec(nib.TraitedSpec): - a = nib.traits.File() - b = nib.traits.Tuple(nib.File(), - nib.File()) + a = nib.File() + b = nib.Tuple(nib.File(), nib.File()) c = nib.traits.List(nib.File()) - d = nib.traits.Either(nib.File(), nib.traits.Float()) + d = nib.Either(nib.File(), nib.traits.Float()) e = nib.OutputMultiObject(nib.File()) f = nib.traits.Dict(nib.Str, nib.File()) - g = nib.traits.Either(nib.File, nib.Str) + g = nib.Either(nib.File, nib.Str) h = nib.Str ee = nib.OutputMultiObject(nib.Str) @@ -28,6 +27,10 @@ def test_rebase_path_traits(): spec.trait('a'), '/some/path/f1.txt', '/some/path') assert a == Path('f1.txt') + a = rebase_path_traits( + spec.trait('a'), '/some/path/f1.txt', '/some/other/path') + assert a == Path('/some/path/f1.txt') + b = rebase_path_traits( spec.trait('b'), ('/some/path/f1.txt', '/some/path/f2.txt'), '/some/path') assert b == (Path('f1.txt'), Path('f2.txt')) @@ -90,6 +93,10 @@ def test_resolve_path_traits(): spec.trait('a'), 'f1.txt', '/some/path') assert a == Path('/some/path/f1.txt') + a = resolve_path_traits( + spec.trait('a'), '/already/absolute/f1.txt', '/some/path') + assert a == Path('/already/absolute/f1.txt') + b = resolve_path_traits( spec.trait('b'), ('f1.txt', 'f2.txt'), '/some/path') assert b == (Path('/some/path/f1.txt'), Path('/some/path/f2.txt')) @@ -128,10 +135,10 @@ def test_resolve_path_traits(): # This is a problematic case, it is impossible to know whether this # was meant to be a string or a file. - # Commented out because in this implementation, strings take precedence - # g = resolve_path_traits( - # spec.trait('g'), 'path/either.txt', '/some') - # assert g == Path('/some/path/either.txt') + # In this implementation, strings take precedence + g = resolve_path_traits( + spec.trait('g'), 'path/either.txt', '/some') + assert g == 'path/either.txt' # This is a problematic case, it is impossible to know whether this # was meant to be a string or a file. diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index dbe37eefb1..64bf45755f 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -308,11 +308,6 @@ def validate(self, objekt, name, value, return_pathlike=False): return value -# Patch in traits these two new -traits.File = File -traits.Directory = Directory - - class ImageFile(File): """Defines a trait whose value must be a known neuroimaging file.""" @@ -489,7 +484,7 @@ def inner_traits(self): return self.types -class PatchedEither(TraitType): +class Either(TraitType): """Defines a trait whose value can be any of of a specified list of traits.""" def __init__(self, *traits, **metadata): @@ -504,7 +499,7 @@ def as_ctrait(self): traits.Tuple = Tuple -traits.Either = PatchedEither +traits.Either = Either def _rebase_path(value, cwd): @@ -523,34 +518,6 @@ def _rebase_path(value, cwd): return value -def rebase_path_traits(thistrait, value, cwd): - """Rebase a BasePath-derived trait given an interface spec.""" - if thistrait.is_trait_type(BasePath): - value = _rebase_path(value, cwd) - elif thistrait.is_trait_type(traits.List): - innertrait, = thistrait.inner_traits - if not isinstance(value, (list, tuple)): - value = rebase_path_traits(innertrait, value, cwd) - else: - value = [rebase_path_traits(innertrait, v, cwd) - for v in value] - elif thistrait.is_trait_type(traits.Dict): - _, innertrait = thistrait.inner_traits - value = {k: rebase_path_traits(innertrait, v, cwd) - for k, v in value.items()} - elif thistrait.is_trait_type(Tuple): - value = tuple([rebase_path_traits(subtrait, v, cwd) - for subtrait, v in zip(thistrait.inner_traits, value)]) - elif thistrait.alternatives: - is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) - for f in thistrait.alternatives] - if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): - return value - for subtrait in thistrait.alternatives: - value = rebase_path_traits(subtrait, value, cwd) - return value - - def _resolve_path(value, cwd): if isinstance(value, list): return [_resolve_path(v, cwd) for v in value] @@ -565,29 +532,41 @@ def _resolve_path(value, cwd): return value -def resolve_path_traits(thistrait, value, cwd): - """Resolve a BasePath-derived trait given an interface spec.""" +def _recurse_on_path_traits(func, thistrait, value, cwd): + """Run func recursively on BasePath-derived traits.""" if thistrait.is_trait_type(BasePath): - value = _resolve_path(value, cwd) + value = func(value, cwd) elif thistrait.is_trait_type(traits.List): innertrait, = thistrait.inner_traits if not isinstance(value, (list, tuple)): - value = resolve_path_traits(innertrait, value, cwd) + value = _recurse_on_path_traits(func, innertrait, value, cwd) else: - value = [resolve_path_traits(innertrait, v, cwd) + value = [_recurse_on_path_traits(func, innertrait, v, cwd) for v in value] elif thistrait.is_trait_type(traits.Dict): _, innertrait = thistrait.inner_traits - value = {k: resolve_path_traits(innertrait, v, cwd) + value = {k: _recurse_on_path_traits(func, innertrait, v, cwd) for k, v in value.items()} elif thistrait.is_trait_type(Tuple): - value = tuple([resolve_path_traits(subtrait, v, cwd) + value = tuple([_recurse_on_path_traits(func, subtrait, v, cwd) for subtrait, v in zip(thistrait.inner_traits, value)]) elif thistrait.alternatives: is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) for f in thistrait.alternatives] if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): return value - for subtrait in thistrait.alternatives: - value = resolve_path_traits(subtrait, value, cwd) + is_basepath = [f.is_trait_type(BasePath) for f in thistrait.alternatives] + if any(is_basepath): + subtrait = thistrait.alternatives[is_basepath.index(True)] + value = _recurse_on_path_traits(func, subtrait, value, cwd) return value + + +def rebase_path_traits(thistrait, value, cwd): + """Rebase a BasePath-derived trait given an interface spec.""" + return _recurse_on_path_traits(_rebase_path, thistrait, value, cwd) + + +def resolve_path_traits(thistrait, value, cwd): + """Resolve a BasePath-derived trait given an interface spec.""" + return _recurse_on_path_traits(_resolve_path, thistrait, value, cwd) From 2c70fa80b2a0aeb503f69c3c46867072a2fa09ed Mon Sep 17 00:00:00 2001 From: oesteban Date: Fri, 19 Jul 2019 15:16:06 -0700 Subject: [PATCH 06/13] fix: replace all ``traits.File`` -> ``File`` --- nipype/algorithms/misc.py | 2 +- nipype/interfaces/afni/model.py | 2 +- nipype/interfaces/afni/preprocess.py | 10 +- nipype/interfaces/afni/utils.py | 28 ++--- nipype/interfaces/base/specs.py | 6 +- nipype/interfaces/dipy/base.py | 2 +- nipype/interfaces/dipy/tests/test_base.py | 6 +- nipype/interfaces/freesurfer/model.py | 12 +- nipype/interfaces/freesurfer/preprocess.py | 18 +-- nipype/interfaces/freesurfer/registration.py | 2 +- nipype/interfaces/freesurfer/utils.py | 32 +++--- nipype/interfaces/fsl/fix.py | 6 +- nipype/interfaces/fsl/model.py | 2 +- nipype/interfaces/minc/minc.py | 58 +++++----- nipype/interfaces/niftyfit/asl.py | 30 ++--- nipype/interfaces/niftyfit/dwi.py | 106 +++++++++--------- nipype/interfaces/niftyfit/qt1.py | 10 +- nipype/interfaces/nipy/model.py | 6 +- nipype/interfaces/utility/base.py | 2 +- .../interfaces/utility/tests/test_wrappers.py | 2 +- nipype/pipeline/engine/tests/test_base.py | 2 +- 21 files changed, 173 insertions(+), 171 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index a4ecd3a5e2..a2eaad3610 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -727,7 +727,7 @@ def _list_outputs(self): class AddCSVRowInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec): - in_file = traits.File( + in_file = File( mandatory=True, desc='Input comma-separated value (CSV) files') _outputs = traits.Dict(traits.Any, value={}, usedefault=True) diff --git a/nipype/interfaces/afni/model.py b/nipype/interfaces/afni/model.py index e091a87c57..295a01ce0c 100644 --- a/nipype/interfaces/afni/model.py +++ b/nipype/interfaces/afni/model.py @@ -383,7 +383,7 @@ class RemlfitInputSpec(AFNICommandInputSpec): 'produces a matrix with a single column of all ones', argstr='-polort %d', xor=['matrix']) - matim = traits.File( + matim = File( desc='read a standard file as the matrix. You can use only Col as ' 'a name in GLTs with these nonstandard matrix input methods, ' 'since the other names come from the \'matrix\' file. ' diff --git a/nipype/interfaces/afni/preprocess.py b/nipype/interfaces/afni/preprocess.py index ee81ee7e2f..a2e9cb7f4d 100644 --- a/nipype/interfaces/afni/preprocess.py +++ b/nipype/interfaces/afni/preprocess.py @@ -369,7 +369,7 @@ class AllineateInputSpec(AFNICommandInputSpec): 'larger weights mean that voxel count more in the cost function. ' 'If an image file is given, the volume must be defined on the ' 'same grid as the base dataset') - out_weight_file = traits.File( + out_weight_file = File( argstr='-wtprefix %s', desc='Write the weight volume to disk as a dataset', xor=['allcostx']) @@ -966,7 +966,7 @@ class ClipLevelInputSpec(CommandLineInputSpec): argstr='-doall', position=3, xor=('grad')) - grad = traits.File( + grad = File( desc='Also compute a \'gradual\' clip level as a function of voxel ' 'position, and output that to a dataset.', argstr='-grad %s', @@ -1831,7 +1831,7 @@ class ROIStatsInputSpec(CommandLineInputSpec): 'a \'0\' in the output file. Only active if `num_roi` is ' 'enabled.', argstr='-zerofill %s') - roisel = traits.File( + roisel = File( exists=True, desc='Only considers ROIs denoted by values found in the specified ' 'file. Note that the order of the ROIs as specified in the file ' @@ -2260,7 +2260,7 @@ class TCorrMapInputSpec(AFNICommandInputSpec): polort = traits.Int(argstr='-polort %d') bandpass = traits.Tuple( (traits.Float(), traits.Float()), argstr='-bpass %f %f') - regress_out_timeseries = traits.File(exists=True, argstr='-ort %s') + regress_out_timeseries = File(exists=True, argstr='-ort %s') blur_fwhm = traits.Float(argstr='-Gblur %f') seeds_width = traits.Float(argstr='-Mseed %f', xor=('seeds')) @@ -3249,7 +3249,7 @@ class QwarpInputSpec(AFNICommandInputSpec): '* As with \'-wball\', the factor \'f\' should be between 1 and 100.' '* You cannot use \'-wball\' and \'-wmask\' together!', argstr='-wpass %s %f') - out_weight_file = traits.File( + out_weight_file = File( argstr='-wtprefix %s', desc='Write the weight volume to disk as a dataset') blur = traits.List( diff --git a/nipype/interfaces/afni/utils.py b/nipype/interfaces/afni/utils.py index e16e1bbd79..4f08ee26a0 100644 --- a/nipype/interfaces/afni/utils.py +++ b/nipype/interfaces/afni/utils.py @@ -1397,7 +1397,7 @@ class LocalBistatInputSpec(AFNICommandInputSpec): ' * ALL = all of the above, in that order' 'More than one option can be used.', argstr='-stat %s...') - mask_file = traits.File( + mask_file = File( exists=True, desc='mask image file name. Voxels NOT in the mask will not be used ' 'in the neighborhood of any voxel. Also, a voxel NOT in the mask ' @@ -1407,13 +1407,13 @@ class LocalBistatInputSpec(AFNICommandInputSpec): desc='Compute the mask as in program 3dAutomask.', argstr='-automask', xor=['weight_file']) - weight_file = traits.File( + weight_file = File( exists=True, desc='File name of an image to use as a weight. Only applies to ' '\'pearson\' statistics.', argstr='-weight %s', xor=['automask']) - out_file = traits.File( + out_file = File( desc='Output dataset.', argstr='-prefix %s', name_source='in_file1', @@ -1527,7 +1527,7 @@ class LocalstatInputSpec(AFNICommandInputSpec): ' mean, median, MAD, P2skew\n' 'More than one option can be used.', argstr='-stat %s...') - mask_file = traits.File( + mask_file = File( exists=True, desc='Mask image file name. Voxels NOT in the mask will not be used ' 'in the neighborhood of any voxel. Also, a voxel NOT in the ' @@ -1589,7 +1589,7 @@ class LocalstatInputSpec(AFNICommandInputSpec): overwrite = traits.Bool( desc='overwrite output file if it already exists', argstr='-overwrite') - out_file = traits.File( + out_file = File( desc='Output dataset.', argstr='-prefix %s', name_source='in_file', @@ -1905,7 +1905,7 @@ class NwarpApplyInputSpec(CommandLineInputSpec): inv_warp = traits.Bool( desc='After the warp specified in \'-nwarp\' is computed, invert it', argstr='-iwarp') - master = traits.File( + master = File( exists=True, desc='the name of the master dataset, which defines the output grid', argstr='-master %s') @@ -1980,10 +1980,10 @@ class NwarpApply(AFNICommandBase): class NwarpCatInputSpec(AFNICommandInputSpec): in_files = traits.List( - traits.Either(traits.File(), + traits.Either(File(), traits.Tuple( traits.Enum('IDENT', 'INV', 'SQRT', 'SQRTINV'), - traits.File())), + File())), desc="list of tuples of 3D warps and associated functions", mandatory=True, argstr="%s", @@ -2137,7 +2137,7 @@ class OneDToolPyInputSpec(AFNIPythonCommandInputSpec): desc= 'display a list of TRs which were not censored in the specified style', argstr='-show_trs_uncensored %s') - show_cormat_warnings = traits.File( + show_cormat_warnings = File( desc='Write cormat warnings to a file', argstr="-show_cormat_warnings |& tee %s", position=-1, @@ -2226,7 +2226,7 @@ class RefitInputSpec(CommandLineInputSpec): desc='Associates the dataset with a specific template type, e.g. ' 'TLRC, MNI, ORIG') atrcopy = traits.Tuple( - traits.File(exists=True), + File(exists=True), traits.Str(), argstr='-atrcopy %s %s', desc='Copy AFNI header attribute from the given file into the header ' @@ -2310,7 +2310,7 @@ class ReHoInputSpec(CommandLineInputSpec): position=1, mandatory=True, exists=True) - out_file = traits.File( + out_file = File( desc='Output dataset.', argstr='-prefix %s', name_source='in_file', @@ -2323,7 +2323,7 @@ class ReHoInputSpec(CommandLineInputSpec): 'Kendall\'s W. This option is currently compatible only with ' 'the AFNI (BRIK/HEAD) output type; the chi-squared value will ' 'be the second sub-brick of the output dataset.') - mask_file = traits.File( + mask_file = File( desc='Mask within which ReHo should be calculated voxelwise', argstr='-mask %s') neighborhood = traits.Enum( @@ -2451,7 +2451,7 @@ class ResampleInputSpec(AFNICommandInputSpec): *[traits.Float()] * 3, argstr='-dxyz %f %f %f', desc='resample to new dx, dy and dz') - master = traits.File( + master = File( argstr='-master %s', desc='align dataset grid to a reference file') @@ -3247,7 +3247,7 @@ class ZeropadInputSpec(AFNICommandInputSpec): 'and slice thickness = 2.5 mm ==> 2 slices added', argstr='-mm', xor=['master']) - master = traits.File( + master = File( desc='match the volume described in dataset ' '\'mset\', where mset must have the same ' 'orientation and grid spacing as dataset to be ' diff --git a/nipype/interfaces/base/specs.py b/nipype/interfaces/base/specs.py index fb0f3295f0..5028d5c30a 100644 --- a/nipype/interfaces/base/specs.py +++ b/nipype/interfaces/base/specs.py @@ -25,6 +25,8 @@ md5, hash_infile, hash_timestamp, to_str, USING_PATHLIB2) from .traits_extension import ( traits, + File, + Str, Undefined, isdefined, has_metadata, @@ -373,13 +375,13 @@ class DynamicTraitedSpec(BaseTraitedSpec): class CommandLineInputSpec(BaseInterfaceInputSpec): - args = traits.Str(argstr='%s', desc='Additional parameters to the command') + args = Str(argstr='%s', desc='Additional parameters to the command') environ = traits.DictStrStr( desc='Environment variables', usedefault=True, nohash=True) class StdOutCommandLineInputSpec(CommandLineInputSpec): - out_file = traits.File(argstr="> %s", position=-1, genfile=True) + out_file = File(argstr="> %s", position=-1, genfile=True) class MpiCommandLineInputSpec(CommandLineInputSpec): diff --git a/nipype/interfaces/dipy/base.py b/nipype/interfaces/dipy/base.py index 27f26e989a..3e0e064bb4 100644 --- a/nipype/interfaces/dipy/base.py +++ b/nipype/interfaces/dipy/base.py @@ -100,7 +100,7 @@ def convert_to_traits_type(dipy_type, is_file=False): elif "string" in dipy_type and not is_file: return traits.Str, is_mandatory elif "string" in dipy_type and is_file: - return traits.File, is_mandatory + return File, is_mandatory elif "int" in dipy_type: return traits.Int, is_mandatory elif "float" in dipy_type: diff --git a/nipype/interfaces/dipy/tests/test_base.py b/nipype/interfaces/dipy/tests/test_base.py index 1d475ac0f7..740057bcd3 100644 --- a/nipype/interfaces/dipy/tests/test_base.py +++ b/nipype/interfaces/dipy/tests/test_base.py @@ -1,6 +1,6 @@ import pytest from collections import namedtuple -from ...base import traits, TraitedSpec, BaseInterfaceInputSpec +from ...base import traits, File, TraitedSpec, BaseInterfaceInputSpec from ..base import (convert_to_traits_type, create_interface_specs, dipy_to_nipype_interface, DipyBaseInterface, no_dipy, get_dipy_workflows) @@ -35,10 +35,10 @@ def test_convert_to_traits_type(): Res(traits.ListStr, False), Res(traits.ListFloat, False), Res(traits.ListBool, False), Res(traits.ListComplex, False), Res(traits.Str, True), Res(traits.Int, True), - Res(traits.File, True), Res(traits.Float, True), + Res(File, True), Res(traits.Float, True), Res(traits.Bool, True), Res(traits.Complex, True), Res(traits.Str, False), Res(traits.Int, False), - Res(traits.File, False), Res(traits.Float, False), + Res(File, False), Res(traits.Float, False), Res(traits.Bool, False), Res(traits.Complex, False), ] diff --git a/nipype/interfaces/freesurfer/model.py b/nipype/interfaces/freesurfer/model.py index 58d168e2d7..d1c8dcdc93 100644 --- a/nipype/interfaces/freesurfer/model.py +++ b/nipype/interfaces/freesurfer/model.py @@ -988,7 +988,7 @@ class SegStatsReconAllInputSpec(SegStatsInputSpec): mandatory=True, desc="Subject id being processed") # implicit - ribbon = traits.File( + ribbon = File( mandatory=True, exists=True, desc="Input file mri/ribbon.mgz") presurf_seg = File(exists=True, desc="Input segmentation volume") transform = File(mandatory=True, exists=True, desc="Input transform file") @@ -1216,19 +1216,19 @@ class MS_LDAInputSpec(FSTraitedSpec): maxlen=2, sep=' ', desc='pair of class labels to optimize') - weight_file = traits.File( + weight_file = File( argstr='-weight %s', mandatory=True, desc='filename for the LDA weights (input or output)') - vol_synth_file = traits.File( + vol_synth_file = File( exists=False, argstr='-synth %s', mandatory=True, desc=('filename for the synthesized output ' 'volume')) - label_file = traits.File( + label_file = File( exists=True, argstr='-label %s', desc='filename of the label volume') - mask_file = traits.File( + mask_file = File( exists=True, argstr='-mask %s', desc='filename of the brain mask volume') @@ -1532,7 +1532,7 @@ class SphericalAverageInputSpec(FSTraitedSpec): exists=False, position=-1, desc="Output filename") - in_average = traits.Directory( + in_average = Directory( argstr="%s", exists=True, genfile=True, diff --git a/nipype/interfaces/freesurfer/preprocess.py b/nipype/interfaces/freesurfer/preprocess.py index 2941968f85..682910cfb3 100644 --- a/nipype/interfaces/freesurfer/preprocess.py +++ b/nipype/interfaces/freesurfer/preprocess.py @@ -2313,7 +2313,7 @@ class NormalizeInputSpec(FSTraitedSpec): class NormalizeOutputSpec(TraitedSpec): - out_file = traits.File(exists=False, desc="The output file for Normalize") + out_file = File(exists=False, desc="The output file for Normalize") class Normalize(FSCommand): @@ -2379,7 +2379,7 @@ class CANormalizeInputSpec(FSTraitedSpec): class CANormalizeOutputSpec(TraitedSpec): - out_file = traits.File(exists=False, desc="The output file for Normalize") + out_file = File(exists=False, desc="The output file for Normalize") control_points = File( exists=False, desc="The output control points for Normalize") @@ -2460,7 +2460,7 @@ class CARegisterInputSpec(FSTraitedSpecOpenMP): class CARegisterOutputSpec(TraitedSpec): - out_file = traits.File(exists=False, desc="The output file for CARegister") + out_file = File(exists=False, desc="The output file for CARegister") class CARegister(FSCommandOpenMP): @@ -2540,13 +2540,13 @@ class CALabelInputSpec(FSTraitedSpecOpenMP): desc=("Reclassify voxels at least some std" " devs from the mean using some size" " Gaussian window")) - label = traits.File( + label = File( argstr="-l %s", exists=True, desc= "Undocumented flag. Autorecon3 uses ../label/{hemisphere}.cortex.label as input file" ) - aseg = traits.File( + aseg = File( argstr="-aseg %s", exists=True, desc= @@ -2630,13 +2630,13 @@ class MRIsCALabelInputSpec(FSTraitedSpecOpenMP): name_template="%s.aparc.annot", desc="Annotated surface output file") # optional - label = traits.File( + label = File( argstr="-l %s", exists=True, desc= "Undocumented flag. Autorecon3 uses ../label/{hemisphere}.cortex.label as input file" ) - aseg = traits.File( + aseg = File( argstr="-aseg %s", exists=True, desc= @@ -2985,13 +2985,13 @@ class ConcatenateLTAInputSpec(FSTraitedSpec): 'VOX2VOX', 'RAS2RAS', argstr='-out_type %d', desc='set final LTA type') # Talairach options - tal_source_file = traits.File( + tal_source_file = File( exists=True, argstr='-tal %s', position=-5, requires=['tal_template_file'], desc='if in_lta2 is talairach.xfm, specify source for talairach') - tal_template_file = traits.File( + tal_template_file = File( exists=True, argstr='%s', position=-4, diff --git a/nipype/interfaces/freesurfer/registration.py b/nipype/interfaces/freesurfer/registration.py index 99ee7d0179..9cf6fb7b44 100644 --- a/nipype/interfaces/freesurfer/registration.py +++ b/nipype/interfaces/freesurfer/registration.py @@ -134,7 +134,7 @@ class RegisterAVItoTalairachInputSpec(FSTraitedSpec): class RegisterAVItoTalairachOutputSpec(FSScriptOutputSpec): - out_file = traits.File( + out_file = File( exists=False, desc="The output file for RegisterAVItoTalairach") diff --git a/nipype/interfaces/freesurfer/utils.py b/nipype/interfaces/freesurfer/utils.py index 55e38576bb..a60c5f7a11 100644 --- a/nipype/interfaces/freesurfer/utils.py +++ b/nipype/interfaces/freesurfer/utils.py @@ -13,7 +13,7 @@ from ... import logging from ...utils.filemanip import fname_presuffix, split_filename -from ..base import (TraitedSpec, File, traits, OutputMultiPath, isdefined, +from ..base import (TraitedSpec, Directory, File, traits, OutputMultiPath, isdefined, CommandLine, CommandLineInputSpec) from .base import (FSCommand, FSTraitedSpec, FSSurfaceCommand, FSScriptCommand, FSScriptOutputSpec, FSTraitedSpecOpenMP, FSCommandOpenMP) @@ -769,7 +769,7 @@ class SurfaceSnapshotsInputSpec(FSTraitedSpec): desc="load an overlay volume/surface", requires=["overlay_range"]) reg_xors = ["overlay_reg", "identity_reg", "mni152_reg"] - overlay_reg = traits.File( + overlay_reg = File( exists=True, argstr="-overlay-reg %s", xor=reg_xors, @@ -1956,7 +1956,7 @@ class CheckTalairachAlignmentInputSpec(FSTraitedSpec): class CheckTalairachAlignmentOutputSpec(TraitedSpec): - out_file = traits.File( + out_file = File( exists=True, desc="The input file for CheckTalairachAlignment") @@ -2002,11 +2002,11 @@ class TalairachAVIInputSpec(FSTraitedSpec): class TalairachAVIOutputSpec(TraitedSpec): - out_file = traits.File( + out_file = File( exists=False, desc="The output transform for TalairachAVI") - out_log = traits.File( + out_log = File( exists=False, desc="The output log file for TalairachAVI") - out_txt = traits.File( + out_txt = File( exists=False, desc="The output text file for TaliarachAVI") @@ -2348,7 +2348,7 @@ class FixTopologyInputSpec(FSTraitedSpec): desc= "No documentation. Direct questions to analysis-bugs@nmr.mgh.harvard.edu" ) - sphere = traits.File(argstr="-sphere %s", desc="Sphere input file") + sphere = File(argstr="-sphere %s", desc="Sphere input file") class FixTopologyOutputSpec(TraitedSpec): @@ -3215,27 +3215,27 @@ class ParcellationStatsInputSpec(FSTraitedSpec): surface = traits.String( position=-1, argstr="%s", desc="Input surface (e.g. 'white')") mgz = traits.Bool(argstr="-mgz", desc="Look for mgz files") - in_cortex = traits.File( + in_cortex = File( argstr="-cortex %s", exists=True, desc="Input cortex label") - in_annotation = traits.File( + in_annotation = File( argstr="-a %s", exists=True, xor=['in_label'], desc= "compute properties for each label in the annotation file separately") - in_label = traits.File( + in_label = File( argstr="-l %s", exists=True, xor=['in_annotatoin', 'out_color'], desc="limit calculations to specified label") tabular_output = traits.Bool(argstr="-b", desc="Tabular output") - out_table = traits.File( + out_table = File( argstr="-f %s", exists=False, genfile=True, requires=['tabular_output'], desc="Table output to tablefile") - out_color = traits.File( + out_color = File( argstr="-c %s", exists=False, genfile=True, @@ -3395,13 +3395,13 @@ class ContrastInputSpec(FSTraitedSpec): mandatory=True, exists=True, desc="Input file must be /surf/.white") - annotation = traits.File( + annotation = File( mandatory=True, exists=True, desc= "Input annotation file must be /label/.aparc.annot" ) - cortex = traits.File( + cortex = File( mandatory=True, exists=True, desc= @@ -3500,7 +3500,7 @@ class RelabelHypointensitiesInputSpec(FSTraitedSpec): mandatory=True, exists=True, desc="Input aseg file") - surf_directory = traits.Directory( + surf_directory = Directory( '.', argstr="%s", position=-2, @@ -3804,7 +3804,7 @@ class MRIsExpandInputSpec(FSTraitedSpec): # desc=('Tuple of (n_averages, min_averages) parameters ' # '(implicit: (16, 0))')) # target_intensity = traits.Tuple( - # traits.Float, traits.File(exists=True), + # traits.Float, File(exists=True), # argstr='-intensity %g %s', # desc='Tuple of intensity and brain volume to crop to target intensity') diff --git a/nipype/interfaces/fsl/fix.py b/nipype/interfaces/fsl/fix.py index ebe986eb79..5b5a044e88 100644 --- a/nipype/interfaces/fsl/fix.py +++ b/nipype/interfaces/fsl/fix.py @@ -329,13 +329,13 @@ class CleanerInputSpec(CommandLineInputSpec): 'Apply aggressive (full variance) cleanup, instead of the default less-aggressive (unique variance) cleanup.', position=3) - confound_file = traits.File( + confound_file = File( argstr='-x %s', desc='Include additional confound file.', position=4) - confound_file_1 = traits.File( + confound_file_1 = File( argstr='-x %s', desc='Include additional confound file.', position=5) - confound_file_2 = traits.File( + confound_file_2 = File( argstr='-x %s', desc='Include additional confound file.', position=6) diff --git a/nipype/interfaces/fsl/model.py b/nipype/interfaces/fsl/model.py index 113f785120..ad69968363 100644 --- a/nipype/interfaces/fsl/model.py +++ b/nipype/interfaces/fsl/model.py @@ -1891,7 +1891,7 @@ class ClusterInputSpec(FSLCommandInputSpec): peak_distance = traits.Float( argstr='--peakdist=%.10f', desc='minimum distance between local maxima/minima, in mm (default 0)') - cope_file = traits.File(argstr='--cope=%s', desc='cope volume') + cope_file = File(argstr='--cope=%s', desc='cope volume') volume = traits.Int( argstr='--volume=%d', desc='number of voxels in the mask') dlh = traits.Float( diff --git a/nipype/interfaces/minc/minc.py b/nipype/interfaces/minc/minc.py index 8ac8babe52..907d35675f 100644 --- a/nipype/interfaces/minc/minc.py +++ b/nipype/interfaces/minc/minc.py @@ -677,7 +677,7 @@ class AverageInputSpec(CommandLineInputSpec): _xor_input_files = ('input_files', 'filelist') input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s)', mandatory=True, sep=' ', @@ -685,7 +685,7 @@ class AverageInputSpec(CommandLineInputSpec): position=-2, xor=_xor_input_files) - filelist = traits.File( + filelist = File( desc='Specify the name of a file containing input file names.', argstr='-filelist %s', exists=True, @@ -806,7 +806,7 @@ class AverageInputSpec(CommandLineInputSpec): argstr='-range %d %d', desc='Valid range for output data.') - sdfile = traits.File( + sdfile = File( desc='Specify an output sd file (default=none).', argstr='-sdfile %s') _xor_copy_header = ('copy_header', 'no_copy_header') @@ -938,7 +938,7 @@ class CalcInputSpec(CommandLineInputSpec): _xor_input_files = ('input_files', 'filelist') input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s) for calculation', mandatory=True, sep=' ', @@ -979,7 +979,7 @@ class CalcInputSpec(CommandLineInputSpec): debug = traits.Bool(desc='Print out debugging messages.', argstr='-debug') - filelist = traits.File( + filelist = File( desc='Specify the name of a file containing input file names.', argstr='-filelist %s', mandatory=True, @@ -1095,7 +1095,7 @@ class CalcInputSpec(CommandLineInputSpec): argstr='-expression \'%s\'', xor=_xor_expression, mandatory=True) - expfile = traits.File( + expfile = File( desc='Name of file containing expression.', argstr='-expfile %s', xor=_xor_expression, @@ -1106,7 +1106,7 @@ class CalcInputSpec(CommandLineInputSpec): outfiles = traits.List( traits.Tuple( traits.Str, - traits.File, + File, argstr='-outfile %s %s', desc= ('List of (symbol, file) tuples indicating that output should be written' @@ -1300,7 +1300,7 @@ class BeastInputSpec(CommandLineInputSpec): usedefault=True, default_value=True) - configuration_file = traits.File( + configuration_file = File( desc='Specify configuration file.', argstr='-configuration %s') voxel_size = traits.Int( @@ -1349,11 +1349,11 @@ class BeastInputSpec(CommandLineInputSpec): desc='Output final mask with the same resolution as input file.', argstr='-same_resolution') - library_dir = traits.Directory( + library_dir = Directory( desc='library directory', position=-3, argstr='%s', mandatory=True) - input_file = traits.File( + input_file = File( desc='input file', position=-2, argstr='%s', mandatory=True) - output_file = traits.File( + output_file = File( desc='output file', position=-1, argstr='%s', @@ -1741,7 +1741,7 @@ class MathInputSpec(CommandLineInputSpec): _xor_input_files = ('input_files', 'filelist') input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s) for calculation', mandatory=True, sep=' ', @@ -1758,7 +1758,7 @@ class MathInputSpec(CommandLineInputSpec): hash_files=False, name_template='%s_mincmath.mnc') - filelist = traits.File( + filelist = File( desc='Specify the name of a file containing input file names.', argstr='-filelist %s', exists=True, @@ -2193,7 +2193,7 @@ class ResampleInputSpec(CommandLineInputSpec): # This is a dummy input. input_grid_files = InputMultiPath( - traits.File, + File, desc='input grid file(s)', ) @@ -2257,7 +2257,7 @@ class ResampleInputSpec(CommandLineInputSpec): xor=_xor_sinc_window_type, requires=['sinc_interpolation']) - transformation = traits.File( + transformation = File( desc='File giving world transformation. (Default = identity).', exists=True, argstr='-transformation %s') @@ -2278,7 +2278,7 @@ class ResampleInputSpec(CommandLineInputSpec): argstr='-use_input_sampling', xor=_xor_input_sampling) - like = traits.File( + like = File( desc='Specifies a model file for the resampling.', argstr='-like %s', exists=True) @@ -2578,7 +2578,7 @@ class NormInputSpec(CommandLineInputSpec): hash_files=False, name_template='%s_norm.mnc') - output_threshold_mask = traits.File( + output_threshold_mask = File( desc='File in which to store the threshold mask.', argstr='-threshold_mask %s', name_source=['input_file'], @@ -2592,7 +2592,7 @@ class NormInputSpec(CommandLineInputSpec): default_value=True) # Normalisation Options - mask = traits.File( + mask = File( desc='Calculate the image normalisation within a mask.', argstr='-mask %s', exists=True) @@ -2988,7 +2988,7 @@ def _list_outputs(self): class XfmConcatInputSpec(CommandLineInputSpec): input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s)', mandatory=True, sep=' ', @@ -2997,7 +2997,7 @@ class XfmConcatInputSpec(CommandLineInputSpec): # This is a dummy input. input_grid_files = InputMultiPath( - traits.File, + File, desc='input grid file(s)', ) @@ -3165,7 +3165,7 @@ class NlpFitInputSpec(CommandLineInputSpec): # This is a dummy input. input_grid_files = InputMultiPath( - traits.File, + File, desc='input grid file(s)', ) @@ -3256,7 +3256,7 @@ def _list_outputs(self): class XfmAvgInputSpec(CommandLineInputSpec): input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s)', mandatory=True, sep=' ', @@ -3265,7 +3265,7 @@ class XfmAvgInputSpec(CommandLineInputSpec): # This is a dummy input. input_grid_files = InputMultiPath( - traits.File, + File, desc='input grid file(s)', ) @@ -3355,7 +3355,7 @@ def _list_outputs(self): class XfmInvertInputSpec(CommandLineInputSpec): - input_file = traits.File( + input_file = File( desc='input file', exists=True, mandatory=True, @@ -3430,7 +3430,7 @@ def _list_outputs(self): class BigAverageInputSpec(CommandLineInputSpec): input_files = InputMultiPath( - traits.File(exists=True), + File(exists=True), desc='input file(s)', mandatory=True, sep=' ', @@ -3520,7 +3520,7 @@ class BigAverage(CommandLine): class ReshapeInputSpec(CommandLineInputSpec): - input_file = traits.File( + input_file = File( desc='input file', exists=True, mandatory=True, @@ -3578,14 +3578,14 @@ class Reshape(CommandLine): class VolSymmInputSpec(CommandLineInputSpec): - input_file = traits.File( + input_file = File( desc='input file', exists=True, mandatory=True, argstr='%s', position=-3) - trans_file = traits.File( + trans_file = File( desc='output xfm trans file', genfile=True, argstr='%s', @@ -3606,7 +3606,7 @@ class VolSymmInputSpec(CommandLineInputSpec): # This is a dummy input. input_grid_files = InputMultiPath( - traits.File, + File, desc='input grid file(s)', ) diff --git a/nipype/interfaces/niftyfit/asl.py b/nipype/interfaces/niftyfit/asl.py index c4920dc195..0d13880e1e 100644 --- a/nipype/interfaces/niftyfit/asl.py +++ b/nipype/interfaces/niftyfit/asl.py @@ -4,7 +4,7 @@ The ASL module of niftyfit, which wraps the fitting methods in NiftyFit. """ -from ..base import TraitedSpec, traits, CommandLineInputSpec +from ..base import File, TraitedSpec, traits, CommandLineInputSpec from .base import NiftyFitCommand from ..niftyreg.base import get_custom_path @@ -12,7 +12,7 @@ class FitAslInputSpec(CommandLineInputSpec): """ Input Spec for FitAsl. """ desc = 'Filename of the 4D ASL (control/label) source image (mandatory).' - source_file = traits.File( + source_file = File( position=1, exists=True, argstr='-source %s', @@ -23,17 +23,17 @@ class FitAslInputSpec(CommandLineInputSpec): # *** Output options: desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).' - cbf_file = traits.File( + cbf_file = File( name_source=['source_file'], name_template='%s_cbf.nii.gz', argstr='-cbf %s', desc=desc) - error_file = traits.File( + error_file = File( name_source=['source_file'], name_template='%s_error.nii.gz', argstr='-error %s', desc='Filename of the CBF error map.') - syn_file = traits.File( + syn_file = File( name_source=['source_file'], name_template='%s_syn.nii.gz', argstr='-syn %s', @@ -41,20 +41,20 @@ class FitAslInputSpec(CommandLineInputSpec): # *** Input options (see also fit_qt1 for generic T1 fitting): desc = 'Filename of the estimated input T1 map (in ms).' - t1map = traits.File(exists=True, argstr='-t1map %s', desc=desc) + t1map = File(exists=True, argstr='-t1map %s', desc=desc) desc = 'Filename of the estimated input M0 map.' - m0map = traits.File(exists=True, argstr='-m0map %s', desc=desc) + m0map = File(exists=True, argstr='-m0map %s', desc=desc) desc = 'Filename of the estimated input M0 map error.' - m0mape = traits.File(exists=True, argstr='-m0mape %s', desc=desc) + m0mape = File(exists=True, argstr='-m0mape %s', desc=desc) desc = 'Filename of a [1,2,5]s Inversion Recovery volume (T1/M0 fitting \ carried out internally).' - ir_volume = traits.File(exists=True, argstr='-IRvolume %s', desc=desc) + ir_volume = File(exists=True, argstr='-IRvolume %s', desc=desc) desc = 'Output of [1,2,5]s Inversion Recovery fitting.' - ir_output = traits.File(exists=True, argstr='-IRoutput %s', desc=desc) + ir_output = File(exists=True, argstr='-IRoutput %s', desc=desc) # *** Experimental options (Choose those suitable for the model!): - mask = traits.File( + mask = File( position=2, exists=True, desc='Filename of image mask.', @@ -104,7 +104,7 @@ class FitAslInputSpec(CommandLineInputSpec): desc = 'Filename of the 4D segmentation (in ASL space) for L/T1 \ estimation and PV correction {WM,GM,CSF}.' - seg = traits.File(exists=True, argstr='-seg %s', desc=desc) + seg = File(exists=True, argstr='-seg %s', desc=desc) desc = 'Use sigmoid to estimate L from T1: L(T1|gmL,wmL) [Off].' sig = traits.Bool(desc=desc, argstr='-sig') desc = 'Simple PV correction (CBF=vg*CBFg + vw*CBFw, with CBFw=f*CBFg) \ @@ -131,11 +131,11 @@ class FitAslInputSpec(CommandLineInputSpec): class FitAslOutputSpec(TraitedSpec): """ Output Spec for FitAsl. """ desc = 'Filename of the Cerebral Blood Flow map (in ml/100g/min).' - cbf_file = traits.File(exists=True, desc=desc) + cbf_file = File(exists=True, desc=desc) desc = 'Filename of the CBF error map.' - error_file = traits.File(exists=True, desc=desc) + error_file = File(exists=True, desc=desc) desc = 'Filename of the synthetic ASL data.' - syn_file = traits.File(exists=True, desc=desc) + syn_file = File(exists=True, desc=desc) class FitAsl(NiftyFitCommand): diff --git a/nipype/interfaces/niftyfit/dwi.py b/nipype/interfaces/niftyfit/dwi.py index 23b73aea90..900a558fa1 100644 --- a/nipype/interfaces/niftyfit/dwi.py +++ b/nipype/interfaces/niftyfit/dwi.py @@ -4,7 +4,7 @@ The dwi module of niftyfit, which wraps the fitting methods in NiftyFit. """ -from ..base import TraitedSpec, traits, isdefined, CommandLineInputSpec +from ..base import File, TraitedSpec, traits, isdefined, CommandLineInputSpec from .base import NiftyFitCommand from ..niftyreg.base import get_custom_path @@ -12,61 +12,61 @@ class FitDwiInputSpec(CommandLineInputSpec): """ Input Spec for FitDwi. """ # Inputs options - source_file = traits.File( + source_file = File( position=1, exists=True, argstr='-source %s', mandatory=True, desc='The source image containing the dwi data.') desc = 'The file containing the bvalues of the source DWI.' - bval_file = traits.File( + bval_file = File( position=2, exists=True, argstr='-bval %s', mandatory=True, desc=desc) desc = 'The file containing the bvectors of the source DWI.' - bvec_file = traits.File( + bvec_file = File( position=3, exists=True, argstr='-bvec %s', mandatory=True, desc=desc) - te_file = traits.File( + te_file = File( exists=True, argstr='-TE %s', desc='Filename of TEs (ms).', xor=['te_file']) - te_value = traits.File( + te_value = File( exists=True, argstr='-TE %s', desc='Value of TEs (ms).', xor=['te_file']) - mask_file = traits.File( + mask_file = File( exists=True, desc='The image mask', argstr='-mask %s') desc = 'Filename of parameter priors for -ball and -nod.' - prior_file = traits.File(exists=True, argstr='-prior %s', desc=desc) + prior_file = File(exists=True, argstr='-prior %s', desc=desc) desc = 'Rotate the output tensors according to the q/s form of the image \ (resulting tensors will be in mm coordinates, default: 0).' rot_sform_flag = traits.Int(desc=desc, argstr='-rotsform %d') # generic output options: - error_file = traits.File( + error_file = File( name_source=['source_file'], name_template='%s_error.nii.gz', desc='Filename of parameter error maps.', argstr='-error %s') - res_file = traits.File( + res_file = File( name_source=['source_file'], name_template='%s_resmap.nii.gz', desc='Filename of model residual map.', argstr='-res %s') - syn_file = traits.File( + syn_file = File( name_source=['source_file'], name_template='%s_syn.nii.gz', desc='Filename of synthetic image.', argstr='-syn %s') - nodiff_file = traits.File( + nodiff_file = File( name_source=['source_file'], name_template='%s_no_diff.nii.gz', desc='Filename of average no diffusion image.', argstr='-nodiff %s') # Output options, with templated output names based on the source image - mcmap_file = traits.File( + mcmap_file = File( name_source=['source_file'], name_template='%s_mcmap.nii.gz', desc='Filename of multi-compartment model parameter map ' @@ -75,22 +75,22 @@ class FitDwiInputSpec(CommandLineInputSpec): requires=['nodv_flag']) # Model Specific Output options: - mdmap_file = traits.File( + mdmap_file = File( name_source=['source_file'], name_template='%s_mdmap.nii.gz', desc='Filename of MD map/ADC', argstr='-mdmap %s') - famap_file = traits.File( + famap_file = File( name_source=['source_file'], name_template='%s_famap.nii.gz', desc='Filename of FA map', argstr='-famap %s') - v1map_file = traits.File( + v1map_file = File( name_source=['source_file'], name_template='%s_v1map.nii.gz', desc='Filename of PDD map [x,y,z]', argstr='-v1map %s') - rgbmap_file = traits.File( + rgbmap_file = File( name_source=['source_file'], name_template='%s_rgbmap.nii.gz', desc='Filename of colour-coded FA map', @@ -103,13 +103,13 @@ class FitDwiInputSpec(CommandLineInputSpec): ten_type = traits.Enum( 'lower-tri', 'diag-off-diag', desc=desc, usedefault=True) - tenmap_file = traits.File( + tenmap_file = File( name_source=['source_file'], name_template='%s_tenmap.nii.gz', desc='Filename of tensor map [diag,offdiag].', argstr='-tenmap %s', requires=['dti_flag']) - tenmap2_file = traits.File( + tenmap2_file = File( name_source=['source_file'], name_template='%s_tenmap2.nii.gz', desc='Filename of tensor map [lower tri]', @@ -195,7 +195,7 @@ class FitDwiInputSpec(CommandLineInputSpec): identity covariance...).' vb_flag = traits.Bool(desc=desc, argstr='-vb') - cov_file = traits.File( + cov_file = File( exists=True, desc='Filename of ithe nc*nc covariance matrix [I]', argstr='-cov %s') @@ -221,7 +221,7 @@ class FitDwiInputSpec(CommandLineInputSpec): perf_thr = traits.Float(desc=desc, argstr='-perfthreshold %f') # MCMC options: - mcout = traits.File( + mcout = File( name_source=['source_file'], name_template='%s_mcout.txt', desc='Filename of mc samples (ascii text file)', @@ -238,20 +238,20 @@ class FitDwiInputSpec(CommandLineInputSpec): class FitDwiOutputSpec(TraitedSpec): """ Output Spec for FitDwi. """ - 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') - nodiff_file = traits.File(desc='Filename of average no diffusion image.') - mdmap_file = traits.File(desc='Filename of MD map/ADC') - famap_file = traits.File(desc='Filename of FA map') - v1map_file = traits.File(desc='Filename of PDD map [x,y,z]') - 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]') - - mcmap_file = traits.File(desc='Filename of multi-compartment model ' + error_file = File(desc='Filename of parameter error maps') + res_file = File(desc='Filename of model residual map') + syn_file = File(desc='Filename of synthetic image') + nodiff_file = File(desc='Filename of average no diffusion image.') + mdmap_file = File(desc='Filename of MD map/ADC') + famap_file = File(desc='Filename of FA map') + v1map_file = File(desc='Filename of PDD map [x,y,z]') + rgbmap_file = File(desc='Filename of colour FA map') + tenmap_file = File(desc='Filename of tensor map') + tenmap2_file = File(desc='Filename of tensor map [lower tri]') + + mcmap_file = File(desc='Filename of multi-compartment model ' 'parameter map (-ivim,-ball,-nod).') - mcout = traits.File(desc='Filename of mc samples (ascii text file)') + mcout = File(desc='Filename of mc samples (ascii text file)') class FitDwi(NiftyFitCommand): @@ -297,63 +297,63 @@ def _format_arg(self, name, trait_spec, value): class DwiToolInputSpec(CommandLineInputSpec): """ Input Spec for DwiTool. """ desc = 'The source image containing the fitted model.' - source_file = traits.File( + source_file = File( position=1, exists=True, desc=desc, argstr='-source %s', mandatory=True) desc = 'The file containing the bvalues of the source DWI.' - bval_file = traits.File( + bval_file = File( position=2, exists=True, desc=desc, argstr='-bval %s', mandatory=True) desc = 'The file containing the bvectors of the source DWI.' - bvec_file = traits.File( + bvec_file = File( position=3, exists=True, desc=desc, argstr='-bvec %s') - b0_file = traits.File( + b0_file = File( position=4, exists=True, desc='The B0 image corresponding to the source DWI', argstr='-b0 %s') - mask_file = traits.File( + mask_file = File( position=5, exists=True, desc='The image mask', argstr='-mask %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( + mcmap_file = File( name_source=['source_file'], name_template='%s_mcmap.nii.gz', desc=desc, argstr='-mcmap %s') desc = 'Filename of synthetic image. Requires: bvec_file/b0_file.' - syn_file = traits.File( + syn_file = File( name_source=['source_file'], name_template='%s_syn.nii.gz', desc=desc, argstr='-syn %s', requires=['bvec_file', 'b0_file']) - mdmap_file = traits.File( + mdmap_file = File( name_source=['source_file'], name_template='%s_mdmap.nii.gz', desc='Filename of MD map/ADC', argstr='-mdmap %s') - famap_file = traits.File( + famap_file = File( name_source=['source_file'], name_template='%s_famap.nii.gz', desc='Filename of FA map', argstr='-famap %s') - v1map_file = traits.File( + v1map_file = File( name_source=['source_file'], name_template='%s_v1map.nii.gz', desc='Filename of PDD map [x,y,z]', argstr='-v1map %s') - rgbmap_file = traits.File( + rgbmap_file = File( name_source=['source_file'], name_template='%s_rgbmap.nii.gz', desc='Filename of colour FA map.', argstr='-rgbmap %s') - logdti_file = traits.File( + logdti_file = File( name_source=['source_file'], name_template='%s_logdti2.nii.gz', desc='Filename of output logdti map.', @@ -442,13 +442,13 @@ class DwiToolOutputSpec(TraitedSpec): desc = 'Filename of multi-compartment model parameter map \ (-ivim,-ball,-nod)' - mcmap_file = traits.File(desc=desc) - syn_file = traits.File(desc='Filename of synthetic image') - mdmap_file = traits.File(desc='Filename of MD map/ADC') - famap_file = traits.File(desc='Filename of FA map') - v1map_file = traits.File(desc='Filename of PDD map [x,y,z]') - rgbmap_file = traits.File(desc='Filename of colour FA map') - logdti_file = traits.File(desc='Filename of output logdti map') + mcmap_file = File(desc=desc) + syn_file = File(desc='Filename of synthetic image') + mdmap_file = File(desc='Filename of MD map/ADC') + famap_file = File(desc='Filename of FA map') + v1map_file = File(desc='Filename of PDD map [x,y,z]') + rgbmap_file = File(desc='Filename of colour FA map') + logdti_file = File(desc='Filename of output logdti map') class DwiTool(NiftyFitCommand): diff --git a/nipype/interfaces/niftyfit/qt1.py b/nipype/interfaces/niftyfit/qt1.py index ceefbae281..9df8034526 100644 --- a/nipype/interfaces/niftyfit/qt1.py +++ b/nipype/interfaces/niftyfit/qt1.py @@ -109,11 +109,11 @@ class FitQt1InputSpec(CommandLineInputSpec): desc='Inversion times for T1 data [1s,2s,5s].', argstr='-TIs %s', sep=' ') - tis_list = traits.File( + tis_list = File( exists=True, argstr='-TIlist %s', desc='Filename of list of pre-defined TIs.') - t1_list = traits.File( + t1_list = File( exists=True, argstr='-T1list %s', desc='Filename of list of pre-defined T1s') @@ -127,12 +127,12 @@ class FitQt1InputSpec(CommandLineInputSpec): flips = traits.List( traits.Float, desc='Flip angles', argstr='-flips %s', sep=' ') desc = 'Filename of list of pre-defined flip angles (deg).' - flips_list = traits.File(exists=True, argstr='-fliplist %s', desc=desc) + flips_list = File(exists=True, argstr='-fliplist %s', desc=desc) desc = 'Filename of B1 estimate for fitting (or include in prior).' - b1map = traits.File(exists=True, argstr='-b1map %s', desc=desc) + b1map = File(exists=True, argstr='-b1map %s', desc=desc) # MCMC options: - mcout = traits.File( + mcout = File( exists=True, desc='Filename of mc samples (ascii text file)', argstr='-mcout %s') diff --git a/nipype/interfaces/nipy/model.py b/nipype/interfaces/nipy/model.py index 0c2ddf4334..c26168e7b3 100644 --- a/nipype/interfaces/nipy/model.py +++ b/nipype/interfaces/nipy/model.py @@ -51,7 +51,7 @@ class FitGLMInputSpec(BaseInterfaceInputSpec): "is more time consuming but it supports " "autoregressive model"), usedefault=True) - mask = traits.File( + mask = File( exists=True, desc=("restrict the fitting only to the region defined " "by this mask")) @@ -72,7 +72,7 @@ class FitGLMOutputSpec(TraitedSpec): constants = traits.Any() axis = traits.Any() reg_names = traits.List() - residuals = traits.File() + residuals = File() a = File(exists=True) @@ -264,7 +264,7 @@ class EstimateContrastInputSpec(BaseInterfaceInputSpec): constants = traits.Any(mandatory=True) axis = traits.Any(mandatory=True) reg_names = traits.List(mandatory=True) - mask = traits.File(exists=True) + mask = File(exists=True) class EstimateContrastOutputSpec(TraitedSpec): diff --git a/nipype/interfaces/utility/base.py b/nipype/interfaces/utility/base.py index 864951f36a..d01a0a17b9 100644 --- a/nipype/interfaces/utility/base.py +++ b/nipype/interfaces/utility/base.py @@ -216,7 +216,7 @@ class RenameInputSpec(DynamicTraitedSpec): class RenameOutputSpec(TraitedSpec): - out_file = traits.File( + out_file = File( exists=True, desc="softlink to original file with new name") diff --git a/nipype/interfaces/utility/tests/test_wrappers.py b/nipype/interfaces/utility/tests/test_wrappers.py index 392ae094b0..380d840cee 100644 --- a/nipype/interfaces/utility/tests/test_wrappers.py +++ b/nipype/interfaces/utility/tests/test_wrappers.py @@ -51,7 +51,7 @@ def increment_array(in_array): def make_random_array(size): - return np.random.randn(size, size) + return np.random.randn(size, size) # noqa def should_fail(tmp): diff --git a/nipype/pipeline/engine/tests/test_base.py b/nipype/pipeline/engine/tests/test_base.py index fd87aa6878..3513152c06 100644 --- a/nipype/pipeline/engine/tests/test_base.py +++ b/nipype/pipeline/engine/tests/test_base.py @@ -13,7 +13,7 @@ class InputSpec(nib.TraitedSpec): input1 = nib.traits.Int(desc='a random int') input2 = nib.traits.Int(desc='a random int') - input_file = nib.traits.File(desc='Random File') + input_file = nib.File(desc='Random File') class OutputSpec(nib.TraitedSpec): From ad10517c362da6d8aa5b4c074a96df84a53dc722 Mon Sep 17 00:00:00 2001 From: oesteban Date: Fri, 19 Jul 2019 16:51:46 -0700 Subject: [PATCH 07/13] fix: potential fix for #2968 in the context of resolving/rebasing paths --- nipype/interfaces/base/traits_extension.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index 64bf45755f..76f62e27d1 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -539,10 +539,10 @@ def _recurse_on_path_traits(func, thistrait, value, cwd): elif thistrait.is_trait_type(traits.List): innertrait, = thistrait.inner_traits if not isinstance(value, (list, tuple)): - value = _recurse_on_path_traits(func, innertrait, value, cwd) - else: - value = [_recurse_on_path_traits(func, innertrait, v, cwd) - for v in value] + value = [value] + + value = [_recurse_on_path_traits(func, innertrait, v, cwd) + for v in value] elif thistrait.is_trait_type(traits.Dict): _, innertrait = thistrait.inner_traits value = {k: _recurse_on_path_traits(func, innertrait, v, cwd) From 83380fbc927fc317af5f700d6e95d308e770c51c Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 30 Jul 2019 21:47:14 -0700 Subject: [PATCH 08/13] fix(Either): better implementation that allows ``make specs`` --- nipype/interfaces/base/traits_extension.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index 76f62e27d1..69a66a3470 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -489,14 +489,19 @@ class Either(TraitType): def __init__(self, *traits, **metadata): """Create a trait whose value can be any of of a specified list of traits.""" - metadata['alternatives'] = tuple(trait_from(t) for t in traits) + _either_traits = tuple(trait_from(t) for t in traits) self.trait_maker = _TraitMaker( metadata.pop("default", None), *traits, **metadata) + self.either_traits = _either_traits def as_ctrait(self): """Return a CTrait corresponding to the trait defined by this class.""" return self.trait_maker.as_ctrait() + def inner_traits(self): + """Return the *inner trait* (or traits) for this trait.""" + return self.either_traits + traits.Tuple = Tuple traits.Either = Either @@ -550,14 +555,14 @@ def _recurse_on_path_traits(func, thistrait, value, cwd): elif thistrait.is_trait_type(Tuple): value = tuple([_recurse_on_path_traits(func, subtrait, v, cwd) for subtrait, v in zip(thistrait.inner_traits, value)]) - elif thistrait.alternatives: + elif thistrait.is_trait_type(Either): is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) - for f in thistrait.alternatives] + for f in thistrait.inner_traits] if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): return value - is_basepath = [f.is_trait_type(BasePath) for f in thistrait.alternatives] + is_basepath = [f.is_trait_type(BasePath) for f in thistrait.inner_traits] if any(is_basepath): - subtrait = thistrait.alternatives[is_basepath.index(True)] + subtrait = thistrait.inner_traits[is_basepath.index(True)] value = _recurse_on_path_traits(func, subtrait, value, cwd) return value From cf0403be7f0f60bf4119090df7d1007617b24e7e Mon Sep 17 00:00:00 2001 From: oesteban Date: Tue, 30 Jul 2019 23:37:41 -0700 Subject: [PATCH 09/13] fix(traits): avoid redefining ``Either`` and ``Tuple`` and new test-cases --- .../base/tests/test_traits_extension.py | 29 +++++++++-- nipype/interfaces/base/traits_extension.py | 52 +++++-------------- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 832f8f9592..0a4a23905b 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -13,10 +13,13 @@ class _test_spec(nib.TraitedSpec): c = nib.traits.List(nib.File()) d = nib.Either(nib.File(), nib.traits.Float()) e = nib.OutputMultiObject(nib.File()) + ee = nib.OutputMultiObject(nib.Str) f = nib.traits.Dict(nib.Str, nib.File()) g = nib.Either(nib.File, nib.Str) h = nib.Str - ee = nib.OutputMultiObject(nib.Str) + i = nib.Either(nib.File, nib.Tuple(nib.File, nib.traits.Int)) + j = nib.Either(nib.File, nib.Tuple(nib.File, nib.traits.Int), + nib.traits.Dict(nib.Str, nib.File())) def test_rebase_path_traits(): @@ -58,6 +61,11 @@ def test_rebase_path_traits(): '/some/path') assert e == [[Path('f1.txt'), Path('f2.txt')], [[Path('f3.txt')]]] + ee = rebase_path_traits( + spec.trait('ee'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], + '/some/path') + assert ee == [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + f = rebase_path_traits( spec.trait('f'), {'1': '/some/path/f1.txt'}, '/some/path') assert f == {'1': Path('f1.txt')} @@ -79,10 +87,21 @@ def test_rebase_path_traits(): h = rebase_path_traits(spec.trait('h'), '2', '/some/path') assert h == '2' - ee = rebase_path_traits( - spec.trait('ee'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], - '/some/path') - assert ee == [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + i = rebase_path_traits(spec.trait('i'), '/some/path/either/file.txt', '/some/path') + assert '%s' % i == 'either/file.txt' + + i = rebase_path_traits(spec.trait('i'), ('/some/path/either/tuple/file.txt', 2), '/some/path') + assert ('%s' % i[0], i[1]) == ('either/tuple/file.txt', 2) + + j = rebase_path_traits(spec.trait('j'), '/some/path/either/file.txt', '/some/path') + assert '%s' % j == 'either/file.txt' + + j = rebase_path_traits(spec.trait('j'), ('/some/path/either/tuple/file.txt', 2), '/some/path') + assert ('%s' % j[0], j[1]) == ('either/tuple/file.txt', 2) + + j = rebase_path_traits(spec.trait('j'), {'a': '/some/path/either/dict/file.txt'}, + '/some/path') + assert j == {'a': Path('either/dict/file.txt')} def test_resolve_path_traits(): diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index 69a66a3470..f6999480f5 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -471,36 +471,12 @@ class InputMultiObject(MultiObject): OutputMultiPath = OutputMultiObject -class Tuple(traits.BaseTuple): - """Defines a new type of Tuple trait that reports inner types.""" - - def init_fast_validator(self, *args): - """Set up the C-level fast validator.""" - super(Tuple, self).init_fast_validator(*args) - self.fast_validate = args - - def inner_traits(self): - """Return the *inner trait* (or traits) for this trait.""" - return self.types - - -class Either(TraitType): - """Defines a trait whose value can be any of of a specified list of traits.""" - - def __init__(self, *traits, **metadata): - """Create a trait whose value can be any of of a specified list of traits.""" - _either_traits = tuple(trait_from(t) for t in traits) - self.trait_maker = _TraitMaker( - metadata.pop("default", None), *traits, **metadata) - self.either_traits = _either_traits +class Tuple(traits.Tuple): + pass - def as_ctrait(self): - """Return a CTrait corresponding to the trait defined by this class.""" - return self.trait_maker.as_ctrait() - def inner_traits(self): - """Return the *inner trait* (or traits) for this trait.""" - return self.either_traits +class Either(traits.Either): + pass traits.Tuple = Tuple @@ -548,22 +524,22 @@ def _recurse_on_path_traits(func, thistrait, value, cwd): value = [_recurse_on_path_traits(func, innertrait, v, cwd) for v in value] - elif thistrait.is_trait_type(traits.Dict): + elif isinstance(value, dict) and thistrait.is_trait_type(traits.Dict): _, innertrait = thistrait.inner_traits value = {k: _recurse_on_path_traits(func, innertrait, v, cwd) for k, v in value.items()} - elif thistrait.is_trait_type(Tuple): + elif isinstance(value, tuple) and thistrait.is_trait_type(Tuple): value = tuple([_recurse_on_path_traits(func, subtrait, v, cwd) - for subtrait, v in zip(thistrait.inner_traits, value)]) - elif thistrait.is_trait_type(Either): - is_str = [f.is_trait_type((traits.String, traits.BaseStr, traits.BaseBytes, Str)) - for f in thistrait.inner_traits] + for subtrait, v in zip(thistrait.handler.types, value)]) + elif thistrait.is_trait_type(traits.TraitCompound): + is_str = [isinstance(f, (traits.String, traits.BaseStr, traits.BaseBytes, Str)) + for f in thistrait.handler.handlers] if any(is_str) and isinstance(value, (bytes, str)) and not value.startswith('/'): return value - is_basepath = [f.is_trait_type(BasePath) for f in thistrait.inner_traits] - if any(is_basepath): - subtrait = thistrait.inner_traits[is_basepath.index(True)] - value = _recurse_on_path_traits(func, subtrait, value, cwd) + + for subtrait in thistrait.handler.handlers: + value = _recurse_on_path_traits(func, subtrait(), value, cwd) + return value From 676f5666aebadde8b8d307c7b4fc310a16d6bd7e Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 31 Jul 2019 08:26:10 -0700 Subject: [PATCH 10/13] fix: final cleanup after rebase --- nipype/interfaces/base/__init__.py | 2 +- .../base/tests/test_traits_extension.py | 12 +++++------ nipype/interfaces/base/traits_extension.py | 20 +------------------ 3 files changed, 8 insertions(+), 26 deletions(-) diff --git a/nipype/interfaces/base/__init__.py b/nipype/interfaces/base/__init__.py index 672b30178b..a846794561 100644 --- a/nipype/interfaces/base/__init__.py +++ b/nipype/interfaces/base/__init__.py @@ -22,7 +22,7 @@ from .traits_extension import ( traits, Undefined, isdefined, has_metadata, File, ImageFile, Directory, - Tuple, Either, Str, DictStrStr, + Str, DictStrStr, OutputMultiObject, InputMultiObject, OutputMultiPath, InputMultiPath) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 0a4a23905b..1c60e8cc85 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -9,17 +9,17 @@ class _test_spec(nib.TraitedSpec): a = nib.File() - b = nib.Tuple(nib.File(), nib.File()) + b = nib.traits.Tuple(nib.File(), nib.File()) c = nib.traits.List(nib.File()) - d = nib.Either(nib.File(), nib.traits.Float()) + d = nib.traits.Either(nib.File(), nib.traits.Float()) e = nib.OutputMultiObject(nib.File()) ee = nib.OutputMultiObject(nib.Str) f = nib.traits.Dict(nib.Str, nib.File()) - g = nib.Either(nib.File, nib.Str) + g = nib.traits.Either(nib.File, nib.Str) h = nib.Str - i = nib.Either(nib.File, nib.Tuple(nib.File, nib.traits.Int)) - j = nib.Either(nib.File, nib.Tuple(nib.File, nib.traits.Int), - nib.traits.Dict(nib.Str, nib.File())) + i = nib.traits.Either(nib.File, nib.traits.Tuple(nib.File, nib.traits.Int)) + j = nib.traits.Either(nib.File, nib.traits.Tuple(nib.File, nib.traits.Int), + nib.traits.Dict(nib.Str, nib.File())) def test_rebase_path_traits(): diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index f6999480f5..2a824c1fa0 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -30,7 +30,6 @@ import traits.api as traits from traits.trait_handlers import TraitType, NoDefaultSpecified from traits.trait_base import _Undefined -from traits.traits import _TraitMaker, trait_from from traits.api import Unicode from future import standard_library @@ -39,11 +38,6 @@ if USING_PATHLIB2: from future.types.newstr import newstr -try: - from pathlib import Path -except ImportError: - from pathlib2 import Path - if traits_version < '3.7.0': raise ImportError('Traits version 3.7.0 or higher must be installed') @@ -471,18 +465,6 @@ class InputMultiObject(MultiObject): OutputMultiPath = OutputMultiObject -class Tuple(traits.Tuple): - pass - - -class Either(traits.Either): - pass - - -traits.Tuple = Tuple -traits.Either = Either - - def _rebase_path(value, cwd): if isinstance(value, list): return [_rebase_path(v, cwd) for v in value] @@ -528,7 +510,7 @@ def _recurse_on_path_traits(func, thistrait, value, cwd): _, innertrait = thistrait.inner_traits value = {k: _recurse_on_path_traits(func, innertrait, v, cwd) for k, v in value.items()} - elif isinstance(value, tuple) and thistrait.is_trait_type(Tuple): + elif isinstance(value, tuple) and thistrait.is_trait_type(traits.Tuple): value = tuple([_recurse_on_path_traits(func, subtrait, v, cwd) for subtrait, v in zip(thistrait.handler.types, value)]) elif thistrait.is_trait_type(traits.TraitCompound): From 17e48d7288676a2044802f404f11db90a795f8de Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 31 Jul 2019 11:52:29 -0700 Subject: [PATCH 11/13] fix(tests): combine rebase/resolve tests to check idempotence --- .../base/tests/test_traits_extension.py | 232 +++++++++--------- 1 file changed, 118 insertions(+), 114 deletions(-) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 1c60e8cc85..6f6fb6edc1 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -1,6 +1,6 @@ -# -*- coding: utf-8 -*- # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- # vi: set ft=python sts=4 ts=4 sw=4 et: +"""Check the resolving/rebasing feature of ``BasePath``s.""" from __future__ import print_function, unicode_literals from ... import base as nib @@ -22,157 +22,161 @@ class _test_spec(nib.TraitedSpec): nib.traits.Dict(nib.Str, nib.File())) -def test_rebase_path_traits(): - """Check rebase_path_traits.""" +def test_rebase_resolve_path_traits(): + """Check rebase_path_traits and resolve_path_traits and idempotence.""" spec = _test_spec() - a = rebase_path_traits( - spec.trait('a'), '/some/path/f1.txt', '/some/path') + v = '/some/path/f1.txt' + a = rebase_path_traits(spec.trait('a'), v, '/some/path') assert a == Path('f1.txt') - a = rebase_path_traits( - spec.trait('a'), '/some/path/f1.txt', '/some/other/path') - assert a == Path('/some/path/f1.txt') + a = resolve_path_traits(spec.trait('a'), a, '/some/path') + assert a == Path(v) - b = rebase_path_traits( - spec.trait('b'), ('/some/path/f1.txt', '/some/path/f2.txt'), '/some/path') + a = rebase_path_traits(spec.trait('a'), v, '/some/other/path') + assert a == Path(v) + + a = resolve_path_traits(spec.trait('a'), a, '/some/path') + assert a == Path(v) + + v = ('/some/path/f1.txt', '/some/path/f2.txt') + b = rebase_path_traits(spec.trait('b'), v, '/some/path') assert b == (Path('f1.txt'), Path('f2.txt')) - c = rebase_path_traits( - spec.trait('c'), ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'], - '/some/path') + b = resolve_path_traits(spec.trait('b'), b, '/some/path') + assert b == (Path(v[0]), Path(v[1])) + + v = ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'] + c = rebase_path_traits(spec.trait('c'), v, '/some/path') assert c == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] - d = rebase_path_traits( - spec.trait('d'), 2.0, '/some/path') - assert d == 2.0 + c = resolve_path_traits(spec.trait('c'), c, '/some/path') + assert c == [Path(vp) for vp in v] - d = rebase_path_traits( - spec.trait('d'), '/some/path/either.txt', '/some/path') - assert '%s' % d == 'either.txt' + v = 2.0 + d = rebase_path_traits(spec.trait('d'), v, '/some/path') + assert d == v - e = rebase_path_traits( - spec.trait('e'), ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'], - '/some/path') - assert e == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] + d = resolve_path_traits(spec.trait('d'), d, '/some/path') + assert d == v - e = rebase_path_traits( - spec.trait('e'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], - '/some/path') - assert e == [[Path('f1.txt'), Path('f2.txt')], [[Path('f3.txt')]]] + v = '/some/path/either.txt' + d = rebase_path_traits(spec.trait('d'), v, '/some/path') + assert d == Path('either.txt') - ee = rebase_path_traits( - spec.trait('ee'), [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]], - '/some/path') - assert ee == [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + d = resolve_path_traits(spec.trait('d'), d, '/some/path') + assert d == Path(v) - f = rebase_path_traits( - spec.trait('f'), {'1': '/some/path/f1.txt'}, '/some/path') - assert f == {'1': Path('f1.txt')} + v = ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'] + e = rebase_path_traits(spec.trait('e'), v, '/some/path') + assert e == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] - g = rebase_path_traits( - spec.trait('g'), 'some/path/either.txt', '/some/path') - assert '%s' % g == 'some/path/either.txt' + e = resolve_path_traits(spec.trait('e'), e, '/some/path') + assert e == [Path(vp) for vp in v] - g = rebase_path_traits( - spec.trait('g'), '/some/path/either.txt', '/some') - assert '%s' % g == 'path/either.txt' + v = [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + e = rebase_path_traits(spec.trait('e'), v, '/some/path') + assert e == [[Path('f1.txt'), Path('f2.txt')], [[Path('f3.txt')]]] - g = rebase_path_traits(spec.trait('g'), 'string', '/some') - assert '%s' % g == 'string' + e = resolve_path_traits(spec.trait('e'), e, '/some/path') + assert e == [[[Path(vpp) for vpp in vp] if isinstance(vp, list) else Path(vp) for vp in inner] + for inner in v] - g = rebase_path_traits(spec.trait('g'), '2', '/some/path') - assert g == '2' # You dont want this one to be a Path + # These are Str - no rebasing/resolving should happen + v = [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] + ee = rebase_path_traits(spec.trait('ee'), v, '/some/path') + assert ee == v - h = rebase_path_traits(spec.trait('h'), '2', '/some/path') - assert h == '2' + ee = resolve_path_traits(spec.trait('ee'), [['f1.txt', 'f2.txt'], [['f3.txt']]], '/some/path') + assert ee == [['f1.txt', 'f2.txt'], [['f3.txt']]] - i = rebase_path_traits(spec.trait('i'), '/some/path/either/file.txt', '/some/path') - assert '%s' % i == 'either/file.txt' + v = {'1': '/some/path/f1.txt'} + f = rebase_path_traits(spec.trait('f'), v, '/some') + assert f == {'1': Path('path/f1.txt')} - i = rebase_path_traits(spec.trait('i'), ('/some/path/either/tuple/file.txt', 2), '/some/path') - assert ('%s' % i[0], i[1]) == ('either/tuple/file.txt', 2) + f = resolve_path_traits(spec.trait('f'), f, '/some') + assert f == {k: Path(val) for k, val in v.items()} - j = rebase_path_traits(spec.trait('j'), '/some/path/either/file.txt', '/some/path') - assert '%s' % j == 'either/file.txt' + # Either(Str, File): passing in path-like apply manipulation + v = '/some/path/either.txt' + g = rebase_path_traits(spec.trait('g'), v, '/some/path') + assert g == Path('either.txt') - j = rebase_path_traits(spec.trait('j'), ('/some/path/either/tuple/file.txt', 2), '/some/path') - assert ('%s' % j[0], j[1]) == ('either/tuple/file.txt', 2) + g = resolve_path_traits(spec.trait('g'), g, '/some/path') + assert g == Path(v) - j = rebase_path_traits(spec.trait('j'), {'a': '/some/path/either/dict/file.txt'}, - '/some/path') - assert j == {'a': Path('either/dict/file.txt')} + g = rebase_path_traits(spec.trait('g'), v, '/some') + assert g == Path('path/either.txt') + g = resolve_path_traits(spec.trait('g'), g, '/some') + assert g == Path(v) -def test_resolve_path_traits(): - """Check resolve_path_traits.""" - spec = _test_spec() + # Either(Str, File): passing str discards File + v = 'either.txt' + g = rebase_path_traits(spec.trait('g'), v, '/some/path') + assert g == v + + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + # In this implementation, strings take precedence + g = resolve_path_traits(spec.trait('g'), g, '/some/path') + assert g == v - a = resolve_path_traits( - spec.trait('a'), 'f1.txt', '/some/path') - assert a == Path('/some/path/f1.txt') + v = 'string' + g = rebase_path_traits(spec.trait('g'), v, '/some') + assert g == v - a = resolve_path_traits( - spec.trait('a'), '/already/absolute/f1.txt', '/some/path') - assert a == Path('/already/absolute/f1.txt') + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + g = resolve_path_traits(spec.trait('g'), v, '/some') + assert g == v - b = resolve_path_traits( - spec.trait('b'), ('f1.txt', 'f2.txt'), '/some/path') - assert b == (Path('/some/path/f1.txt'), Path('/some/path/f2.txt')) + v = v + g = rebase_path_traits(spec.trait('g'), v, '/some/path') + assert g == v # You dont want this one to be a Path - c = resolve_path_traits( - spec.trait('c'), ['f1.txt', 'f2.txt', 'f3.txt'], - '/some/path') - assert c == [Path('/some/path/f1.txt'), Path('/some/path/f2.txt'), Path('/some/path/f3.txt')] + # This is a problematic case, it is impossible to know whether this + # was meant to be a string or a file. + g = resolve_path_traits(spec.trait('g'), g, '/some/path') + assert g == v # You dont want this one to be a Path - d = resolve_path_traits( - spec.trait('d'), 2.0, '/some/path') - assert d == 2.0 + h = rebase_path_traits(spec.trait('h'), v, '/some/path') + assert h == v - d = resolve_path_traits( - spec.trait('d'), 'either.txt', '/some/path') - assert '%s' % d == '/some/path/either.txt' + h = resolve_path_traits(spec.trait('h'), h, '/some/path') + assert h == v - e = resolve_path_traits( - spec.trait('e'), ['f1.txt', 'f2.txt', 'f3.txt'], - '/some/path') - assert e == [Path('/some/path/f1.txt'), Path('/some/path/f2.txt'), Path('/some/path/f3.txt')] + v = '/some/path/either/file.txt' + i = rebase_path_traits(spec.trait('i'), v, '/some/path') + assert i == Path('either/file.txt') - e = resolve_path_traits( - spec.trait('e'), [['f1.txt', 'f2.txt'], [['f3.txt']]], - '/some/path') - assert e == [[Path('/some/path/f1.txt'), Path('/some/path/f2.txt')], - [[Path('/some/path/f3.txt')]]] + i = resolve_path_traits(spec.trait('i'), i, '/some/path') + assert i == Path(v) - f = resolve_path_traits( - spec.trait('f'), {'1': 'path/f1.txt'}, '/some') - assert f == {'1': Path('/some/path/f1.txt')} + v = ('/some/path/either/tuple/file.txt', 2) + i = rebase_path_traits(spec.trait('i'), v, '/some/path') + assert i == (Path('either/tuple/file.txt'), 2) - g = resolve_path_traits( - spec.trait('g'), '/either.txt', '/some/path') - assert g == Path('/either.txt') + i = resolve_path_traits(spec.trait('i'), i, '/some/path') + assert i == (Path(v[0]), v[1]) - # This is a problematic case, it is impossible to know whether this - # was meant to be a string or a file. - # In this implementation, strings take precedence - g = resolve_path_traits( - spec.trait('g'), 'path/either.txt', '/some') - assert g == 'path/either.txt' + v = '/some/path/either/file.txt' + j = rebase_path_traits(spec.trait('j'), v, '/some/path') + assert j == Path('either/file.txt') - # This is a problematic case, it is impossible to know whether this - # was meant to be a string or a file. - g = resolve_path_traits(spec.trait('g'), 'string', '/some') - assert g == 'string' + j = resolve_path_traits(spec.trait('j'), j, '/some/path') + assert j == Path(v) - # This is a problematic case, it is impossible to know whether this - # was meant to be a string or a file. - g = resolve_path_traits(spec.trait('g'), '2', '/some/path') - assert g == '2' # You dont want this one to be a Path + v = ('/some/path/either/tuple/file.txt', 2) + j = rebase_path_traits(spec.trait('j'), ('/some/path/either/tuple/file.txt', 2), '/some/path') + assert j == (Path('either/tuple/file.txt'), 2) - h = resolve_path_traits(spec.trait('h'), '2', '/some/path') - assert h == '2' + j = resolve_path_traits(spec.trait('j'), j, '/some/path') + assert j == (Path(v[0]), v[1]) - ee = resolve_path_traits( - spec.trait('ee'), [['f1.txt', 'f2.txt'], [['f3.txt']]], - '/some/path') - assert ee == [['f1.txt', 'f2.txt'], [['f3.txt']]] + v = {'a': '/some/path/either/dict/file.txt'} + j = rebase_path_traits(spec.trait('j'), v, '/some/path') + assert j == {'a': Path('either/dict/file.txt')} + + j = resolve_path_traits(spec.trait('j'), j, '/some/path') + assert j == {k: Path(val) for k, val in v.items()} From eb24c6b3b32a8d4735ff15778f649aae166721e1 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 31 Jul 2019 12:43:57 -0700 Subject: [PATCH 12/13] tests: add real idempotence tests --- .../base/tests/test_traits_extension.py | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 6f6fb6edc1..0834bc63f7 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -30,29 +30,53 @@ def test_rebase_resolve_path_traits(): a = rebase_path_traits(spec.trait('a'), v, '/some/path') assert a == Path('f1.txt') + # Idempotence + assert rebase_path_traits(spec.trait('a'), a, '/some/path') == a + a = resolve_path_traits(spec.trait('a'), a, '/some/path') assert a == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('a'), a, '/some/path') == a + a = rebase_path_traits(spec.trait('a'), v, '/some/other/path') assert a == Path(v) + # Idempotence + assert rebase_path_traits(spec.trait('a'), a, '/some/other/path') == a + a = resolve_path_traits(spec.trait('a'), a, '/some/path') assert a == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('a'), a, '/some/path') == a + v = ('/some/path/f1.txt', '/some/path/f2.txt') b = rebase_path_traits(spec.trait('b'), v, '/some/path') assert b == (Path('f1.txt'), Path('f2.txt')) + # Idempotence + assert rebase_path_traits(spec.trait('b'), b, '/some/path') == b + b = resolve_path_traits(spec.trait('b'), b, '/some/path') assert b == (Path(v[0]), Path(v[1])) + # Idempotence + assert resolve_path_traits(spec.trait('b'), b, '/some/path') == b + v = ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'] c = rebase_path_traits(spec.trait('c'), v, '/some/path') assert c == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] + # Idempotence + assert rebase_path_traits(spec.trait('c'), c, '/some/path') == c + c = resolve_path_traits(spec.trait('c'), c, '/some/path') assert c == [Path(vp) for vp in v] + # Idempotence + assert resolve_path_traits(spec.trait('c'), c, '/some/path') == c + v = 2.0 d = rebase_path_traits(spec.trait('d'), v, '/some/path') assert d == v @@ -64,119 +88,215 @@ def test_rebase_resolve_path_traits(): d = rebase_path_traits(spec.trait('d'), v, '/some/path') assert d == Path('either.txt') + # Idempotence + assert rebase_path_traits(spec.trait('d'), d, '/some/path') == d + d = resolve_path_traits(spec.trait('d'), d, '/some/path') assert d == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('d'), d, '/some/path') == d + v = ['/some/path/f1.txt', '/some/path/f2.txt', '/some/path/f3.txt'] e = rebase_path_traits(spec.trait('e'), v, '/some/path') assert e == [Path('f1.txt'), Path('f2.txt'), Path('f3.txt')] + # Idempotence + assert rebase_path_traits(spec.trait('e'), e, '/some/path') == e + e = resolve_path_traits(spec.trait('e'), e, '/some/path') assert e == [Path(vp) for vp in v] + # Idempotence + assert resolve_path_traits(spec.trait('e'), e, '/some/path') == e + v = [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] e = rebase_path_traits(spec.trait('e'), v, '/some/path') assert e == [[Path('f1.txt'), Path('f2.txt')], [[Path('f3.txt')]]] + # Idempotence + assert rebase_path_traits(spec.trait('e'), e, '/some/path') == e + e = resolve_path_traits(spec.trait('e'), e, '/some/path') assert e == [[[Path(vpp) for vpp in vp] if isinstance(vp, list) else Path(vp) for vp in inner] for inner in v] + # Idempotence + assert resolve_path_traits(spec.trait('e'), e, '/some/path') == e + # These are Str - no rebasing/resolving should happen v = [['/some/path/f1.txt', '/some/path/f2.txt'], [['/some/path/f3.txt']]] ee = rebase_path_traits(spec.trait('ee'), v, '/some/path') assert ee == v + # Idempotence + assert rebase_path_traits(spec.trait('ee'), ee, '/some/path') == ee + ee = resolve_path_traits(spec.trait('ee'), [['f1.txt', 'f2.txt'], [['f3.txt']]], '/some/path') assert ee == [['f1.txt', 'f2.txt'], [['f3.txt']]] + # Idempotence + assert resolve_path_traits(spec.trait('ee'), ee, '/some/path') == ee + v = {'1': '/some/path/f1.txt'} f = rebase_path_traits(spec.trait('f'), v, '/some') assert f == {'1': Path('path/f1.txt')} + # Idempotence + assert rebase_path_traits(spec.trait('f'), f, '/some') == f + f = resolve_path_traits(spec.trait('f'), f, '/some') assert f == {k: Path(val) for k, val in v.items()} + # Idempotence + assert resolve_path_traits(spec.trait('f'), f, '/some') == f + # Either(Str, File): passing in path-like apply manipulation v = '/some/path/either.txt' g = rebase_path_traits(spec.trait('g'), v, '/some/path') assert g == Path('either.txt') + # Idempotence + assert rebase_path_traits(spec.trait('g'), g, '/some/path') == g + g = resolve_path_traits(spec.trait('g'), g, '/some/path') assert g == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('g'), g, '/some/path') == g + g = rebase_path_traits(spec.trait('g'), v, '/some') assert g == Path('path/either.txt') + # Idempotence + assert rebase_path_traits(spec.trait('g'), g, '/some/path') == g + g = resolve_path_traits(spec.trait('g'), g, '/some') assert g == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('g'), g, '/some/path') == g + # Either(Str, File): passing str discards File v = 'either.txt' g = rebase_path_traits(spec.trait('g'), v, '/some/path') assert g == v + # Idempotence + assert rebase_path_traits(spec.trait('g'), g, '/some/path') == g + # This is a problematic case, it is impossible to know whether this # was meant to be a string or a file. # In this implementation, strings take precedence g = resolve_path_traits(spec.trait('g'), g, '/some/path') assert g == v + # Idempotence + assert resolve_path_traits(spec.trait('g'), g, '/some/path') == g + v = 'string' g = rebase_path_traits(spec.trait('g'), v, '/some') assert g == v + # Idempotence + assert rebase_path_traits(spec.trait('g'), g, '/some') == g + # This is a problematic case, it is impossible to know whether this # was meant to be a string or a file. g = resolve_path_traits(spec.trait('g'), v, '/some') assert g == v + # Idempotence + assert resolve_path_traits(spec.trait('g'), g, '/some') == g + v = v g = rebase_path_traits(spec.trait('g'), v, '/some/path') assert g == v # You dont want this one to be a Path + # Idempotence + assert rebase_path_traits(spec.trait('g'), g, '/some/path') == g + # This is a problematic case, it is impossible to know whether this # was meant to be a string or a file. g = resolve_path_traits(spec.trait('g'), g, '/some/path') assert g == v # You dont want this one to be a Path + # Idempotence + assert resolve_path_traits(spec.trait('g'), g, '/some/path') == g + h = rebase_path_traits(spec.trait('h'), v, '/some/path') assert h == v + # Idempotence + assert rebase_path_traits(spec.trait('h'), h, '/some/path') == h + h = resolve_path_traits(spec.trait('h'), h, '/some/path') assert h == v + # Idempotence + assert resolve_path_traits(spec.trait('h'), h, '/some/path') == h + v = '/some/path/either/file.txt' i = rebase_path_traits(spec.trait('i'), v, '/some/path') assert i == Path('either/file.txt') + # Idempotence + assert rebase_path_traits(spec.trait('i'), i, '/some/path') == i + i = resolve_path_traits(spec.trait('i'), i, '/some/path') assert i == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('i'), i, '/some/path') == i + v = ('/some/path/either/tuple/file.txt', 2) i = rebase_path_traits(spec.trait('i'), v, '/some/path') assert i == (Path('either/tuple/file.txt'), 2) + # Idempotence + assert rebase_path_traits(spec.trait('i'), i, '/some/path') == i + i = resolve_path_traits(spec.trait('i'), i, '/some/path') assert i == (Path(v[0]), v[1]) + # Idempotence + assert resolve_path_traits(spec.trait('i'), i, '/some/path') == i + v = '/some/path/either/file.txt' j = rebase_path_traits(spec.trait('j'), v, '/some/path') assert j == Path('either/file.txt') + # Idempotence + assert rebase_path_traits(spec.trait('j'), j, '/some/path') == j + j = resolve_path_traits(spec.trait('j'), j, '/some/path') assert j == Path(v) + # Idempotence + assert resolve_path_traits(spec.trait('j'), j, '/some/path') == j + v = ('/some/path/either/tuple/file.txt', 2) j = rebase_path_traits(spec.trait('j'), ('/some/path/either/tuple/file.txt', 2), '/some/path') assert j == (Path('either/tuple/file.txt'), 2) + # Idempotence + assert rebase_path_traits(spec.trait('j'), j, '/some/path') == j + j = resolve_path_traits(spec.trait('j'), j, '/some/path') assert j == (Path(v[0]), v[1]) + # Idempotence + assert resolve_path_traits(spec.trait('j'), j, '/some/path') == j + v = {'a': '/some/path/either/dict/file.txt'} j = rebase_path_traits(spec.trait('j'), v, '/some/path') assert j == {'a': Path('either/dict/file.txt')} + # Idempotence + assert rebase_path_traits(spec.trait('j'), j, '/some/path') == j + j = resolve_path_traits(spec.trait('j'), j, '/some/path') assert j == {k: Path(val) for k, val in v.items()} + + # Idempotence + assert resolve_path_traits(spec.trait('j'), j, '/some/path') == j From b0c80d7bd50ad856f0c59a89c967b85fe3ecc781 Mon Sep 17 00:00:00 2001 From: oesteban Date: Wed, 31 Jul 2019 17:22:44 -0700 Subject: [PATCH 13/13] fix: cover one more edge case (``nipype.interfaces.base.DictStrStr``) --- .../interfaces/base/tests/test_traits_extension.py | 12 +++++++++++- nipype/interfaces/base/traits_extension.py | 7 ++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/nipype/interfaces/base/tests/test_traits_extension.py b/nipype/interfaces/base/tests/test_traits_extension.py index 0834bc63f7..8138d691f3 100644 --- a/nipype/interfaces/base/tests/test_traits_extension.py +++ b/nipype/interfaces/base/tests/test_traits_extension.py @@ -20,6 +20,7 @@ class _test_spec(nib.TraitedSpec): i = nib.traits.Either(nib.File, nib.traits.Tuple(nib.File, nib.traits.Int)) j = nib.traits.Either(nib.File, nib.traits.Tuple(nib.File, nib.traits.Int), nib.traits.Dict(nib.Str, nib.File())) + k = nib.DictStrStr def test_rebase_resolve_path_traits(): @@ -209,7 +210,6 @@ def test_rebase_resolve_path_traits(): # Idempotence assert resolve_path_traits(spec.trait('g'), g, '/some') == g - v = v g = rebase_path_traits(spec.trait('g'), v, '/some/path') assert g == v # You dont want this one to be a Path @@ -300,3 +300,13 @@ def test_rebase_resolve_path_traits(): # Idempotence assert resolve_path_traits(spec.trait('j'), j, '/some/path') == j + + v = {'path': '/some/path/f1.txt'} + k = rebase_path_traits(spec.trait('k'), v, '/some/path') + assert k == v + + # Idempotence + assert rebase_path_traits(spec.trait('k'), k, '/some/path') == k + + k = resolve_path_traits(spec.trait('k'), k, '/some/path') + assert k == v diff --git a/nipype/interfaces/base/traits_extension.py b/nipype/interfaces/base/traits_extension.py index 2a824c1fa0..585a385feb 100644 --- a/nipype/interfaces/base/traits_extension.py +++ b/nipype/interfaces/base/traits_extension.py @@ -520,7 +520,12 @@ def _recurse_on_path_traits(func, thistrait, value, cwd): return value for subtrait in thistrait.handler.handlers: - value = _recurse_on_path_traits(func, subtrait(), value, cwd) + try: + sb_instance = subtrait() + except TypeError: + return value + else: + value = _recurse_on_path_traits(func, sb_instance, value, cwd) return value