bpo-44026: Idle - display interpreter's 'did you mean' hints (GH-25912)

A C function accessible by the default exception handler, but not by python code,
finds the existing name closest to the name causing a name or attribute error.  For
such errors, call the default handler after capturing stderr and retrieve its message line.

Co-authored-by: Terry Jan Reedy <tjreedy@udel.edu>
(cherry picked from commit 092f9ddb5e85665552c8207972cd393d492f764e)
Co-authored-by: E-Paine <63801254+E-Paine@users.noreply.github.com>
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 07e9a2b..3836727 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -4,6 +4,7 @@
 f'''{sys.executable} -c "__import__('idlelib.run').run.main()"'''
 '.run' is needed because __import__ returns idlelib, not idlelib.run.
 """
+import contextlib
 import functools
 import io
 import linecache
@@ -211,6 +212,19 @@ def show_socket_error(err, address):
             parent=root)
     root.destroy()
 
+
+def get_message_lines(typ, exc, tb):
+    "Return line composing the exception message."
+    if typ in (AttributeError, NameError):
+        # 3.10+ hints are not directly accessible from python (#44026).
+        err = io.StringIO()
+        with contextlib.redirect_stderr(err):
+            sys.__excepthook__(typ, exc, tb)
+        return [err.getvalue().split("\n")[-2] + "\n"]
+    else:
+        return traceback.format_exception_only(typ, exc)
+
+
 def print_exception():
     import linecache
     linecache.checkcache()
@@ -241,7 +255,7 @@ def print_exc(typ, exc, tb):
                        "debugger_r.py", "bdb.py")
             cleanup_traceback(tbe, exclude)
             traceback.print_list(tbe, file=efile)
-        lines = traceback.format_exception_only(typ, exc)
+        lines = get_message_lines(typ, exc, tb)
         for line in lines:
             print(line, end='', file=efile)