Python change Exception printable output, eg overload __builtins__

Errors like this are hard-coded into the interpreter (in the case of CPython, anyway, which is most likely what you are using). You will not be able to change the message printed from within Python itself.

The C source code that is executed when the CPython interpreter tries to look up a name can be found here: https://github.com/python/cpython/blob/master/Python/ceval.c#L2602. If you would want to change the error message printed when a name lookup fails, you would need to change this line in the same file:

#define NAME_ERROR_MSG \
    "name '%.200s' is not defined"

Compiling the modified source code would yield a Python interpreter that prints your custom error message when encountering a name that is not defined.


Intro

I'd go with a more critical approach on why you'd even want to do what you want to do.

Python provides you with an ability to handle specific exceptions. That means if you had a business problem, you'd use a particular exception class and provide a custom message for that specific case. Now, remember this paragraph and let's move on, I'll refer to this later.


TL;DR

Now, let's go top-down:

Catching all kinds of errors with except Exception is generally not a good idea if want you catch let's say a variable name error. You'd use except NameError instead. There's really not much you'd add to it that's why it had a default message that perfectly described the issue. So it's assumed you'd use it as it's given. These are called concrete exceptions.

Now, with your specific case notice the alias as exc. By using the alias you can access arguments passed to the exception object, including the default message.

try:
   x # is not defined
except NameError as exc:
   print(exc.args)

Run that code (I put it in app.py) and you'll see:

$ python app.py
("name 'x' is not defined",)

These args are passed to the exception as a series (list, or in this case immutable list that is a tuple).

This leads to the idea of the possibility of easily passing arguments to exceptions' constructors (__init__). In your case "name 'x' is not defined" was passed as an argument.

You can use this to your advantage to solve your problem without much effort by just providing a custom message, like:

try:
   x # is not defined
except NameError as exc:
   your_custom_message = "the name 'x' you suggested is not yet defined, my lord. Improve your coding skills"
   # Now, you can handle it based on your requirement:
   #  print(your_custom_message)
   #  print(NameError(your_custom_message))
   #  raise NameError(your_custom_message)
   #  raise NameError(your_custom_message) from exc

The output is now what you wanted to achieve.

$ python app.py
the name 'x' you suggested is not yet defined, my lord. Improve your coding skills

Remember the first paragraph when I said I'd refer to it later? I mentioned providing a custom message for a specific case. If you build your own library when you want to handle name errors to specific variables relevant to your product, you assume your users will use your code that might raise that NameError exception. They will most likely catch it with except Exception as exc or except NameError as exc. And when they do print(exc), they will see your message now.


Summary

I hope that makes sense to you, just provide a custom message and pass it as an argument to NameError or simply just print it. IMO, it's better to learn it right together with why you'd use what you use.