Repository: numba/numba

Issue:
NamedTuple.__init__ hangs with second argument as literal
Tested on '0.45.0' and '0.46.0dev0+5.gfddbdc5'

The following piece of code hangs forever when calling `NamedTupe.__init__` when second argument is a literal.

This should probably raise an error?

```python
from numba import types


types.NamedTuple(types.uint32, 2)
```


Discussion:
Thanks for the report. I don't think that this is the correct way to instantiate a `types.NamedTuple`, the first argument should be an iterable of types, the second a `namedtuple` type. Example:

```pycon
In [9]: from collections import namedtuple                                                                                      

In [10]: from numba import types                                                                                                

In [11]: f = namedtuple('f', 'a b')                                                                                             

In [12]: types.NamedTuple((types.uint32, types.float64), f)                                                                     
Out[12]: f(uint32, float64)

In [13]: z = types.NamedTuple((types.uint32, types.float64), f)                                                                 

In [14]: type(z)                                                                                                                
Out[14]: numba.types.containers.NamedTuple
```
Thanks for the response. I was expecting an exception being raised for the invalid usage instead of a hang.
Similarly to what happens when a ```types.NamedUniTuple``` is incorrectly instantiated:

```python
In [7]: types.NamedUniTuple(types.uint32, 2, 2)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-7-64339c48032d> in <module>
----> 1 types.NamedUniTuple(types.uint32, 2, 2)

~\Anaconda3\lib\site-packages\numba\types\abstract.py in __call__(cls, *args, **kwargs)
     58         the new instance is returned.
     59         """
---> 60         inst = type.__call__(cls, *args, **kwargs)
     61         return cls._intern(inst)
     62

~\Anaconda3\lib\site-packages\numba\types\containers.py in __init__(self, dtype, count, cls)
    292         self.dtype = dtype
    293         self.count = count
--> 294         self.fields = tuple(cls._fields)
    295         self.instance_class = cls
    296         name = "%s(%s x %d)" % (cls.__name__, dtype, count)

AttributeError: 'int' object has no attribute '_fields'
```
I think that an exception is raised in the second case is entirely an accident, just a consequence of duck typing. IIRC the type constructors have no safety guarantees and are only generally used by developers/more advanced users, obviously it'd be great to strengthen their safety. Will mark this as a bug and a good second issue as I think someone familiar with the Numba types could help with this. 

Resolving patch:
diff --git a/numba/types/containers.py b/numba/types/containers.py
--- a/numba/types/containers.py
+++ b/numba/types/containers.py
@@ -1,5 +1,7 @@
 from __future__ import print_function, division, absolute_import
 
+from collections import Iterable
+
 from .abstract import *
 from .common import *
 from .misc import Undefined, unliteral, Optional, NoneType
@@ -246,10 +248,18 @@ def __len__(self):
     def __iter__(self):
         return iter(self.types)
 
+    @staticmethod
+    def is_types_iterable(types):
+        # issue 4463 - check if argument 'types' is iterable
+        if not isinstance(types, Iterable):
+            raise TypingError("Argument 'types' is not iterable")
+
 
 class Tuple(BaseAnonymousTuple, _HeterogeneousTuple):
 
     def __new__(cls, types):
+        _HeterogeneousTuple.is_types_iterable(types)
+
         if types and all(t == types[0] for t in types[1:]):
             return UniTuple(dtype=types[0], count=len(types))
         else:
@@ -308,6 +318,8 @@ def key(self):
 class NamedTuple(_HeterogeneousTuple, BaseNamedTuple):
 
     def __init__(self, types, cls):
+        _HeterogeneousTuple.is_types_iterable(types)
+
         self.types = tuple(types)
         self.count = len(self.types)
         self.fields = tuple(cls._fields)


Test patch:
diff --git a/numba/tests/test_tuples.py b/numba/tests/test_tuples.py
--- a/numba/tests/test_tuples.py
+++ b/numba/tests/test_tuples.py
@@ -119,6 +119,27 @@ def eattuple(tup):
         expected = "Tuple 'tup' length must be smaller than 1000"
         self.assertIn(expected, str(raises.exception))
 
+class TestTupleTypeNotIterable(unittest.TestCase):
+    '''
+    issue 4369
+    raise an error if 'type' is not iterable
+    '''
+    def test_namedtuple_types_exception(self):
+        with self.assertRaises(errors.TypingError) as raises:
+            types.NamedTuple(types.uint32, 'p')
+        self.assertIn(
+            "Argument 'types' is not iterable",
+            str(raises.exception)
+        )
+
+    def test_tuple_types_exception(self):
+        with self.assertRaises(errors.TypingError) as raises:
+            types.Tuple((types.uint32))
+        self.assertIn(
+            "Argument 'types' is not iterable",
+            str(raises.exception)
+        )
+
 
 class TestTupleReturn(TestCase):