[Python-checkins] bpo-41031: Match C and Python code formatting of unprintable exceptions and exceptions in the __main__ module. (GH-28139)

serhiy-storchaka webhook-mailer at python.org
Sun Sep 5 11:54:28 EDT 2021


https://github.com/python/cpython/commit/9e31b3952f6101ef71ec029481b972169ab0e0f1
commit: 9e31b3952f6101ef71ec029481b972169ab0e0f1
branch: main
author: Irit Katriel <1055913+iritkatriel at users.noreply.github.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2021-09-05T18:54:13+03:00
summary:

bpo-41031: Match C and Python code formatting of unprintable exceptions and exceptions in the __main__ module. (GH-28139)

files:
A Misc/NEWS.d/next/Core and Builtins/2021-09-03-12-35-17.bpo-41031.yPSJEs.rst
M Lib/test/test_sys.py
M Lib/test/test_traceback.py
M Lib/traceback.py
M Python/errors.c
M Python/pythonrun.c

diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 12305ca95d0a0d..e98803b48f6ac0 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1071,19 +1071,29 @@ def __del__(self):
                 self.assertTrue(report.endswith("\n"))
 
     def test_original_unraisablehook_exception_qualname(self):
+        # See bpo-41031, bpo-45083.
+        # Check that the exception is printed with its qualified name
+        # rather than just classname, and the module names appears
+        # unless it is one of the hard-coded exclusions.
         class A:
             class B:
                 class X(Exception):
                     pass
 
-        with test.support.captured_stderr() as stderr, \
-             test.support.swap_attr(sys, 'unraisablehook',
-                                    sys.__unraisablehook__):
-                 expected = self.write_unraisable_exc(
-                     A.B.X(), "msg", "obj");
-        report = stderr.getvalue()
-        testName = 'test_original_unraisablehook_exception_qualname'
-        self.assertIn(f"{testName}.<locals>.A.B.X", report)
+        for moduleName in 'builtins', '__main__', 'some_module':
+            with self.subTest(moduleName=moduleName):
+                A.B.X.__module__ = moduleName
+                with test.support.captured_stderr() as stderr, \
+                     test.support.swap_attr(sys, 'unraisablehook',
+                                            sys.__unraisablehook__):
+                         expected = self.write_unraisable_exc(
+                             A.B.X(), "msg", "obj");
+                report = stderr.getvalue()
+                self.assertIn(A.B.X.__qualname__, report)
+                if moduleName in ['builtins', '__main__']:
+                    self.assertNotIn(moduleName + '.', report)
+                else:
+                    self.assertIn(moduleName + '.', report)
 
     def test_original_unraisablehook_wrong_type(self):
         exc = ValueError(42)
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 949adefd76faac..363165d06ef834 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -172,7 +172,7 @@ def __str__(self):
                 1/0
         err = traceback.format_exception_only(X, X())
         self.assertEqual(len(err), 1)
-        str_value = '<unprintable %s object>' % X.__name__
+        str_value = '<exception str() failed>'
         if X.__module__ in ('__main__', 'builtins'):
             str_name = X.__qualname__
         else:
@@ -1171,19 +1171,45 @@ def test_syntax_error_various_offsets(self):
                 exp = "\n".join(expected)
                 self.assertEqual(exp, err)
 
-    def test_format_exception_only_qualname(self):
+    def test_exception_qualname(self):
         class A:
             class B:
                 class X(Exception):
                     def __str__(self):
                         return "I am X"
-                    pass
+
         err = self.get_report(A.B.X())
         str_value = 'I am X'
         str_name = '.'.join([A.B.X.__module__, A.B.X.__qualname__])
         exp = "%s: %s\n" % (str_name, str_value)
         self.assertEqual(exp, err)
 
+    def test_exception_modulename(self):
+        class X(Exception):
+            def __str__(self):
+                return "I am X"
+
+        for modulename in '__main__', 'builtins', 'some_module':
+            X.__module__ = modulename
+            with self.subTest(modulename=modulename):
+                err = self.get_report(X())
+                str_value = 'I am X'
+                if modulename in ['builtins', '__main__']:
+                    str_name = X.__qualname__
+                else:
+                    str_name = '.'.join([X.__module__, X.__qualname__])
+                exp = "%s: %s\n" % (str_name, str_value)
+                self.assertEqual(exp, err)
+
+    def test_exception_bad__str__(self):
+        class X(Exception):
+            def __str__(self):
+                1/0
+        err = self.get_report(X())
+        str_value = '<exception str() failed>'
+        str_name = '.'.join([X.__module__, X.__qualname__])
+        self.assertEqual(err, f"{str_name}: {str_value}\n")
+
 
 class PyExcReportingTests(BaseExceptionReportingTests, unittest.TestCase):
     #
diff --git a/Lib/traceback.py b/Lib/traceback.py
index d51c2010005b76..1b537dc5a91142 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -169,7 +169,7 @@ def _some_str(value):
     try:
         return str(value)
     except:
-        return '<unprintable %s object>' % type(value).__name__
+        return '<exception str() failed>'
 
 # --
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-09-03-12-35-17.bpo-41031.yPSJEs.rst b/Misc/NEWS.d/next/Core and Builtins/2021-09-03-12-35-17.bpo-41031.yPSJEs.rst
new file mode 100644
index 00000000000000..5dcfaa0046c65c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2021-09-03-12-35-17.bpo-41031.yPSJEs.rst	
@@ -0,0 +1 @@
+Match C and Python code formatting of unprintable exceptions and exceptions in the :mod:`__main__` module.
\ No newline at end of file
diff --git a/Python/errors.c b/Python/errors.c
index 15ca21b68400f2..b2030f728a7ebd 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -25,6 +25,7 @@ extern char *strerror(int);
 extern "C" {
 #endif
 
+_Py_IDENTIFIER(__main__);
 _Py_IDENTIFIER(__module__);
 _Py_IDENTIFIER(builtins);
 _Py_IDENTIFIER(stderr);
@@ -1297,7 +1298,8 @@ write_unraisable_exc_file(PyThreadState *tstate, PyObject *exc_type,
         }
     }
     else {
-        if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins)) {
+        if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins) &&
+            !_PyUnicode_EqualToASCIIId(modulename, &PyId___main__)) {
             if (PyFile_WriteObject(modulename, file, Py_PRINT_RAW) < 0) {
                 Py_DECREF(modulename);
                 return -1;
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 3d07f43b5256d1..0e0262c0e8c69f 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -35,6 +35,7 @@
 #endif
 
 
+_Py_IDENTIFIER(__main__);
 _Py_IDENTIFIER(builtins);
 _Py_IDENTIFIER(excepthook);
 _Py_IDENTIFIER(flush);
@@ -974,7 +975,8 @@ print_exception(PyObject *f, PyObject *value)
             err = PyFile_WriteString("<unknown>", f);
         }
         else {
-            if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins))
+            if (!_PyUnicode_EqualToASCIIId(modulename, &PyId_builtins) &&
+                !_PyUnicode_EqualToASCIIId(modulename, &PyId___main__))
             {
                 err = PyFile_WriteObject(modulename, f, Py_PRINT_RAW);
                 err += PyFile_WriteString(".", f);



More information about the Python-checkins mailing list