Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 20f7cf2

Browse files
authored
[3.11] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359) (#114118)
1 parent a4ad7a0 commit 20f7cf2

File tree

4 files changed

+52
-2
lines changed

4 files changed

+52
-2
lines changed

Lib/test/test_traceback.py

+15
Original file line numberDiff line numberDiff line change
@@ -1601,6 +1601,21 @@ def __repr__(self):
16011601
err_msg = '<note str() failed>'
16021602
self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')
16031603

1604+
# an exception with a broken __getattr__ raising a non expected error
1605+
class BrokenException(Exception):
1606+
broken = False
1607+
def __getattr__(self, name):
1608+
if self.broken:
1609+
raise ValueError(f'no {name}')
1610+
raise AttributeError(name)
1611+
1612+
e = BrokenException(123)
1613+
vanilla = self.get_report(e)
1614+
e.broken = True
1615+
self.assertEqual(
1616+
self.get_report(e),
1617+
vanilla + "Ignored error getting __notes__: ValueError('no __notes__')\n")
1618+
16041619
def test_exception_with_multiple_notes(self):
16051620
for e in [ValueError(42), SyntaxError('bad syntax')]:
16061621
with self.subTest(e=e):

Lib/traceback.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -733,7 +733,11 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
733733
# Capture now to permit freeing resources: only complication is in the
734734
# unofficial API _format_final_exc_line
735735
self._str = _safe_string(exc_value, 'exception')
736-
self.__notes__ = getattr(exc_value, '__notes__', None)
736+
try:
737+
self.__notes__ = getattr(exc_value, '__notes__', None)
738+
except Exception as e:
739+
self.__notes__ = [
740+
f'Ignored error getting __notes__: {_safe_string(e, "__notes__", repr)}']
737741

738742
if exc_type and issubclass(exc_type, SyntaxError):
739743
# Handle SyntaxError's specially
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix rendering tracebacks for exceptions with a broken ``__getattr__``.

Python/pythonrun.c

+31-1
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,36 @@ print_exception_notes(struct exception_print_context *ctx, PyObject *notes)
11991199
return -1;
12001200
}
12011201

1202+
static int
1203+
get_exception_notes(struct exception_print_context *ctx, PyObject *value, PyObject **notes) {
1204+
PyObject *note = NULL;
1205+
1206+
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), notes) < 0) {
1207+
PyObject *type, *errvalue, *tback;
1208+
PyErr_Fetch(&type, &errvalue, &tback);
1209+
note = PyUnicode_FromFormat("Ignored error getting __notes__: %R", errvalue);
1210+
Py_XDECREF(type);
1211+
Py_XDECREF(errvalue);
1212+
Py_XDECREF(tback);
1213+
if (!note) {
1214+
goto error;
1215+
}
1216+
*notes = PyList_New(1);
1217+
if (!*notes) {
1218+
goto error;
1219+
}
1220+
if (PyList_SetItem(*notes, 0, note) < 0) {
1221+
Py_DECREF(*notes);
1222+
goto error;
1223+
}
1224+
}
1225+
1226+
return 0;
1227+
error:
1228+
Py_XDECREF(note);
1229+
return -1;
1230+
}
1231+
12021232
static int
12031233
print_exception(struct exception_print_context *ctx, PyObject *value)
12041234
{
@@ -1218,7 +1248,7 @@ print_exception(struct exception_print_context *ctx, PyObject *value)
12181248

12191249
/* grab the type and notes now because value can change below */
12201250
PyObject *type = (PyObject *) Py_TYPE(value);
1221-
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), &notes) < 0) {
1251+
if (get_exception_notes(ctx, value, &notes) < 0) {
12221252
goto error;
12231253
}
12241254

0 commit comments

Comments
 (0)