diff --git a/.travis.yml b/.travis.yml index 4a49e0778d..5e7790b1b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,6 +45,7 @@ install: - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then conda install --yes vtk; fi - pip install python-coveralls - pip install nose-cov +- pip install mock # Add tvtk (PIL is required by blockcanvas) # Install mayavi (see https://github.com/enthought/mayavi/issues/271) - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then diff --git a/circle.yml b/circle.yml index e9c59276a2..acc345115e 100644 --- a/circle.yml +++ b/circle.yml @@ -21,7 +21,7 @@ dependencies: # Set up python environment - pip install --upgrade pip - pip install -e . - - pip install matplotlib sphinx ipython boto coverage dipy + - pip install matplotlib sphinx ipython boto coverage dipy mock # Add tvtk - pip install http://effbot.org/downloads/Imaging-1.1.7.tar.gz - pip install -e git+https://github.com/enthought/etsdevtools.git#egg=etsdevtools diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 1aea8cdbae..c476ed003a 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -15,6 +15,7 @@ from __future__ import division from builtins import zip from builtins import range +from future.utils import raise_from import os import os.path as op @@ -857,9 +858,9 @@ def __init__(self, infields=None, force_run=True, **kwargs): def _run_interface(self, runtime): try: import pandas as pd - except ImportError: - raise ImportError(('This interface requires pandas ' - '(http://pandas.pydata.org/) to run.')) + except ImportError as e: + raise_from(ImportError('This interface requires pandas ' + '(http://pandas.pydata.org/) to run.'), e) try: import lockfile as pl diff --git a/nipype/external/six.py b/nipype/external/six.py index 876aed3945..190c0239cd 100644 --- a/nipype/external/six.py +++ b/nipype/external/six.py @@ -29,12 +29,13 @@ import types __author__ = "Benjamin Peterson " -__version__ = "1.9.0" +__version__ = "1.10.0" # Useful for very coarse version differentiation. PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) if PY3: string_types = str, @@ -57,6 +58,7 @@ else: # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): + def __len__(self): return 1 << 31 try: @@ -160,6 +162,7 @@ def _resolve(self): class _SixMetaPathImporter(object): + """ A meta path importer to import six.moves and its submodules. @@ -224,6 +227,7 @@ def get_code(self, fullname): class _MovedItems(_LazyModule): + """Lazy loading of moved objects""" __path__ = [] # mark as package @@ -235,8 +239,10 @@ class _MovedItems(_LazyModule): MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("intern", "__builtin__", "sys"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "imp", "reload"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), MovedAttribute("StringIO", "StringIO", "io"), @@ -246,7 +252,6 @@ class _MovedItems(_LazyModule): MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), @@ -293,8 +298,13 @@ class _MovedItems(_LazyModule): MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), - MovedModule("winreg", "_winreg"), ] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) if isinstance(attr, MovedModule): @@ -308,6 +318,7 @@ class _MovedItems(_LazyModule): class Module_six_moves_urllib_parse(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_parse""" @@ -347,6 +358,7 @@ class Module_six_moves_urllib_parse(_LazyModule): class Module_six_moves_urllib_error(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_error""" @@ -366,6 +378,7 @@ class Module_six_moves_urllib_error(_LazyModule): class Module_six_moves_urllib_request(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_request""" @@ -415,6 +428,7 @@ class Module_six_moves_urllib_request(_LazyModule): class Module_six_moves_urllib_response(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_response""" @@ -435,6 +449,7 @@ class Module_six_moves_urllib_response(_LazyModule): class Module_six_moves_urllib_robotparser(_LazyModule): + """Lazy loading of moved objects in six.moves.urllib_robotparser""" @@ -452,6 +467,7 @@ class Module_six_moves_urllib_robotparser(_LazyModule): class Module_six_moves_urllib(types.ModuleType): + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" __path__ = [] # mark as package parse = _importer._get_module("moves.urllib_parse") @@ -522,6 +538,9 @@ def get_unbound_function(unbound): create_bound_method = types.MethodType + def create_unbound_method(func, cls): + return func + Iterator = object else: def get_unbound_function(unbound): @@ -530,6 +549,9 @@ def get_unbound_function(unbound): def create_bound_method(func, obj): return types.MethodType(func, obj, obj.__class__) + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + class Iterator(object): def next(self): @@ -568,16 +590,16 @@ def iterlists(d, **kw): viewitems = operator.methodcaller("items") else: def iterkeys(d, **kw): - return iter(d.iterkeys(**kw)) + return d.iterkeys(**kw) def itervalues(d, **kw): - return iter(d.itervalues(**kw)) + return d.itervalues(**kw) def iteritems(d, **kw): - return iter(d.iteritems(**kw)) + return d.iteritems(**kw) def iterlists(d, **kw): - return iter(d.iterlists(**kw)) + return d.iterlists(**kw) viewkeys = operator.methodcaller("viewkeys") @@ -600,12 +622,9 @@ def b(s): def u(s): return s unichr = chr - if sys.version_info[1] <= 1: - def int2byte(i): - return bytes((i,)) - else: - # This is about 2x faster than the implementation above on 3.2+ - int2byte = operator.methodcaller("to_bytes", 1, "big") + import struct + int2byte = struct.Struct(">B").pack + del struct byte2int = operator.itemgetter(0) indexbytes = operator.getitem iterbytes = iter @@ -613,8 +632,12 @@ def int2byte(i): StringIO = io.StringIO BytesIO = io.BytesIO _assertCountEqual = "assertCountEqual" - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" else: def b(s): return s @@ -780,6 +803,7 @@ def with_metaclass(meta, *bases): # metaclass for one level of class instantiation that replaces itself with # the actual metaclass. class metaclass(meta): + def __new__(cls, name, this_bases, d): return meta(name, bases, d) return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/nipype/interfaces/afni/base.py b/nipype/interfaces/afni/base.py index 431b024780..3438240741 100644 --- a/nipype/interfaces/afni/base.py +++ b/nipype/interfaces/afni/base.py @@ -5,6 +5,7 @@ import os from sys import platform from builtins import object +from future.utils import raise_from from ... import logging from ...utils.filemanip import split_filename @@ -82,9 +83,9 @@ def outputtype_to_ext(cls, outputtype): try: return cls.ftypes[outputtype] - except KeyError: + except KeyError as e: msg = 'Invalid AFNIOUTPUTTYPE: ', outputtype - raise KeyError(msg) + raise_from(KeyError(msg), e) @classmethod def outputtype(cls): diff --git a/nipype/pipeline/plugins/ipython.py b/nipype/pipeline/plugins/ipython.py index 1cb25f5e2a..cb31a697ee 100644 --- a/nipype/pipeline/plugins/ipython.py +++ b/nipype/pipeline/plugins/ipython.py @@ -5,6 +5,8 @@ from future import standard_library standard_library.install_aliases() +from future.utils import raise_from + from pickle import dumps import sys @@ -62,19 +64,20 @@ def run(self, graph, config, updatehash=False): name = 'ipyparallel' __import__(name) self.iparallel = sys.modules[name] - except ImportError: - raise ImportError("ipyparallel not found. Parallel execution " - "will be unavailable") + except ImportError as e: + raise_from(ImportError("ipyparallel not found. Parallel execution " + "will be unavailable"), e) try: self.taskclient = self.iparallel.Client() except Exception as e: if isinstance(e, TimeoutError): - raise Exception("No IPython clients found.") + raise_from(Exception("No IPython clients found."), e) if isinstance(e, IOError): - raise Exception("ipcluster/ipcontroller has not been started") + raise_from(Exception("ipcluster/ipcontroller has not been started"), e) if isinstance(e, ValueError): - raise Exception("Ipython kernel not installed") - raise e + raise_from(Exception("Ipython kernel not installed"), e) + else: + raise e return super(IPythonPlugin, self).run(graph, config, updatehash=updatehash) def _get_result(self, taskid): diff --git a/nipype/pipeline/plugins/ipythonx.py b/nipype/pipeline/plugins/ipythonx.py index 4421f35358..f825f7f4fa 100644 --- a/nipype/pipeline/plugins/ipythonx.py +++ b/nipype/pipeline/plugins/ipythonx.py @@ -4,6 +4,7 @@ """ import sys +from future.utils import raise_from from ...interfaces.base import LooseVersion IPython_not_loaded = False @@ -44,16 +45,16 @@ def run(self, graph, config, updatehash=False): name = 'IPython.kernel.client' __import__(name) self.ipyclient = sys.modules[name] - except ImportError: - raise ImportError("Ipython kernel not found. Parallel execution " - "will be unavailable") + except ImportError as e: + raise_from(ImportError("Ipython kernel not found. Parallel execution " + "will be unavailable"), e) try: self.taskclient = self.ipyclient.TaskClient() except Exception as e: if isinstance(e, ConnectionRefusedError): - raise Exception("No IPython clients found.") + raise_from(Exception("No IPython clients found."), e) if isinstance(e, ValueError): - raise Exception("Ipython kernel not installed") + raise_from(Exception("Ipython kernel not installed"), e) return super(IPythonXPlugin, self).run(graph, config, updatehash=updatehash) def _get_result(self, taskid): diff --git a/nipype/testing/tests/test_utils.py b/nipype/testing/tests/test_utils.py index d7bd7d6861..6902444ec8 100644 --- a/nipype/testing/tests/test_utils.py +++ b/nipype/testing/tests/test_utils.py @@ -5,8 +5,10 @@ import os import warnings +import subprocess +from mock import patch, MagicMock from nipype.testing.utils import TempFATFS -from nose.tools import assert_true +from nose.tools import assert_true, assert_raises def test_tempfatfs(): @@ -17,3 +19,12 @@ def test_tempfatfs(): else: with fatfs as tmpdir: yield assert_true, os.path.exists(tmpdir) + +@patch('subprocess.check_call', MagicMock( + side_effect=subprocess.CalledProcessError('',''))) +def test_tempfatfs_calledprocesserror(): + yield assert_raises, IOError, TempFATFS + +@patch('subprocess.Popen', MagicMock(side_effect=OSError())) +def test_tempfatfs_oserror(): + yield assert_raises, IOError, TempFATFS diff --git a/nipype/testing/utils.py b/nipype/testing/utils.py index 2e7404bcc5..abc0362d42 100644 --- a/nipype/testing/utils.py +++ b/nipype/testing/utils.py @@ -12,7 +12,7 @@ from tempfile import mkdtemp from ..utils.misc import package_check from nose import SkipTest - +from future.utils import raise_from def skip_if_no_package(*args, **kwargs): """Raise SkipTest if package_check fails @@ -62,15 +62,15 @@ def __init__(self, size_in_mbytes=8, delay=0.5): try: subprocess.check_call(args=mkfs_args, stdout=self.dev_null, stderr=self.dev_null) - except subprocess.CalledProcessError: - raise IOError("mkfs.vfat failed") + except subprocess.CalledProcessError as e: + raise_from(IOError("mkfs.vfat failed"), e) try: self.fusefat = subprocess.Popen(args=mount_args, stdout=self.dev_null, stderr=self.dev_null) - except OSError: - raise IOError("fusefat is not installed") + except OSError as e: + raise_from(IOError("fusefat is not installed"), e) time.sleep(self.delay) diff --git a/nipype/utils/misc.py b/nipype/utils/misc.py index 0c98900695..8024b3a67d 100644 --- a/nipype/utils/misc.py +++ b/nipype/utils/misc.py @@ -5,6 +5,7 @@ from future import standard_library standard_library.install_aliases() +from future.utils import raise_from from builtins import next from pickle import dumps, loads import inspect @@ -90,14 +91,14 @@ def create_function_from_source(function_source, imports=None): import_keys = list(ns.keys()) exec(function_source, ns) - except Exception as msg: - msg = str(msg) + '\nError executing function:\n %s\n' % function_source + except Exception as e: + msg = '\nError executing function:\n %s\n' % function_source msg += '\n'.join(["Functions in connection strings have to be standalone.", "They cannot be declared either interactively or inside", "another function or inline in the connect string. Any", "imports should be done inside the function" ]) - raise RuntimeError(msg) + raise_from(RuntimeError(msg), e) ns_funcs = list(set(ns) - set(import_keys + ['__builtins__'])) assert len(ns_funcs) == 1, "Function or inputs are ill-defined" funcname = ns_funcs[0] @@ -199,14 +200,14 @@ def package_check(pkg_name, version=None, app=None, checker=LooseVersion, msg += ' with version >= %s' % (version,) try: mod = __import__(pkg_name) - except ImportError: - raise exc_failed_import(msg) + except ImportError as e: + raise_from(exc_failed_import(msg), e) if not version: return try: have_version = mod.__version__ - except AttributeError: - raise exc_failed_check('Cannot find version for %s' % pkg_name) + except AttributeError as e: + raise_from(exc_failed_check('Cannot find version for %s' % pkg_name), e) if checker(have_version) < checker(version): raise exc_failed_check(msg) diff --git a/nipype/utils/spm_docs.py b/nipype/utils/spm_docs.py index dd720e024a..3dd5af528e 100644 --- a/nipype/utils/spm_docs.py +++ b/nipype/utils/spm_docs.py @@ -4,6 +4,7 @@ import os +from future.utils import raise_from from nipype.interfaces import matlab @@ -55,5 +56,5 @@ def _strip_header(doc): except ValueError: index = len(doc) return doc[:index] - except KeyError: - raise IOError('This docstring was not generated by Nipype!\n') + except KeyError as e: + raise_from(IOError('This docstring was not generated by Nipype!\n'), e) diff --git a/nipype/utils/tests/test_misc.py b/nipype/utils/tests/test_misc.py index 861ee4ec5e..5b458deb64 100644 --- a/nipype/utils/tests/test_misc.py +++ b/nipype/utils/tests/test_misc.py @@ -5,7 +5,8 @@ from builtins import next -from nipype.testing import assert_equal, assert_true, assert_false +from nipype.testing import (assert_equal, assert_true, assert_false, + assert_raises) from nipype.utils.misc import (container_to_string, getsource, create_function_from_source, str2bool, flatten, @@ -49,6 +50,9 @@ def func1(x): f_recreated = create_function_from_source(f_src) yield assert_equal, f(2.3), f_recreated(2.3) +def test_func_to_str_err(): + bad_src = "obbledygobbledygook" + yield assert_raises, RuntimeError, create_function_from_source, bad_src def test_str2bool(): yield assert_true, str2bool("yes") diff --git a/nipype/workflows/dmri/connectivity/group_connectivity.py b/nipype/workflows/dmri/connectivity/group_connectivity.py index e4651febd9..8ebf0a927d 100644 --- a/nipype/workflows/dmri/connectivity/group_connectivity.py +++ b/nipype/workflows/dmri/connectivity/group_connectivity.py @@ -1,4 +1,5 @@ from __future__ import print_function +from future.utils import raise_from import os.path as op @@ -459,9 +460,8 @@ def create_average_networks_by_group_workflow(group_list, data_dir, subjects_dir try: l4infosource.inputs.group_id1 = list(group_list.keys())[0] l4infosource.inputs.group_id2 = list(group_list.keys())[1] - except IndexError: - print('The create_average_networks_by_group_workflow requires 2 groups') - raise Exception + except IndexError as e: + raise_from(Exception('The create_average_networks_by_group_workflow requires 2 groups'), e) l4info = dict(networks=[['group_id', '']], CMatrices=[['group_id', '']], fibmean=[['group_id', 'mean_fiber_length']], fibdev=[['group_id', 'fiber_length_std']])