]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-41031: Match C and Python code formatting of unprintable exceptions and exception...
authorIrit Katriel <1055913+iritkatriel@users.noreply.github.com>
Sun, 5 Sep 2021 15:54:13 +0000 (16:54 +0100)
committerGitHub <noreply@github.com>
Sun, 5 Sep 2021 15:54:13 +0000 (18:54 +0300)
Lib/test/test_sys.py
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Core and Builtins/2021-09-03-12-35-17.bpo-41031.yPSJEs.rst [new file with mode: 0644]
Python/errors.c
Python/pythonrun.c

index 12305ca95d0a0db093603ec52d48e053d91f024d..e98803b48f6ac0efad3b320c9fc093e7b5bdaeeb 100644 (file)
@@ -1071,19 +1071,29 @@ class UnraisableHookTest(unittest.TestCase):
                 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)
index 949adefd76faac27dabfeec3915bf56355218097..363165d06ef8345e9dbe4828763129c119d411a1 100644 (file)
@@ -172,7 +172,7 @@ class TracebackCases(unittest.TestCase):
                 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 @@ class BaseExceptionReportingTests:
                 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):
     #
index d51c2010005b768b924dbe3bc0e6e610ff149ed5..1b537dc5a9114224b2c7a4b8594b55903ce9b3ac 100644 (file)
@@ -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 (file)
index 0000000..5dcfaa0
--- /dev/null
@@ -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
index 15ca21b68400f223fee23aaed3036dd55d130721..b2030f728a7ebdcfc7bbff636c2597b775222102 100644 (file)
@@ -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;
index 3d07f43b5256d1bd833f3a9b93416ee6900bba21..0e0262c0e8c69f1d7fec28c7fa93f234eb858701 100644 (file)
@@ -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);