Repository: numba/numba

Issue:
`numba.vectorize` interface inconsistency between `target="cpu"` and `target="parallel"`
`numba.vectorize` on 4911192c5 (and further back) appears to have the following interface inconsistency:
```python
import numpy as np
import numba as nb


@nb.jit
def add(x, y):
    return x + y


custom_vectorize = nb.vectorize([], identity=None, target="cpu")

# No problem here
test_fn = custom_vectorize(add)

custom_vectorize_2 = nb.vectorize([], identity=None, target="parallel")

# Problem here
test_fn_2 = custom_vectorize_2(add)

# TypeError: A jit decorator was called on an already jitted function CPUDispatcher(<function add at 0x7fa399ca2ef0>).  If trying to access the original python function, use the CPUDispatcher(<function add at 0x7fa399ca2ef0>).py_func attribute.
```


Discussion:
I can reproduce this.
Thanks for the report. I think something like this needs doing for the parallel target (fairly sure the below is why the `'cpu'` target works):
https://github.com/numba/numba/blob/4911192c5b51f3ecbb76a12b74372979378c43a1/numba/np/ufunc/dufunc.py#L80-L81
but it can probably just use the extension API for the determination of whether the function is already `@jit` wrapped: https://numba.readthedocs.io/en/stable/extending/high-level.html#numba.extending.is_jitted

Resolving patch:
diff --git a/numba/np/ufunc/dufunc.py b/numba/np/ufunc/dufunc.py
--- a/numba/np/ufunc/dufunc.py
+++ b/numba/np/ufunc/dufunc.py
@@ -1,9 +1,9 @@
 from numba import jit, typeof
 from numba.core import cgutils, types, serialize, sigutils
+from numba.core.extending import is_jitted
 from numba.core.typing import npydecl
 from numba.core.typing.templates import AbstractTemplate, signature
 from numba.np.ufunc import _internal
-from numba.core.dispatcher import Dispatcher
 from numba.parfors import array_analysis
 from numba.np.ufunc import ufuncbuilder
 from numba.np import numpy_support
@@ -77,7 +77,7 @@ class DUFunc(serialize.ReduceMixin, _internal._DUFunc):
     __base_kwargs = set(('identity', '_keepalive', 'nin', 'nout'))
 
     def __init__(self, py_func, identity=None, cache=False, targetoptions={}):
-        if isinstance(py_func, Dispatcher):
+        if is_jitted(py_func):
             py_func = py_func.py_func
         dispatcher = jit(_target='npyufunc',
                          cache=cache,
diff --git a/numba/np/ufunc/ufuncbuilder.py b/numba/np/ufunc/ufuncbuilder.py
--- a/numba/np/ufunc/ufuncbuilder.py
+++ b/numba/np/ufunc/ufuncbuilder.py
@@ -6,6 +6,7 @@
 from numba.core import config, targetconfig
 from numba.core.decorators import jit
 from numba.core.descriptors import TargetDescriptor
+from numba.core.extending import is_jitted
 from numba.core.options import TargetOptions, include_default_options
 from numba.core.registry import cpu_target
 from numba.core.target_extension import dispatcher_registry, target_registry
@@ -254,6 +255,8 @@ def disable_compile(self):
 class UFuncBuilder(_BaseUFuncBuilder):
 
     def __init__(self, py_func, identity=None, cache=False, targetoptions={}):
+        if is_jitted(py_func):
+            py_func = py_func.py_func
         self.py_func = py_func
         self.identity = parse_identity(identity)
         self.nb_func = jit(_target='npyufunc',


Test patch:
diff --git a/numba/tests/test_dispatcher.py b/numba/tests/test_dispatcher.py
--- a/numba/tests/test_dispatcher.py
+++ b/numba/tests/test_dispatcher.py
@@ -8,7 +8,7 @@
 
 import numpy as np
 
-from numba import njit, jit, generated_jit, typeof
+from numba import njit, jit, generated_jit, typeof, vectorize
 from numba.core import types, errors
 from numba import _dispatcher
 from numba.core.compiler import compile_isolated
@@ -1205,5 +1205,23 @@ def test_function_def_param(self):
         self.run_fc_multiproc(add_func)
 
 
+class TestVectorizeDifferentTargets(unittest.TestCase):
+    """Test that vectorize can be reapplied if the target is different
+    """
+
+    def test_cpu_vs_parallel(self):
+        @jit
+        def add(x, y):
+            return x + y
+
+        custom_vectorize = vectorize([], identity=None, target='cpu')
+
+        custom_vectorize(add)
+
+        custom_vectorize_2 = vectorize([], identity=None, target='parallel')
+
+        custom_vectorize_2(add)
+
+
 if __name__ == '__main__':
     unittest.main()