bpo-36829: Add _PyErr_WriteUnraisableMsg() (GH-13488)

* sys.unraisablehook: add 'err_msg' field to UnraisableHookArgs.
* Use _PyErr_WriteUnraisableMsg() in _ctypes _DictRemover_call()
  and gc delete_garbage().
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 1e5f168..dfe63b1 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -878,31 +878,38 @@
 
 @test.support.cpython_only
 class UnraisableHookTest(unittest.TestCase):
-    def write_unraisable_exc(self, exc, obj):
+    def write_unraisable_exc(self, exc, err_msg, obj):
         import _testcapi
         import types
+        err_msg2 = f"Exception ignored {err_msg}"
         try:
-            _testcapi.write_unraisable_exc(exc, obj)
+            _testcapi.write_unraisable_exc(exc, err_msg, obj)
             return types.SimpleNamespace(exc_type=type(exc),
                                          exc_value=exc,
                                          exc_traceback=exc.__traceback__,
+                                         err_msg=err_msg2,
                                          object=obj)
         finally:
             # Explicitly break any reference cycle
             exc = None
 
     def test_original_unraisablehook(self):
-        obj = "an object"
+        for err_msg in (None, "original hook"):
+            with self.subTest(err_msg=err_msg):
+                obj = "an object"
 
-        with test.support.captured_output("stderr") as stderr:
-            with test.support.swap_attr(sys, 'unraisablehook',
-                                        sys.__unraisablehook__):
-                self.write_unraisable_exc(ValueError(42), obj)
+                with test.support.captured_output("stderr") as stderr:
+                    with test.support.swap_attr(sys, 'unraisablehook',
+                                                sys.__unraisablehook__):
+                        self.write_unraisable_exc(ValueError(42), err_msg, obj)
 
-        err = stderr.getvalue()
-        self.assertIn(f'Exception ignored in: {obj!r}\n', err)
-        self.assertIn('Traceback (most recent call last):\n', err)
-        self.assertIn('ValueError: 42\n', err)
+                err = stderr.getvalue()
+                if err_msg is not None:
+                    self.assertIn(f'Exception ignored {err_msg}: {obj!r}\n', err)
+                else:
+                    self.assertIn(f'Exception ignored in: {obj!r}\n', err)
+                self.assertIn('Traceback (most recent call last):\n', err)
+                self.assertIn('ValueError: 42\n', err)
 
     def test_original_unraisablehook_err(self):
         # bpo-22836: PyErr_WriteUnraisable() should give sensible reports
@@ -962,8 +969,9 @@
         obj = object()
         try:
             with test.support.swap_attr(sys, 'unraisablehook', hook_func):
-                expected = self.write_unraisable_exc(ValueError(42), obj)
-                for attr in "exc_type exc_value exc_traceback object".split():
+                expected = self.write_unraisable_exc(ValueError(42),
+                                                     "custom hook", obj)
+                for attr in "exc_type exc_value exc_traceback err_msg object".split():
                     self.assertEqual(getattr(hook_args, attr),
                                      getattr(expected, attr),
                                      (hook_args, expected))
@@ -978,10 +986,12 @@
 
         with test.support.captured_output("stderr") as stderr:
             with test.support.swap_attr(sys, 'unraisablehook', hook_func):
-                self.write_unraisable_exc(ValueError(42), None)
+                self.write_unraisable_exc(ValueError(42),
+                                          "custom hook fail", None)
 
         err = stderr.getvalue()
-        self.assertIn(f'Exception ignored in: {hook_func!r}\n',
+        self.assertIn(f'Exception ignored in sys.unraisablehook: '
+                      f'{hook_func!r}\n',
                       err)
         self.assertIn('Traceback (most recent call last):\n', err)
         self.assertIn('Exception: hook_func failed\n', err)