]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-103895: Improve how invalid `Exception.__notes__` are displayed (#103897)
authorCarey Metcalfe <carey@cmetcalfe.ca>
Mon, 1 May 2023 07:32:04 +0000 (01:32 -0600)
committerGitHub <noreply@github.com>
Mon, 1 May 2023 07:32:04 +0000 (08:32 +0100)
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Core and Builtins/2023-04-26-17-56-18.gh-issue-103895.ESB6tn.rst [new file with mode: 0644]
Python/pythonrun.c

index 5e2b353782994e23b3156a292304fe16ffba3aff..19a2be88d2c1bc98f235af671d7f17a90af43a02 100644 (file)
@@ -1539,11 +1539,11 @@ class BaseExceptionReportingTests:
 
         e.__notes__ = BadThing()
         notes_repr = 'bad repr'
-        self.assertEqual(self.get_report(e), vanilla + notes_repr)
+        self.assertEqual(self.get_report(e), vanilla + notes_repr + '\n')
 
         e.__notes__ = Unprintable()
         err_msg = '<__notes__ repr() failed>'
-        self.assertEqual(self.get_report(e), vanilla + err_msg)
+        self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
 
         # non-string item in the __notes__ sequence
         e.__notes__  = [BadThing(), 'Final Note']
@@ -1555,6 +1555,14 @@ class BaseExceptionReportingTests:
         err_msg = '<note str() failed>'
         self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')
 
+        e.__notes__  = "please do not explode me"
+        err_msg = "'please do not explode me'"
+        self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
+
+        e.__notes__  = b"please do not show me as numbers"
+        err_msg = "b'please do not show me as numbers'"
+        self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
+
     def test_exception_with_note_with_multiple_notes(self):
         e = ValueError(42)
         vanilla = self.get_report(e)
index 9e720ac9948fcea6d25735e06c11e17b6d4d1be4..ba4a9ffd001b53e8f975dc39cb395b1dae8cd927 100644 (file)
@@ -852,12 +852,16 @@ class TracebackException:
             yield _format_final_exc_line(stype, self._str)
         else:
             yield from self._format_syntax_error(stype)
-        if isinstance(self.__notes__, collections.abc.Sequence):
+
+        if (
+            isinstance(self.__notes__, collections.abc.Sequence)
+            and not isinstance(self.__notes__, (str, bytes))
+        ):
             for note in self.__notes__:
                 note = _safe_string(note, 'note')
                 yield from [l + '\n' for l in note.split('\n')]
         elif self.__notes__ is not None:
-            yield _safe_string(self.__notes__, '__notes__', func=repr)
+            yield "{}\n".format(_safe_string(self.__notes__, '__notes__', func=repr))
 
     def _format_syntax_error(self, stype):
         """Format SyntaxError exceptions (internal helper)."""
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-04-26-17-56-18.gh-issue-103895.ESB6tn.rst b/Misc/NEWS.d/next/Core and Builtins/2023-04-26-17-56-18.gh-issue-103895.ESB6tn.rst
new file mode 100644 (file)
index 0000000..6fed304
--- /dev/null
@@ -0,0 +1,3 @@
+Improve handling of edge cases in showing ``Exception.__notes__``. Ensures
+that the messages always end with a newline and that string/bytes are not
+exploded over multiple lines. Patch by Carey Metcalfe.
index b16d3f53f89fb9fc33706199ca56f9834f691ca6..05e7b4370869afd9b2f5b2c6cf25fdd0f5f01739 100644 (file)
@@ -1107,7 +1107,7 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
     if (notes == NULL) {
         return -1;
     }
-    if (!PySequence_Check(notes)) {
+    if (!PySequence_Check(notes) || PyUnicode_Check(notes) || PyBytes_Check(notes)) {
         int res = 0;
         if (write_indented_margin(ctx, f) < 0) {
             res = -1;
@@ -1122,6 +1122,9 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
             Py_DECREF(s);
         }
         Py_DECREF(notes);
+        if (PyFile_WriteString("\n", f) < 0) {
+            res = -1;
+        }
         return res;
     }
     Py_ssize_t num_notes = PySequence_Length(notes);