Repository: numba/numba

Issue:
numpy.take behaviour
`numba` fails in nopython mode when argument to `ndarray.take` is `tuple`: 

```py
import numpy as np
from numba import njit

arr = np.arange(16).reshape(-1, 4)

@njit
def fun1(arr):
    return arr.take( (0, 3, 2) )  # tuple here

@njit
def fun2(arr):
    return arr.take( [0, 3, 2] )  # list here

# Does not work
fun1(arr)

# Works
fun2(arr)
```

Win 10
numba 0.35.0



Discussion:

Resolving patch:
diff --git a/numba/targets/arrayobj.py b/numba/targets/arrayobj.py
--- a/numba/targets/arrayobj.py
+++ b/numba/targets/arrayobj.py
@@ -3387,6 +3387,8 @@ def take_impl(a, indices):
 
 @lower_builtin('array.take', types.Array, types.List)
 @lower_builtin(np.take, types.Array, types.List)
+@lower_builtin('array.take', types.Array, types.BaseTuple)
+@lower_builtin(np.take, types.Array, types.BaseTuple)
 def numpy_take_3(context, builder, sig, args):
 
     def take_impl(a, indices):
diff --git a/numba/typing/arraydecl.py b/numba/typing/arraydecl.py
--- a/numba/typing/arraydecl.py
+++ b/numba/typing/arraydecl.py
@@ -1,5 +1,7 @@
 from __future__ import print_function, division, absolute_import
 
+import numpy as np
+
 from collections import namedtuple
 
 from numba import types, utils
@@ -434,8 +436,12 @@ def resolve_take(self, ary, args, kws):
             sig = signature(ary.dtype, *args)
         elif isinstance(argty, types.Array):
             sig = signature(argty.copy(layout='C', dtype=ary.dtype), *args)
-        elif isinstance(argty, types.List):
+        elif isinstance(argty, types.List): # 1d lists only
             sig = signature(types.Array(ary.dtype, 1, 'C'), *args)
+        elif isinstance(argty, types.BaseTuple):
+            sig = signature(types.Array(ary.dtype, np.ndim(argty), 'C'), *args)
+        else:
+            raise TypeError("take(%s) not supported for %s" % argty)
         return sig
 
     def generic_resolve(self, ary, attr):
diff --git a/numba/typing/npydecl.py b/numba/typing/npydecl.py
--- a/numba/typing/npydecl.py
+++ b/numba/typing/npydecl.py
@@ -1198,6 +1198,10 @@ def generic(self, args, kws):
             retty = types.Array(ndim=ind.ndim, dtype=arr.dtype, layout='C')
         elif isinstance(ind, types.List):
             retty = types.Array(ndim=1, dtype=arr.dtype, layout='C')
+        elif isinstance(ind, types.BaseTuple):
+            retty = types.Array(ndim=np.ndim(ind), dtype=arr.dtype, layout='C')
+        else:
+            return None
 
         return signature(retty, *args)
 


Test patch:
diff --git a/numba/tests/test_array_methods.py b/numba/tests/test_array_methods.py
--- a/numba/tests/test_array_methods.py
+++ b/numba/tests/test_array_methods.py
@@ -740,6 +740,7 @@ def check(arr, ind):
         # 2. 1d array index
         # 3. nd array index, >2d and F order
         # 4. reflected list
+        # 5. tuples
 
         test_indices = []
         test_indices.append(1)
@@ -751,6 +752,9 @@ def check(arr, ind):
         test_indices.append(np.array([[[1, 5, 1], [11, 3, 0]]]))
         test_indices.append(np.array([[[[1, 5]], [[11, 0]],[[1, 2]]]]))
         test_indices.append([1, 5, 1, 11, 3])
+        test_indices.append((1, 5, 1))
+        test_indices.append(((1, 5, 1), (11, 3, 2)))
+        test_indices.append((((1,), (5,), (1,)), ((11,), (3,), (2,))))
 
         layouts = cycle(['C', 'F', 'A'])
 
diff --git a/numba/tests/test_fancy_indexing.py b/numba/tests/test_fancy_indexing.py
--- a/numba/tests/test_fancy_indexing.py
+++ b/numba/tests/test_fancy_indexing.py
@@ -186,12 +186,15 @@ def check(arr, ind):
         # 2. 1d array index
         # 3. nd array index
         # 4. reflected list
+        # 5. tuples
         
         test_indices = []
         test_indices.append(1)
         test_indices.append(np.array([1, 5, 1, 11, 3]))
         test_indices.append(np.array([[[1], [5]], [[1], [11]]]))
         test_indices.append([1, 5, 1, 11, 3])
+        test_indices.append((1, 5, 1))
+        test_indices.append(((1, 5, 1), (11, 3, 2)))
     
         for dt in [np.int64, np.complex128]:
             A = np.arange(12, dtype=dt).reshape((4, 3))