Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nipype/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def get_nipype_gitversion():
'packaging',
'futures; python_version == "2.7"',
'configparser; python_version <= "3.4"',
'pathlib2; python_version <= "3.4"',
]

TESTS_REQUIRES = [
Expand Down
5 changes: 4 additions & 1 deletion nipype/interfaces/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
This module defines the API of all nipype interfaces.

"""
from traits.trait_handlers import TraitDictObject, TraitListObject
from traits.trait_errors import TraitError

from .core import (Interface, BaseInterface, SimpleInterface, CommandLine,
StdOutCommandLine, MpiCommandLine, SEMLikeCommandLine,
LibraryBaseInterface, PackageInfo)
Expand All @@ -17,7 +20,7 @@
StdOutCommandLineInputSpec)

from .traits_extension import (
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined,
traits, Undefined, isdefined,
File, Directory, Str, DictStrStr, has_metadata, ImageFile,
OutputMultiObject, InputMultiObject,
OutputMultiPath, InputMultiPath)
Expand Down
3 changes: 2 additions & 1 deletion nipype/interfaces/base/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import simplejson as json
from dateutil.parser import parse as parseutc
from future import standard_library
from traits.trait_errors import TraitError

from ... import config, logging, LooseVersion
from ...utils.provenance import write_provenance
Expand All @@ -37,7 +38,7 @@

from ...external.due import due

from .traits_extension import traits, isdefined, TraitError
from .traits_extension import traits, isdefined
from .specs import (BaseInterfaceInputSpec, CommandLineInputSpec,
StdOutCommandLineInputSpec, MpiCommandLineInputSpec,
get_filecopy_info)
Expand Down
68 changes: 41 additions & 27 deletions nipype/interfaces/base/specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,20 @@
from builtins import str, bytes
from packaging.version import Version

from ...utils.filemanip import md5, hash_infile, hash_timestamp, to_str
from traits.trait_errors import TraitError
from traits.trait_handlers import TraitDictObject, TraitListObject
from ...utils.filemanip import (
md5, hash_infile, hash_timestamp, to_str, USING_PATHLIB2)
from .traits_extension import (
traits,
Undefined,
isdefined,
TraitError,
TraitDictObject,
TraitListObject,
has_metadata,
)

from ... import config, __version__


FLOAT_FORMAT = '{:.10f}'.format
nipype_version = Version(__version__)

Expand Down Expand Up @@ -314,6 +315,39 @@ def __all__(self):
return self.copyable_trait_names()


def _deepcopypatch(self, memo):
"""
Replace the ``__deepcopy__`` member with a traits-friendly implementation.
A bug in ``__deepcopy__`` for ``HasTraits`` results in weird cloning behaviors.
Occurs for all specs in Python<3 and only for DynamicTraitedSpec in Python>2.
"""
id_self = id(self)
if id_self in memo:
return memo[id_self]
dup_dict = deepcopy(self.trait_get(), memo)
# access all keys
for key in self.copyable_trait_names():
if key in self.__dict__.keys():
_ = getattr(self, key)
# clone once
dup = self.clone_traits(memo=memo)
for key in self.copyable_trait_names():
try:
_ = getattr(dup, key)
except:
pass
# clone twice
dup = self.clone_traits(memo=memo)
dup.trait_set(**dup_dict)
return dup


if USING_PATHLIB2:
BaseTraitedSpec.__deepcopy__ = _deepcopypatch


class TraitedSpec(BaseTraitedSpec):
""" Create a subclass with strict traits.
Expand All @@ -333,29 +367,9 @@ class DynamicTraitedSpec(BaseTraitedSpec):
functioning well together.
"""

def __deepcopy__(self, memo):
""" bug in deepcopy for HasTraits results in weird cloning behavior for
added traits
"""
id_self = id(self)
if id_self in memo:
return memo[id_self]
dup_dict = deepcopy(self.trait_get(), memo)
# access all keys
for key in self.copyable_trait_names():
if key in self.__dict__.keys():
_ = getattr(self, key)
# clone once
dup = self.clone_traits(memo=memo)
for key in self.copyable_trait_names():
try:
_ = getattr(dup, key)
except:
pass
# clone twice
dup = self.clone_traits(memo=memo)
dup.trait_set(**dup_dict)
return dup

if not USING_PATHLIB2:
DynamicTraitedSpec.__deepcopy__ = _deepcopypatch


class CommandLineInputSpec(BaseInterfaceInputSpec):
Expand Down
2 changes: 1 addition & 1 deletion nipype/interfaces/base/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def _inputs_help(cls):

>>> from nipype.interfaces.afni import GCOR
>>> _inputs_help(GCOR) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
['Inputs::', '', '\t[Mandatory]', '\tin_file: (an existing file name)', ...
['Inputs::', '', '\t[Mandatory]', '\tin_file: (a pathlike object or string...

"""
helpstr = ['Inputs::']
Expand Down
9 changes: 4 additions & 5 deletions nipype/interfaces/base/tests/test_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# 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 future import standard_library
import os
import warnings
from future import standard_library

import pytest

Expand Down Expand Up @@ -420,18 +420,17 @@ def test_ImageFile():
# setup traits
x.add_trait('nifti', nib.ImageFile(types=['nifti1', 'dicom']))
x.add_trait('anytype', nib.ImageFile())
x.add_trait('newtype', nib.ImageFile(types=['nifti10']))
with pytest.raises(ValueError):
x.add_trait('newtype', nib.ImageFile(types=['nifti10']))
x.add_trait('nocompress',
nib.ImageFile(types=['mgh'], allow_compressed=False))

with pytest.raises(nib.TraitError):
x.nifti = 'test.mgz'
x.nifti = 'test.nii'
x.anytype = 'test.xml'
with pytest.raises(AttributeError):
x.newtype = 'test.nii'
with pytest.raises(nib.TraitError):
x.nocompress = 'test.nii.gz'
x.nocompress = 'test.mgz'
x.nocompress = 'test.mgh'


Expand Down
Loading