Repository: nipy/nipype

Issue:
Consider alternative to using Exception.message to get message text
### Summary
Apparently, some `Exception` classes may not have a `.message` attribute. This can be more problematic when catching base `Exception` and then trying to log/warn using a string incorporating the original Exception's `.message` attribute. Maybe using `str(ex)` is more compatible, or more specific exception classes?

Example (from `master`):
https://github.com/nipy/nipype/blob/b356ad16828afd4c63f862dfc72fa7e841863391/nipype/utils/filemanip.py#L866-L877

will cause a throw exception if an `OSError` is caught, since OSError does not have a `.message` attribute (at least in python 3.8.3)
 
### Actual behavior
```
Python 3.8.3 (default, May 19 2020, 18:47:26) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = OSError('darn')
>>> a.message
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'OSError' object has no attribute 'message'
```
### Expected behavior
```
>>> str(a)
'darn'
```



Discussion:
I agree, let's move to str.

Resolving patch:
diff --git a/nipype/interfaces/io.py b/nipype/interfaces/io.py
--- a/nipype/interfaces/io.py
+++ b/nipype/interfaces/io.py
@@ -1010,11 +1010,8 @@ def _list_outputs(self):
                         try:
                             filledtemplate = template % tuple(argtuple)
                         except TypeError as e:
-                            raise TypeError(
-                                e.message
-                                + ": Template %s failed to convert with args %s"
-                                % (template, str(tuple(argtuple)))
-                            )
+                            raise TypeError(f"{e}: Template {template} failed to convert "
+                                            f"with args {tuple(argtuple)}")
                     outfiles = []
                     for fname in bkt_files:
                         if re.match(filledtemplate, fname):
@@ -1286,11 +1283,8 @@ def _list_outputs(self):
                         try:
                             filledtemplate = template % tuple(argtuple)
                         except TypeError as e:
-                            raise TypeError(
-                                e.message
-                                + ": Template %s failed to convert with args %s"
-                                % (template, str(tuple(argtuple)))
-                            )
+                            raise TypeError(f"{e}: Template {template} failed to convert "
+                                            f"with args {tuple(argtuple)}")
                     outfiles = glob.glob(filledtemplate)
                     if len(outfiles) == 0:
                         msg = "Output key: %s Template: %s returned no files" % (
@@ -2664,11 +2658,8 @@ def _list_outputs(self):
                         try:
                             filledtemplate = template % tuple(argtuple)
                         except TypeError as e:
-                            raise TypeError(
-                                e.message
-                                + ": Template %s failed to convert with args %s"
-                                % (template, str(tuple(argtuple)))
-                            )
+                            raise TypeError(f"{e}: Template {template} failed to convert "
+                                            f"with args {tuple(argtuple)}")
 
                     outputs[key].append(self._get_files_over_ssh(filledtemplate))
 
diff --git a/nipype/utils/filemanip.py b/nipype/utils/filemanip.py
--- a/nipype/utils/filemanip.py
+++ b/nipype/utils/filemanip.py
@@ -439,7 +439,7 @@ def copyfile(
             fmlogger.debug("Copying File: %s->%s", newfile, originalfile)
             shutil.copyfile(originalfile, newfile)
         except shutil.Error as e:
-            fmlogger.warning(e.message)
+            fmlogger.warning(str(e))
 
     # Associated files
     if copy_related_files:
@@ -870,10 +870,8 @@ def get_dependencies(name, environ):
         o, e = proc.communicate()
         deps = o.rstrip()
     except Exception as ex:
-        deps = '"%s" failed' % command
-        fmlogger.warning(
-            "Could not get dependencies of %s. Error:\n%s", name, ex.message
-        )
+        deps = f'{command!r} failed'
+        fmlogger.warning(f"Could not get dependencies of {name}s. Error:\n{ex}")
     return deps
 
 
diff --git a/nipype/utils/subprocess.py b/nipype/utils/subprocess.py
--- a/nipype/utils/subprocess.py
+++ b/nipype/utils/subprocess.py
@@ -124,7 +124,7 @@ def _process(drain=0):
                 res = select.select(streams, [], [], timeout)
             except select.error as e:
                 iflogger.info(e)
-                if e[0] == errno.EINTR:
+                if e.errno == errno.EINTR:
                     return
                 else:
                     raise


Test patch:
diff --git a/nipype/interfaces/base/tests/test_specs.py b/nipype/interfaces/base/tests/test_specs.py
--- a/nipype/interfaces/base/tests/test_specs.py
+++ b/nipype/interfaces/base/tests/test_specs.py
@@ -183,7 +183,7 @@ class DeprecationSpec3(nib.TraitedSpec):
         except nib.TraitError:
             not_raised = False
         assert not_raised
-        assert len(w) == 1, "deprecated warning 1 %s" % [w1.message for w1 in w]
+        assert len(w) == 1, f"deprecated warning 1 {[str(w1) for w1 in w]}"
 
     with warnings.catch_warnings(record=True) as w:
         warnings.filterwarnings("always", "", UserWarning)
@@ -201,7 +201,7 @@ class DeprecationSpec3(nib.TraitedSpec):
         assert not_raised
         assert spec_instance.foo == Undefined
         assert spec_instance.bar == 1
-        assert len(w) == 1, "deprecated warning 2 %s" % [w1.message for w1 in w]
+        assert len(w) == 1, f"deprecated warning 2 {[str(w1) for w1 in w]}"
 
 
 def test_namesource(setup_file):