Skip to content

Commit 3588252

Browse files
[3.12] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359)
(cherry picked from commit 04fabe2) Co-authored-by: Jérome Perrin <[email protected]> Co-authored-by: Irit Katriel <[email protected]>
1 parent ae2a25b commit 3588252

File tree

4 files changed

+52
-2
lines changed

4 files changed

+52
-2
lines changed

Lib/test/test_traceback.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,21 @@ def __repr__(self):
16541654
err_msg = "b'please do not show me as numbers'"
16551655
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
16561656

1657+
# an exception with a broken __getattr__ raising a non expected error
1658+
class BrokenException(Exception):
1659+
broken = False
1660+
def __getattr__(self, name):
1661+
if self.broken:
1662+
raise ValueError(f'no {name}')
1663+
raise AttributeError(name)
1664+
1665+
e = BrokenException(123)
1666+
vanilla = self.get_report(e)
1667+
e.broken = True
1668+
self.assertEqual(
1669+
self.get_report(e),
1670+
vanilla + "Ignored error getting __notes__: ValueError('no __notes__')\n")
1671+
16571672
def test_exception_with_multiple_notes(self):
16581673
for e in [ValueError(42), SyntaxError('bad syntax')]:
16591674
with self.subTest(e=e):

Lib/traceback.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
738738
# Capture now to permit freeing resources: only complication is in the
739739
# unofficial API _format_final_exc_line
740740
self._str = _safe_string(exc_value, 'exception')
741-
self.__notes__ = getattr(exc_value, '__notes__', None)
741+
try:
742+
self.__notes__ = getattr(exc_value, '__notes__', None)
743+
except Exception as e:
744+
self.__notes__ = [
745+
f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}']
742746

743747
if exc_type and issubclass(exc_type, SyntaxError):
744748
# Handle SyntaxError's specially
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix rendering tracebacks for exceptions with a broken ``__getattr__``.

Python/pythonrun.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,36 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *notes)
11641164
return -1;
11651165
}
11661166

1167+
static int
1168+
get_exception_notes(struct exception_print_context *ctx, PyObject *value, PyObject **notes) {
1169+
PyObject *note = NULL;
1170+
1171+
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), notes) < 0) {
1172+
PyObject *type, *errvalue, *tback;
1173+
PyErr_Fetch(&type, &errvalue, &tback);
1174+
note = PyUnicode_FromFormat("Ignored error getting __notes__: %R", errvalue);
1175+
Py_XDECREF(type);
1176+
Py_XDECREF(errvalue);
1177+
Py_XDECREF(tback);
1178+
if (!note) {
1179+
goto error;
1180+
}
1181+
*notes = PyList_New(1);
1182+
if (!*notes) {
1183+
goto error;
1184+
}
1185+
if (PyList_SetItem(*notes, 0, note) < 0) {
1186+
Py_DECREF(*notes);
1187+
goto error;
1188+
}
1189+
}
1190+
1191+
return 0;
1192+
error:
1193+
Py_XDECREF(note);
1194+
return -1;
1195+
}
1196+
11671197
static int
11681198
print_exception(struct exception_print_context *ctx, PyObject *value)
11691199
{
@@ -1183,7 +1213,7 @@ print_exception(struct exception_print_context *ctx, PyObject *value)
11831213

11841214
/* grab the type and notes now because value can change below */
11851215
PyObject *type = (PyObject *) Py_TYPE(value);
1186-
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), &notes) < 0) {
1216+
if (get_exception_notes(ctx, value, &notes) < 0) {
11871217
goto error;
11881218
}
11891219

0 commit comments

Comments
 (0)