Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1555087
PEP-678: exception notes are set by add_note(). __notes__ holds a tup…
iritkatriel Feb 13, 2022
6a38688
clear notes with del e.__notes__, remove replace arg and None note op…
iritkatriel Feb 23, 2022
7bab63e
do not create new tuple when __notes__ is accessed
iritkatriel Mar 2, 2022
396b5f1
Revert "do not create new tuple when __notes__ is accessed"
iritkatriel Mar 16, 2022
f518aa5
add __notes__ attribute in add_note. Traceback ignores it if it's not…
iritkatriel Mar 16, 2022
0438933
add in add_note a check that __notes__ is a list. Add the test. tweak…
iritkatriel Mar 16, 2022
dcc93ef
TypeError in add_note if __notes__ is not a list
iritkatriel Mar 16, 2022
614378e
if __notes__ is not sequence, print repr(__notes__). If note is not a…
iritkatriel Mar 17, 2022
f240e71
simplify traceback code (no need to special case note which is a string)
iritkatriel Mar 21, 2022
6786dbd
shallow copy the notes in split(), if it's a sequence
iritkatriel Mar 22, 2022
bdd4e2a
split() ignores notes if they are not a sequence
iritkatriel Mar 23, 2022
711e804
typo in doc
iritkatriel Mar 28, 2022
e147f52
fix typo in test
iritkatriel Apr 12, 2022
622ca51
Merge remote-tracking branch 'upstream/main' into pep-678
iritkatriel Apr 12, 2022
404f80d
📜🤖 Added by blurb_it.
blurb-it[bot] Apr 12, 2022
c014ad8
update test_traceback
iritkatriel Apr 12, 2022
47bfcdc
Merge branch 'main' into pep-678
iritkatriel Apr 12, 2022
2ae22e4
update whatnew
iritkatriel Apr 12, 2022
95be670
METH_VARARGS --> METH_O
iritkatriel Apr 14, 2022
520efd1
Merge branch 'main' into pep-678
iritkatriel Apr 14, 2022
a14e915
add_note no longer has a replace kwarg
iritkatriel Apr 14, 2022
991e982
finish converting add_note to METH_O
iritkatriel Apr 14, 2022
602b4c4
fix whitespace
iritkatriel Apr 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
if __notes__ is not sequence, print repr(__notes__). If note is not a…
… string, print str(note)
  • Loading branch information
iritkatriel committed Mar 17, 2022
commit 614378e465087f5529eed26d3a63c73de3b399f6
41 changes: 34 additions & 7 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ def test_syntax_error_various_offsets(self):
self.assertEqual(exp, err)

def test_exception_with_note(self):
e = ValueError(42)
e = ValueError(123)
vanilla = self.get_report(e)

e.add_note('My Note')
Expand All @@ -1340,13 +1340,40 @@ def test_exception_with_note(self):
del e.__notes__
self.assertEqual(self.get_report(e), vanilla)

# non-sequence __notes__ is ignored
e.__notes__ = 42
self.assertEqual(self.get_report(e), vanilla)
def test_exception_with_invalid_notes(self):
e = ValueError(123)
vanilla = self.get_report(e)

# non-sequence __notes__
class BadThing:
def __str__(self):
return 'bad str'

def __repr__(self):
return 'bad repr'

# unprintable, non-sequence __notes__
class Unprintable:
def __repr__(self):
raise ValueError('bad value')

e.__notes__ = BadThing()
notes_repr = 'bad repr'
self.assertEqual(self.get_report(e), vanilla + notes_repr)

e.__notes__ = Unprintable()
err_msg = '<__notes__ repr() failed>'
self.assertEqual(self.get_report(e), vanilla + err_msg)

# non-string item in the __notes__ sequence
e.__notes__ = [BadThing(), 'Final Note']
bad_note = 'bad str'
self.assertEqual(self.get_report(e), vanilla + bad_note + '\nFinal Note\n')

# non-string items in the __notes__ sequence are ignored
e.__notes__ = [42, 'Final Note']
self.assertEqual(self.get_report(e), vanilla + 'Final Note\n')
# unprintable, non-string item in the __notes__ sequence
e.__notes__ = [Unprintable(), 'Final Note']
err_msg = '<note str() failed>'
self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')

def test_exception_with_note_with_multiple_notes(self):
e = ValueError(42)
Expand Down
17 changes: 10 additions & 7 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,18 +163,18 @@ def format_exception_only(exc, /, value=_sentinel):
# -- not official API but folk probably use these two functions.

def _format_final_exc_line(etype, value):
valuestr = _some_str(value)
valuestr = _safe_string(value, 'exception')
if value is None or not valuestr:
line = "%s\n" % etype
else:
line = "%s: %s\n" % (etype, valuestr)
return line

def _some_str(value):
def _safe_string(value, what, func=str):
try:
return str(value)
return func(value)
except:
return '<exception str() failed>'
return f'<{what} {func.__name__}() failed>'

# --

Expand Down Expand Up @@ -688,7 +688,7 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
self.exc_type = exc_type
# Capture now to permit freeing resources: only complication is in the
# unofficial API _format_final_exc_line
self._str = _some_str(exc_value)
self._str = _safe_string(exc_value, 'exception')
self.__notes__ = getattr(exc_value, '__notes__', None)

if exc_type and issubclass(exc_type, SyntaxError):
Expand Down Expand Up @@ -824,8 +824,11 @@ def format_exception_only(self):
yield from self._format_syntax_error(stype)
if isinstance(self.__notes__, collections.abc.Sequence):
for note in self.__notes__:
if isinstance(note, str):
yield from [l + '\n' for l in note.split('\n')]
if not isinstance(note, str):
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)

def _format_syntax_error(self, stype):
"""Format SyntaxError exceptions (internal helper)."""
Expand Down
36 changes: 34 additions & 2 deletions Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -1145,8 +1145,21 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
return -1;
}
if (!PySequence_Check(notes)) {
int res = 0;
if (write_indented_margin(ctx, f) < 0) {
res = -1;
}
PyObject *s = PyObject_Repr(notes);
if (s == NULL) {
PyErr_Clear();
res = PyFile_WriteString("<__notes__ repr() failed>", f);
}
else {
res = PyFile_WriteObject(s, f, Py_PRINT_RAW);
Py_DECREF(s);
}
Py_DECREF(notes);
return 0;
return res;
}
Py_ssize_t num_notes = PySequence_Length(notes);
PyObject *lines = NULL;
Expand Down Expand Up @@ -1176,8 +1189,27 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *value)
Py_CLEAR(lines);
}
else {
/* Ignore notes which are not strings */
int res = 0;
if (write_indented_margin(ctx, f) < 0) {
res = -1;
}
PyObject *s = PyObject_Str(note);
if (s == NULL) {
PyErr_Clear();
res = PyFile_WriteString("<note str() failed>", f);
}
else {
res = PyFile_WriteObject(s, f, Py_PRINT_RAW);
Py_DECREF(s);
}
Py_DECREF(note);
if (res < 0) {
goto error;
}
if (PyFile_WriteString("\n", f) < 0) {
goto error;
}

}
}

Expand Down