Repository: numba/numba

Issue:
Refactor: make helper function for selecting/sorting target-specific template
I think this eventually needs be a helper function, suspect it's going to become a common pattern.

_Originally posted by @stuartarchibald in https://github.com/numba/numba/pull/7061#discussion_r658633189_


Discussion:
@sklam  Is this issue still opened ? I'd like to work on it
@ankh6, sorry, all the tasks assigned to the [target overload refactor and cleanup project](https://github.com/numba/numba/projects/17#card-66262219) are maintainer tasks. If you would like to contribute, I'd suggest other issues that are not assigned to a Github project. For easy ones, look for issues with the following labels [good first issue](https://github.com/numba/numba/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22), [good second issue](https://github.com/numba/numba/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+second+issue%22), [easy](https://github.com/numba/numba/issues?q=is%3Aopen+is%3Aissue+label%3Aeasy)

Resolving patch:
diff --git a/numba/core/types/functions.py b/numba/core/types/functions.py
--- a/numba/core/types/functions.py
+++ b/numba/core/types/functions.py
@@ -279,38 +279,17 @@ def get_impl_key(self, sig):
         return self._impl_keys[sig.args]
 
     def get_call_type(self, context, args, kws):
-        from numba.core.target_extension import (target_registry,
-                                                 get_local_target)
 
         prefer_lit = [True, False]    # old behavior preferring literal
         prefer_not = [False, True]    # new behavior preferring non-literal
         failures = _ResolutionFailures(context, self, args, kws,
                                        depth=self._depth)
 
-        # get the current target target
+        # get the order in which to try templates
+        from numba.core.target_extension import get_local_target # circular
         target_hw = get_local_target(context)
-
-        # fish out templates that are specific to the target if a target is
-        # specified
-        DEFAULT_TARGET = 'generic'
-        usable = []
-        for ix, temp_cls in enumerate(self.templates):
-            # ? Need to do something about this next line
-            hw = temp_cls.metadata.get('target', DEFAULT_TARGET)
-            if hw is not None:
-                hw_clazz = target_registry[hw]
-                if target_hw.inherits_from(hw_clazz):
-                    usable.append((temp_cls, hw_clazz, ix))
-
-        # sort templates based on target specificity
-        def key(x):
-            return target_hw.__mro__.index(x[1])
-        order = [x[0] for x in sorted(usable, key=key)]
-
-        if not order:
-            msg = (f"Function resolution cannot find any matches for function"
-                   f" '{self.key[0]}' for the current target: '{target_hw}'.")
-            raise errors.UnsupportedError(msg)
+        order = utils.order_by_target_specificity(target_hw, self.templates,
+                                                  fnkey=self.key[0])
 
         self._depth += 1
 
diff --git a/numba/core/typing/context.py b/numba/core/typing/context.py
--- a/numba/core/typing/context.py
+++ b/numba/core/typing/context.py
@@ -10,6 +10,7 @@
 from numba.core import types, errors
 from numba.core.typeconv import Conversion, rules
 from numba.core.typing import templates
+from numba.core.utils import order_by_target_specificity
 from .typeof import typeof, Purpose
 
 from numba.core import utils
@@ -284,31 +285,13 @@ def core(typ):
                 return attrty
 
     def find_matching_getattr_template(self, typ, attr):
-        from numba.core.target_extension import (target_registry,
-                                                 get_local_target)
 
         templates = list(self._get_attribute_templates(typ))
 
-        # get the current target target
+        # get the order in which to try templates
+        from numba.core.target_extension import get_local_target # circular
         target_hw = get_local_target(self)
-
-        # fish out templates that are specific to the target if a target is
-        # specified
-        DEFAULT_TARGET = 'generic'
-        usable = []
-        for ix, temp_cls in enumerate(templates):
-            md = getattr(temp_cls, "metadata", {})
-            hw = md.get('target', DEFAULT_TARGET)
-            if hw is not None:
-                hw_clazz = target_registry[hw]
-                if target_hw.inherits_from(hw_clazz):
-                    usable.append((temp_cls, hw_clazz, ix))
-
-        # sort templates based on target specificity
-        def key(x):
-            return target_hw.__mro__.index(x[1])
-
-        order = [x[0] for x in sorted(usable, key=key)]
+        order = order_by_target_specificity(target_hw, templates, fnkey=attr)
 
         for template in order:
             return_type = template.resolve(typ, attr)
diff --git a/numba/core/utils.py b/numba/core/utils.py
--- a/numba/core/utils.py
+++ b/numba/core/utils.py
@@ -279,6 +279,45 @@ def __hash__(self):
         return hash(tuple(sorted(self._values.items())))
 
 
+def order_by_target_specificity(target, templates, fnkey=''):
+    """This orders the given templates from most to least specific against the
+    current "target". "fnkey" is an indicative typing key for use in the
+    exception message in the case that there's no usable templates for the
+    current "target".
+    """
+    # No templates... return early!
+    if templates == []:
+        return []
+
+    from numba.core.target_extension import target_registry
+
+    # fish out templates that are specific to the target if a target is
+    # specified
+    DEFAULT_TARGET = 'generic'
+    usable = []
+    for ix, temp_cls in enumerate(templates):
+        # ? Need to do something about this next line
+        md = getattr(temp_cls, "metadata", {})
+        hw = md.get('target', DEFAULT_TARGET)
+        if hw is not None:
+            hw_clazz = target_registry[hw]
+            if target.inherits_from(hw_clazz):
+                usable.append((temp_cls, hw_clazz, ix))
+
+    # sort templates based on target specificity
+    def key(x):
+        return target.__mro__.index(x[1])
+    order = [x[0] for x in sorted(usable, key=key)]
+
+    if not order:
+        msg = (f"Function resolution cannot find any matches for function "
+               f"'{fnkey}' for the current target: '{target}'.")
+        from numba.core.errors import UnsupportedError
+        raise UnsupportedError(msg)
+
+    return order
+
+
 class SortedMap(Mapping):
     """Immutable
     """


Test patch: