Skip to content

Commit 487f55d

Browse files
authored
gh-103895: Improve how invalid Exception.__notes__ are displayed (#103897)
1 parent 93107aa commit 487f55d

File tree

4 files changed

+23
-5
lines changed

4 files changed

+23
-5
lines changed

Lib/test/test_traceback.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -1539,11 +1539,11 @@ def __repr__(self):
15391539

15401540
e.__notes__ = BadThing()
15411541
notes_repr = 'bad repr'
1542-
self.assertEqual(self.get_report(e), vanilla + notes_repr)
1542+
self.assertEqual(self.get_report(e), vanilla + notes_repr + '\n')
15431543

15441544
e.__notes__ = Unprintable()
15451545
err_msg = '<__notes__ repr() failed>'
1546-
self.assertEqual(self.get_report(e), vanilla + err_msg)
1546+
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
15471547

15481548
# non-string item in the __notes__ sequence
15491549
e.__notes__ = [BadThing(), 'Final Note']
@@ -1555,6 +1555,14 @@ def __repr__(self):
15551555
err_msg = '<note str() failed>'
15561556
self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')
15571557

1558+
e.__notes__ = "please do not explode me"
1559+
err_msg = "'please do not explode me'"
1560+
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
1561+
1562+
e.__notes__ = b"please do not show me as numbers"
1563+
err_msg = "b'please do not show me as numbers'"
1564+
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
1565+
15581566
def test_exception_with_note_with_multiple_notes(self):
15591567
e = ValueError(42)
15601568
vanilla = self.get_report(e)

Lib/traceback.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -852,12 +852,16 @@ def format_exception_only(self):
852852
yield _format_final_exc_line(stype, self._str)
853853
else:
854854
yield from self._format_syntax_error(stype)
855-
if isinstance(self.__notes__, collections.abc.Sequence):
855+
856+
if (
857+
isinstance(self.__notes__, collections.abc.Sequence)
858+
and not isinstance(self.__notes__, (str, bytes))
859+
):
856860
for note in self.__notes__:
857861
note = _safe_string(note, 'note')
858862
yield from [l + '\n' for l in note.split('\n')]
859863
elif self.__notes__ is not None:
860-
yield _safe_string(self.__notes__, '__notes__', func=repr)
864+
yield "{}\n".format(_safe_string(self.__notes__, '__notes__', func=repr))
861865

862866
def _format_syntax_error(self, stype):
863867
"""Format SyntaxError exceptions (internal helper)."""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Improve handling of edge cases in showing ``Exception.__notes__``. Ensures
2+
that the messages always end with a newline and that string/bytes are not
3+
exploded over multiple lines. Patch by Carey Metcalfe.

Python/pythonrun.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
11071107
if (notes == NULL) {
11081108
return -1;
11091109
}
1110-
if (!PySequence_Check(notes)) {
1110+
if (!PySequence_Check(notes) || PyUnicode_Check(notes) || PyBytes_Check(notes)) {
11111111
int res = 0;
11121112
if (write_indented_margin(ctx, f) < 0) {
11131113
res = -1;
@@ -1122,6 +1122,9 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
11221122
Py_DECREF(s);
11231123
}
11241124
Py_DECREF(notes);
1125+
if (PyFile_WriteString("\n", f) < 0) {
1126+
res = -1;
1127+
}
11251128
return res;
11261129
}
11271130
Py_ssize_t num_notes = PySequence_Length(notes);

0 commit comments

Comments
 (0)