Repository: numba/numba

Issue:
JIT class: Internal error at resolving type of attribute
With numba v0.23.0, the following code:

``` python
from numba import jitclass, double
import numpy as np

spec = [
        ('a', int),
        ('x', double[:]),
        ]

@jitclass(spec)
class Test:
    def __init__(self, a):
        self.a = a
        self.x = np.empty(self.a, dtype = double)

t = Test(5)
```

gives the following error:

```
numba.errors.TypingError: Failed at nopython (nopython frontend)
Failed at nopython (nopython frontend)
Internal error at resolving type of attribute "a" of "self":
--%<-----------------------------------------------------------------
Traceback (most recent call last):
  File "/home/david/anaconda3/lib/python3.4/site-packages/numba/typeinfer.py", line 111, in propagate
    constraint(typeinfer)
  File "/home/david/anaconda3/lib/python3.4/site-packages/numba/typeinfer.py", line 356, in __call__
    typeinfer.add_type(self.target, attrty)
  File "/home/david/anaconda3/lib/python3.4/site-packages/numba/typeinfer.py", line 572, in add_type
    unified = tv.add_type(tp)
  File "/home/david/anaconda3/lib/python3.4/site-packages/numba/typeinfer.py", line 33, in add_type
    assert isinstance(tp, types.Type), type(tp)
AssertionError: <class 'type'>
--%<-----------------------------------------------------------------
```



Discussion:
That's because you must specify a Numba type (such as `int32`, etc.), not Python `int`.
We should catch those errors early and output a nice error message, though.

Thanks.

Reopening to make sure we add type checking in the API.


Resolving patch:
diff --git a/numba/jitclass/base.py b/numba/jitclass/base.py
--- a/numba/jitclass/base.py
+++ b/numba/jitclass/base.py
@@ -96,6 +96,14 @@ def __call__(cls, *args, **kwargs):
 ##############################################################################
 # Registration utils
 
+def _validate_spec(spec):
+    for k, v in spec.items():
+        if not isinstance(k, str):
+            raise TypeError("spec keys should be strings, got %r" % (k,))
+        if not isinstance(v, types.Type):
+            raise TypeError("spec values should be Numba type instances, got %r"
+                            % (v,))
+
 def register_class_type(cls, spec, class_ctor, builder):
     """
     Internal function to create a jitclass.
@@ -110,6 +118,7 @@ def register_class_type(cls, spec, class_ctor, builder):
     # Normalize spec
     if isinstance(spec, Sequence):
         spec = OrderedDict(spec)
+    _validate_spec(spec)
 
     # Copy methods from base classes
     clsdct = {}


Test patch:
diff --git a/numba/tests/test_jitclasses.py b/numba/tests/test_jitclasses.py
--- a/numba/tests/test_jitclasses.py
+++ b/numba/tests/test_jitclasses.py
@@ -40,6 +40,23 @@ def test_list_spec(self):
                 ('y', float32)]
         self._check_spec(spec)
 
+    def test_spec_errors(self):
+        spec1 = [('x', int), ('y', float32[:])]
+        spec2 = [(1, int32), ('y', float32[:])]
+
+        class Test(object):
+            def __init__(self):
+                pass
+
+        with self.assertRaises(TypeError) as raises:
+            jitclass(spec1)(Test)
+        self.assertIn("spec values should be Numba type instances",
+                      str(raises.exception))
+        with self.assertRaises(TypeError) as raises:
+            jitclass(spec2)(Test)
+        self.assertEqual(str(raises.exception),
+                         "spec keys should be strings, got 1")
+
     def _make_Float2AndArray(self):
         spec = OrderedDict()
         spec['x'] = float32