Repository: nipy/nipype

Issue:
iterfield in MapNode can't be range (py3)
### Summary
In py3 `range` is not a list, and if iterfield in `MapNode` is defined as `range` than entire range is send to the interface at once.  

### Script/Workflow details

```
from nipype import Function, MapNode

def square_func(x):
    print(x, type(x))
    return x ** 2

square = Function(["x"], ["f_x"], square_func)

square_node = MapNode(square, name="square", iterfield=["x"])

square_node.inputs.x = range(4)
res  = square_node.run()
```

### Actual behavior
print statement from `square_func` gives `range(0, 4) <class 'range'>`

### Expected behavior
print statement from `square_func` should give  `0 <class 'int'>`, etc.




Discussion:

Patch:
diff --git a/nipype/interfaces/base.py b/nipype/interfaces/base.py
index 2f8b1bf0e..19cf9ccaa 100644
--- a/nipype/interfaces/base.py
+++ b/nipype/interfaces/base.py
@@ -32,6 +32,7 @@ from warnings import warn
 import simplejson as json
 from dateutil.parser import parse as parseutc
 from packaging.version import Version
+import collections
 
 from .. import config, logging, LooseVersion, __version__
 from ..utils.provenance import write_provenance
@@ -2058,9 +2059,15 @@ class MultiPath(traits.List):
     """
 
     def validate(self, object, name, value):
+
+        # want to treat range and other sequences (except str) as list
+        if not isinstance(value, (str, bytes)) and isinstance(value, collections.Sequence):
+            value  = list(value)
+
         if not isdefined(value) or \
                 (isinstance(value, list) and len(value) == 0):
             return Undefined
+
         newvalue = value
 
         if not isinstance(value, list) \


Test patch:
diff --git a/nipype/pipeline/engine/tests/test_engine.py b/nipype/pipeline/engine/tests/test_engine.py
index e2624d03c..cece44444 100644
--- a/nipype/pipeline/engine/tests/test_engine.py
+++ b/nipype/pipeline/engine/tests/test_engine.py
@@ -456,6 +456,24 @@ def test_mapnode_iterfield_check():
     with pytest.raises(ValueError): mod1._check_iterfield()
 
 
+@pytest.mark.parametrize("x_inp, f_exp", [
+        (3, [6]), ([2, 3], [4, 6]), ((2, 3), [4, 6]),
+        (range(3), [0, 2, 4]),
+         ("Str", ["StrStr"]), (["Str1", "Str2"], ["Str1Str1", "Str2Str2"])
+        ])
+def test_mapnode_iterfield_type(x_inp, f_exp):
+    from nipype import MapNode, Function
+    def double_func(x):
+        return 2 * x
+    double = Function(["x"], ["f_x"], double_func)
+
+    double_node = MapNode(double, name="double", iterfield=["x"])
+    double_node.inputs.x = x_inp
+
+    res  = double_node.run()
+    assert res.outputs.f_x == f_exp
+
+
 def test_mapnode_nested(tmpdir):
     os.chdir(str(tmpdir))
     from nipype import MapNode, Function