Repository: numba/numba

Issue:
Improved error message for non existing variable
Attempting to use a variable that has not been defined returns an error message that can be confusing for someone new to Numba.

```python
@njit
def foo(x):
    a = monkey
    return a

foo(np.ones(2))

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
Untyped global name 'monkey': cannot determine Numba type of <class 'numba.core.ir.UndefinedType'>
```

while Python would return a much clearer `NameError: name 'monkey' is not defined`.


Discussion:
Starter patch:
```diff
diff --git a/numba/core/typeinfer.py b/numba/core/typeinfer.py
index 59d041b9c..7d39eca4d 100644
--- a/numba/core/typeinfer.py
+++ b/numba/core/typeinfer.py
@@ -1469,7 +1469,15 @@ http://numba.pydata.org/numba-doc/latest/user/troubleshoot.html#my-code-has-an-u
                 typ = types.Dispatcher(_temporary_dispatcher_map[gvar.name])
             else:
                 nm = gvar.name
-                msg = _termcolor.errmsg("Untyped global name '%s':" % nm)
+                # check if the problem is actually a name error
+                func_glbls = self.func_id.func.__globals__
+                if nm not in func_glbls.keys():
+                    errstr = "NameError: name '%s' is not defined"
+                    msg = _termcolor.errmsg(errstr % nm)
+                    e.patch_message(msg)
+                    raise
+                else:
+                    msg = _termcolor.errmsg("Untyped global name '%s':" % nm)
                 msg += " %s"  # interps the actual error
 
                 # if the untyped global is a numba internal function then add
```
the new errmsg makes sense. matches what CPython would do.

Resolving patch:
diff --git a/numba/core/typeinfer.py b/numba/core/typeinfer.py
--- a/numba/core/typeinfer.py
+++ b/numba/core/typeinfer.py
@@ -1468,13 +1468,25 @@ def typeof_global(self, inst, target, gvar):
                 # as a global variable
                 typ = types.Dispatcher(_temporary_dispatcher_map[gvar.name])
             else:
+                from numba.misc import special
+
                 nm = gvar.name
-                msg = _termcolor.errmsg("Untyped global name '%s':" % nm)
+                # check if the problem is actually a name error
+                func_glbls = self.func_id.func.__globals__
+                if (nm not in func_glbls.keys() and
+                        nm not in special.__all__ and
+                        nm not in __builtins__.keys()):
+                    errstr = "NameError: name '%s' is not defined"
+                    msg = _termcolor.errmsg(errstr % nm)
+                    e.patch_message(msg)
+                    raise
+                else:
+                    msg = _termcolor.errmsg("Untyped global name '%s':" % nm)
                 msg += " %s"  # interps the actual error
 
                 # if the untyped global is a numba internal function then add
                 # to the error message asking if it's been imported.
-                from numba.misc import special
+
                 if nm in special.__all__:
                     tmp = ("\n'%s' looks like a Numba internal function, has "
                            "it been imported (i.e. 'from numba import %s')?\n" %


Test patch:
diff --git a/numba/tests/test_errorhandling.py b/numba/tests/test_errorhandling.py
--- a/numba/tests/test_errorhandling.py
+++ b/numba/tests/test_errorhandling.py
@@ -171,6 +171,16 @@ def foo():
                 dec(foo)()
             self.assertIn(expected, str(raises.exception))
 
+    def test_handling_undefined_variable(self):
+        @njit
+        def foo():
+            return a
+
+        expected = "NameError: name 'a' is not defined"
+
+        with self.assertRaises(errors.TypingError) as raises:
+            foo()
+        self.assertIn(expected, str(raises.exception))
 
 class TestConstantInferenceErrorHandling(unittest.TestCase):
 
diff --git a/numba/tests/test_typingerror.py b/numba/tests/test_typingerror.py
--- a/numba/tests/test_typingerror.py
+++ b/numba/tests/test_typingerror.py
@@ -82,7 +82,7 @@ def test_unknown_module(self):
         # This used to print "'object' object has no attribute 'int32'"
         with self.assertRaises(TypingError) as raises:
             compile_isolated(unknown_module, ())
-        self.assertIn("Untyped global name 'numpyz'", str(raises.exception))
+        self.assertIn("name 'numpyz' is not defined", str(raises.exception))
 
     def test_issue_868(self):
         '''