Repository: nipy/nipype

Issue:
Please make error message more informative
### Summary
hard to figure out what went wrong/what I forgot to specify

### Actual behavior
```
(Pdb) p interface
<nipype.interfaces.ants.registration.ANTS object at 0x7f178f8d0810>
(Pdb) p interface.inputs

affine_gradient_descent_option = <undefined>
args = <undefined>
delta_time = <undefined>
dimension = 3
environ = {'NSLOTS': '1'}
fixed_image = [u'/input/fixed_image/1.nii.gz']
gradient_step_length = <undefined>
ignore_exception = False
metric = [u'MI']
metric_weight = <undefined>
mi_option = <undefined>
moving_image = [u'/input/moving_image/moving_image.nii.gz']
num_threads = 1
number_of_affine_iterations = <undefined>
number_of_iterations = <undefined>
number_of_time_steps = <undefined>
output_transform_prefix = out
radius = <undefined>
regularization = DMFFD
regularization_deformation_field_sigma = <undefined>
regularization_gradient_field_sigma = <undefined>
smoothing_sigmas = <undefined>
subsampling_factors = <undefined>
symmetry_type = <undefined>
terminal_output = stream
transformation_model = SyN
use_histogram_matching = False

(Pdb) p interface.run()
*** TypeError: TypeError("'_Undefined' object does not support indexing\nInterface ANTS failed to run. ",)
```

### Expected behavior
Something like
```
(Pdb) p interface.run()
*** TypeError: TypeError("Ain't it a lovely Friday.  I am sorry to report that you forgot to specify the damn field metric_weight, which lead to ANTS interface fail to run and us throw some nasty exception which we swallowed just in time but spitting out into your face just in case:  '_Undefined' object does not support indexing. ",)
```
?

### Platform details:

Please paste the output of: `python -c "import nipype; print(nipype.get_info()); print(nipype.__version__)"`

```
(Pdb) import nipype; print(nipype.get_info()); print(nipype.__version__)
{'nibabel_version': '2.2.0', 'networkx_version': '1.11', 'numpy_version': '1.12.1', 'sys_platform': 'linux2', 'sys_version': '2.7.13 (default, Jan 19 2017, 14:48:08) \n[GCC 6.3.0 20170118]', 'commit_source': '(none found)', 'commit_hash': '<not found>', 'pkg_path': '/usr/lib/python2.7/dist-packages/nipype', 'sys_executable': '/usr/bin/python', 'traits_version': '4.6.0', 'scipy_version': '0.18.1'}
```



Discussion:
I'd say this is actually a bug, not just an obscure error message.
@oesteban - I copied default value for `metric_weight` from other class, but should `radius` also have a default? https://github.com/nipy/nipype/blob/master/nipype/interfaces/ants/registration.py#L53

Resolving patch:
diff --git a/nipype/interfaces/ants/registration.py b/nipype/interfaces/ants/registration.py
--- a/nipype/interfaces/ants/registration.py
+++ b/nipype/interfaces/ants/registration.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
-"""The ants module provides basic functions for interfacing with ants functions.
+"""The ants module provides basic functions for interfacing with ants
+   functions.
 
    Change directory to provide relative paths for doctests
    >>> import os
@@ -7,7 +8,8 @@
    >>> datadir = os.path.realpath(os.path.join(filepath, '../../testing/data'))
    >>> os.chdir(datadir)
 """
-from __future__ import print_function, division, unicode_literals, absolute_import
+from __future__ import (print_function, division, unicode_literals,
+                        absolute_import)
 from builtins import range, str
 import os
 
@@ -20,17 +22,19 @@ class ANTSInputSpec(ANTSCommandInputSpec):
     dimension = traits.Enum(3, 2, argstr='%d', usedefault=False,
                             position=1, desc='image dimension (2 or 3)')
     fixed_image = InputMultiPath(File(exists=True), mandatory=True,
-                                 desc=('image to which the moving image is warped'))
+                                 desc=('image to which the moving image is '
+                                       'warped'))
     moving_image = InputMultiPath(File(exists=True), argstr='%s',
                                   mandatory=True,
-                                  desc=('image to apply transformation to (generally a coregistered '
+                                  desc=('image to apply transformation to '
+                                        '(generally a coregistered'
                                         'functional)'))
 
 #    Not all metrics are appropriate for all modalities. Also, not all metrics
-#    are efficeint or appropriate at all resolution levels, Some metrics perform
-#    well for gross global registraiton, but do poorly for small changes (i.e.
-#    Mattes), and some metrics do well for small changes but don't work well for
-#    gross level changes (i.e. 'CC').
+#    are efficeint or appropriate at all resolution levels, Some metrics
+#    perform well for gross global registraiton, but do poorly for small
+#    changes (i.e. Mattes), and some metrics do well for small changes but
+#    don't work well for gross level changes (i.e. 'CC').
 #
 #    This is a two stage registration. in the first stage
 #      [ 'Mattes', .................]
@@ -49,10 +53,18 @@ class ANTSInputSpec(ANTSCommandInputSpec):
     metric = traits.List(traits.Enum('CC', 'MI', 'SMI', 'PR', 'SSD',
                                      'MSQ', 'PSE'), mandatory=True, desc='')
 
-    metric_weight = traits.List(traits.Float(), requires=['metric'], desc='')
-    radius = traits.List(traits.Int(), requires=['metric'], desc='')
+    metric_weight = traits.List(traits.Float(), value=[1.0], usedefault=True,
+                                requires=['metric'], mandatory=True,
+                                desc='the metric weight(s) for each stage. '
+                                'The weights must sum to 1 per stage.')
 
-    output_transform_prefix = Str('out', usedefault=True, argstr='--output-naming %s',
+    radius = traits.List(traits.Int(), requires=['metric'],  mandatory=True,
+                         desc='radius of the region (i.e. number of layers'
+                         ' around a voxel point)'
+                         ' that is used for computing cross correlation')
+
+    output_transform_prefix = Str('out', usedefault=True,
+                                  argstr='--output-naming %s',
                                   mandatory=True, desc='')
     transformation_model = traits.Enum('Diff', 'Elast', 'Exp', 'Greedy Exp',
                                        'SyN', argstr='%s', mandatory=True,


Test patch:
diff --git a/nipype/interfaces/ants/tests/test_registration.py b/nipype/interfaces/ants/tests/test_registration.py
new file mode 100644
--- /dev/null
+++ b/nipype/interfaces/ants/tests/test_registration.py
@@ -0,0 +1,22 @@
+# 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 unicode_literals
+from nipype.interfaces.ants import registration
+import os
+import pytest
+
+
+def test_ants_mand(tmpdir):
+    tmpdir.chdir()
+    filepath = os.path.dirname(os.path.realpath(__file__))
+    datadir = os.path.realpath(os.path.join(filepath, '../../../testing/data'))
+
+    ants = registration.ANTS()
+    ants.inputs.transformation_model = "SyN"
+    ants.inputs.moving_image = [os.path.join(datadir, 'resting.nii')]
+    ants.inputs.fixed_image = [os.path.join(datadir, 'T1.nii')]
+    ants.inputs.metric = ['MI']
+
+    with pytest.raises(ValueError) as er:
+        ants.run()
+    assert "ANTS requires a value for input 'radius'" in str(er.value)