mirror of
https://github.com/python/cpython.git
synced 2025-07-19 09:15:34 +00:00
[3.12] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359) (#114173)
This commit is contained in:
parent
afefa4a74c
commit
00e8c9ce9e
4 changed files with 53 additions and 2 deletions
|
@ -1654,6 +1654,21 @@ class BaseExceptionReportingTests:
|
||||||
err_msg = "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')
|
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
|
||||||
|
|
||||||
|
# an exception with a broken __getattr__ raising a non expected error
|
||||||
|
class BrokenException(Exception):
|
||||||
|
broken = False
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if self.broken:
|
||||||
|
raise ValueError(f'no {name}')
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
e = BrokenException(123)
|
||||||
|
vanilla = self.get_report(e)
|
||||||
|
e.broken = True
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_report(e),
|
||||||
|
vanilla + "Ignored error getting __notes__: ValueError('no __notes__')\n")
|
||||||
|
|
||||||
def test_exception_with_multiple_notes(self):
|
def test_exception_with_multiple_notes(self):
|
||||||
for e in [ValueError(42), SyntaxError('bad syntax')]:
|
for e in [ValueError(42), SyntaxError('bad syntax')]:
|
||||||
with self.subTest(e=e):
|
with self.subTest(e=e):
|
||||||
|
|
|
@ -738,7 +738,11 @@ class TracebackException:
|
||||||
# Capture now to permit freeing resources: only complication is in the
|
# Capture now to permit freeing resources: only complication is in the
|
||||||
# unofficial API _format_final_exc_line
|
# unofficial API _format_final_exc_line
|
||||||
self._str = _safe_string(exc_value, 'exception')
|
self._str = _safe_string(exc_value, 'exception')
|
||||||
self.__notes__ = getattr(exc_value, '__notes__', None)
|
try:
|
||||||
|
self.__notes__ = getattr(exc_value, '__notes__', None)
|
||||||
|
except Exception as e:
|
||||||
|
self.__notes__ = [
|
||||||
|
f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}']
|
||||||
|
|
||||||
if exc_type and issubclass(exc_type, SyntaxError):
|
if exc_type and issubclass(exc_type, SyntaxError):
|
||||||
# Handle SyntaxError's specially
|
# Handle SyntaxError's specially
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix rendering tracebacks for exceptions with a broken ``__getattr__``.
|
|
@ -1164,6 +1164,37 @@ error:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_exception_notes(struct exception_print_context *ctx, PyObject *value, PyObject **notes) {
|
||||||
|
PyObject *note = NULL;
|
||||||
|
|
||||||
|
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), notes) < 0) {
|
||||||
|
PyObject *type, *errvalue, *tback;
|
||||||
|
PyErr_Fetch(&type, &errvalue, &tback);
|
||||||
|
PyErr_NormalizeException(&type, &errvalue, &tback);
|
||||||
|
note = PyUnicode_FromFormat("Ignored error getting __notes__: %R", errvalue);
|
||||||
|
Py_XDECREF(type);
|
||||||
|
Py_XDECREF(errvalue);
|
||||||
|
Py_XDECREF(tback);
|
||||||
|
if (!note) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
*notes = PyList_New(1);
|
||||||
|
if (!*notes) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyList_SetItem(*notes, 0, note) < 0) {
|
||||||
|
Py_DECREF(*notes);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
error:
|
||||||
|
Py_XDECREF(note);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
print_exception(struct exception_print_context *ctx, PyObject *value)
|
print_exception(struct exception_print_context *ctx, PyObject *value)
|
||||||
{
|
{
|
||||||
|
@ -1183,7 +1214,7 @@ print_exception(struct exception_print_context *ctx, PyObject *value)
|
||||||
|
|
||||||
/* grab the type and notes now because value can change below */
|
/* grab the type and notes now because value can change below */
|
||||||
PyObject *type = (PyObject *) Py_TYPE(value);
|
PyObject *type = (PyObject *) Py_TYPE(value);
|
||||||
if (_PyObject_LookupAttr(value, &_Py_ID(__notes__), ¬es) < 0) {
|
if (get_exception_notes(ctx, value, ¬es) < 0) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue