Repository: numba/numba

Issue:
jitclass decorator fails when encountering annotations (numba 0.25)
When using jitclass decorator, if ANY of the methods in the class have annotations, the compilation fails. 
This is the code sample sufficient to trigger the error:

```
spec={
'obj':int32,
}

@jitclass(spec)
class TTT(object):
    def __init__(self, obj:int):
        self.obj = obj

    def add(self, v:int):
        return self.obj + v
```

This is the traceback:

```
python3.4 test.py 
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    class TTT(object):
  File "/usr/lib64/python3.4/site-packages/numba/jitclass/decorators.py", line 27, in wrap
    return register_class_type(cls, spec, types.ClassType, ClassBuilder)
  File "/usr/lib64/python3.4/site-packages/numba/jitclass/base.py", line 170, in register_class_type
    __doc__=docstring))
  File "/usr/lib64/python3.4/site-packages/numba/jitclass/base.py", line 70, in __new__
    outcls._set_init()
  File "/usr/lib64/python3.4/site-packages/numba/jitclass/base.py", line 76, in _set_init
    argspec = inspect.getargspec(init)
  File "/usr/lib64/python3.4/inspect.py", line 936, in getargspec
    raise ValueError("Function has keyword-only arguments or annotations"
ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them
```

This is not really a major issue, but I am certain it is easy to fix. 
Thank you!



Discussion:

Resolving patch:
diff --git a/numba/jitclass/base.py b/numba/jitclass/base.py
--- a/numba/jitclass/base.py
+++ b/numba/jitclass/base.py
@@ -59,8 +59,8 @@ def _mangle_attr(name):
 # Class object
 
 _ctor_template = """
-def ctor({args}):
-    return __numba_cls_({args})
+def ctor(*args):
+    return __numba_cls_(*args)
 """
 
 
@@ -80,14 +80,11 @@ def __new__(cls, name, bases, dct):
         return outcls
 
     def _set_init(cls):
-        # make ctor
-        init = cls.class_type.instance_type.methods['__init__']
-        argspec = inspect.getargspec(init)
-        assert not argspec.varargs, 'varargs not supported'
-        assert not argspec.keywords, 'keywords not supported'
-        assert not argspec.defaults, 'defaults not supported'
-        args = ', '.join(argspec.args[1:])
-        ctor_source = _ctor_template.format(args=args)
+        """
+        Generate a wrapper for calling the constructor from pure Python.
+        Note the wrapper will only accept positional arguments.
+        """
+        ctor_source = _ctor_template
         glbls = {"__numba_cls_": cls}
         exec_(ctor_source, glbls)
         ctor = glbls['ctor']
diff --git a/numba/jitclass/boxing.py b/numba/jitclass/boxing.py
--- a/numba/jitclass/boxing.py
+++ b/numba/jitclass/boxing.py
@@ -4,7 +4,6 @@
 """
 from __future__ import print_function, absolute_import
 
-import inspect
 from functools import wraps, partial
 
 from llvmlite import ir
@@ -27,8 +26,8 @@ def mutator(__numba_self_, __numba_val):
 """
 
 _method_code_template = """
-def method(__numba_self_, {args}):
-    return __numba_self_.{method}({args})
+def method(__numba_self_, *args):
+    return __numba_self_.{method}(*args)
 """
 
 
@@ -50,15 +49,10 @@ def _generate_property(field, template, fname):
 
 def _generate_method(name, func):
     """
-    Generate a wrapper for calling a method
+    Generate a wrapper for calling a method.  Note the wrapper will only
+    accept positional arguments.
     """
-    argspec = inspect.getargspec(func)
-    assert not argspec.varargs, 'varargs not supported'
-    assert not argspec.keywords, 'keywords not supported'
-    assert not argspec.defaults, 'defaults not supported'
-
-    args = ', '.join(argspec.args[1:])  # skipped self arg
-    source = _method_code_template.format(method=name, args=args)
+    source = _method_code_template.format(method=name)
     glbls = {}
     exec_(source, glbls)
     method = njit(glbls['method'])


Test patch:
diff --git a/numba/tests/annotation_usecases.py b/numba/tests/annotation_usecases.py
new file mode 100644
--- /dev/null
+++ b/numba/tests/annotation_usecases.py
@@ -0,0 +1,16 @@
+"""
+Usecases with Python 3 function annotations.  This is a separate module
+in order to avoid syntax errors with Python 2.
+"""
+
+
+class AnnotatedClass:
+    """
+    A class with annotated methods.
+    """
+
+    def __init__(self, v: int):
+        self.x = v
+
+    def add(self, v: int) -> int:
+        return self.x + v
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
@@ -2,6 +2,7 @@
 
 from collections import OrderedDict
 import ctypes
+import sys
 
 import numpy as np
 
@@ -549,5 +550,20 @@ def access_dunder(inst):
             access_dunder.py_func(inst)
         self.assertIn('_TestJitClass__value', str(raises.exception))
 
+    @unittest.skipIf(sys.version_info < (3,), "Python 3-specific test")
+    def test_annotations(self):
+        """
+        Methods with annotations should compile fine (issue #1911).
+        """
+        from .annotation_usecases import AnnotatedClass
+
+        spec = {'x': int32}
+        cls = jitclass(spec)(AnnotatedClass)
+
+        obj = cls(5)
+        self.assertEqual(obj.x, 5)
+        self.assertEqual(obj.add(2), 7)
+
+
 if __name__ == '__main__':
     unittest.main()