Repository: numba/numba

Issue:
numba.cuda.cudadrv.devicearray.DeviceNDArray is not iterable 
## Reporting a bug

- [ ] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->
MWE:
```python
>>> from numba import cuda
>>> a = cuda.device_array((5,))
>>> a[:] = range(5)
>>>
>>> # this throws an AssertionError
... for v in a:
...     print(v)
... 
0.0
1.0
2.0
3.0
4.0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devices.py", line 212, in _require_cuda_context
    return fn(*args, **kws)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devicearray.py", line 479, in __getitem__
    return self._do_getitem(item)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devicearray.py", line 489, in _do_getitem
    arr = self._dummy.__getitem__(item)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 219, in __getitem__
    dims = [dim.__getitem__(it) for dim, it in zip(self.dims, item)]
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 219, in <listcomp>
    dims = [dim.__getitem__(it) for dim, it in zip(self.dims, item)]
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 78, in __getitem__
    single=True,
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 51, in __init__
    assert not single or size == 1
AssertionError
>>> # but manually limiting the range works fine
... for i in range(5):
...     print(a[i])
... 
0.0
1.0
2.0
3.0
4.0
>>>
```
It seems that the `StopIteration` exception should have been raised somewhere...

numba.cuda.cudadrv.devicearray.DeviceNDArray is not iterable 
## Reporting a bug

- [ ] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->
MWE:
```python
>>> from numba import cuda
>>> a = cuda.device_array((5,))
>>> a[:] = range(5)
>>>
>>> # this throws an AssertionError
... for v in a:
...     print(v)
... 
0.0
1.0
2.0
3.0
4.0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devices.py", line 212, in _require_cuda_context
    return fn(*args, **kws)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devicearray.py", line 479, in __getitem__
    return self._do_getitem(item)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/cuda/cudadrv/devicearray.py", line 489, in _do_getitem
    arr = self._dummy.__getitem__(item)
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 219, in __getitem__
    dims = [dim.__getitem__(it) for dim, it in zip(self.dims, item)]
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 219, in <listcomp>
    dims = [dim.__getitem__(it) for dim, it in zip(self.dims, item)]
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 78, in __getitem__
    single=True,
  File "/home/leofang/conda_envs/cupy_dev/lib/python3.6/site-packages/numba/dummyarray.py", line 51, in __init__
    assert not single or size == 1
AssertionError
>>> # but manually limiting the range works fine
... for i in range(5):
...     print(a[i])
... 
0.0
1.0
2.0
3.0
4.0
>>>
```
It seems that the `StopIteration` exception should have been raised somewhere...



Discussion:
Thanks for the report, I can reproduce with:
```python
from numba import cuda
a = cuda.device_array((5,))
a[:] = range(5)

# this throws an AssertionError
for v in a:
    print(v)
```
seems like there's a problem in the logic of `__getitem__`, I think that if the `dummyarray.Dim` class needs to be `single` the list comp in `dummyarray.Array.__getitem__` should raise `IndexError`.
Thanks for the report, I can reproduce with:
```python
from numba import cuda
a = cuda.device_array((5,))
a[:] = range(5)

# this throws an AssertionError
for v in a:
    print(v)
```
seems like there's a problem in the logic of `__getitem__`, I think that if the `dummyarray.Dim` class needs to be `single` the list comp in `dummyarray.Array.__getitem__` should raise `IndexError`.

Resolving patch:
diff --git a/numba/dummyarray.py b/numba/dummyarray.py
--- a/numba/dummyarray.py
+++ b/numba/dummyarray.py
@@ -70,6 +70,8 @@ def __getitem__(self, item):
             return ret
         else:
             sliced = self[item:item + 1]
+            if sliced.size != 1:
+                raise IndexError
             return Dim(
                 start=sliced.start,
                 stop=sliced.stop,


Test patch:
diff --git a/numba/tests/test_dummyarray.py b/numba/tests/test_dummyarray.py
--- a/numba/tests/test_dummyarray.py
+++ b/numba/tests/test_dummyarray.py
@@ -262,6 +262,20 @@ def test_extent_iter_2d(self):
         self.assertEqual(len(list(arr[::2].iter_contiguous_extent())), 2)
 
 
+class TestIterate(unittest.TestCase):
+    def test_for_loop(self):
+        # for #4201
+        N = 5
+        nparr = np.empty(N)
+        arr = Array.from_desc(0, nparr.shape, nparr.strides,
+                              nparr.dtype.itemsize)
+
+        x = 0  # just a placeholder
+        # this loop should not raise AssertionError
+        for val in arr:
+            x = val
+
+
 if __name__ == '__main__':
     unittest.main()