Skip to content

Commit

Permalink
gh-103895: Improve how invalid Exception.__notes__ are displayed (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pR0Ps authored May 1, 2023
1 parent 93107aa commit 487f55d
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 5 deletions.
12 changes: 10 additions & 2 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1539,11 +1539,11 @@ def __repr__(self):

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']
Expand All @@ -1555,6 +1555,14 @@ def __repr__(self):
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)
Expand Down
8 changes: 6 additions & 2 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,12 +852,16 @@ def format_exception_only(self):
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)."""
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 4 additions & 1 deletion Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 487f55d

Please sign in to comment.