diff options
author | Jérome Perrin <perrinjerome@gmail.com> | 2024-01-21 17:12:17 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-21 17:12:17 (GMT) |
commit | 00e8c9ce9ef73fb6f64dde7f97a75e17fcaa7541 (patch) | |
tree | 8b233f29d6fb5921189af68067339a30a0935cfe | |
parent | afefa4a74cecc505a54094c6a0f129fcdc02060a (diff) | |
download | cpython-00e8c9ce9ef73fb6f64dde7f97a75e17fcaa7541.zip cpython-00e8c9ce9ef73fb6f64dde7f97a75e17fcaa7541.tar.gz cpython-00e8c9ce9ef73fb6f64dde7f97a75e17fcaa7541.tar.bz2 |
[3.12] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359) (#114173)
-rw-r--r-- | Lib/test/test_traceback.py | 15 | ||||
-rw-r--r-- | Lib/traceback.py | 6 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst | 1 | ||||
-rw-r--r-- | Python/pythonrun.c | 33 |
4 files changed, 53 insertions, 2 deletions
diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index e0ef9e0..61eb0a7 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1654,6 +1654,21 @@ class BaseExceptionReportingTests: err_msg = "b'please do not show me as numbers'" 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): for e in [ValueError(42), SyntaxError('bad syntax')]: with self.subTest(e=e): diff --git a/Lib/traceback.py b/Lib/traceback.py index f61d5db..8247d8f 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -738,7 +738,11 @@ class TracebackException: # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line 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): # Handle SyntaxError's specially diff --git a/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst b/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst new file mode 100644 index 0000000..4afbbda --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst @@ -0,0 +1 @@ +Fix rendering tracebacks for exceptions with a broken ``__getattr__``. diff --git a/Python/pythonrun.c b/Python/pythonrun.c index f4c5d39..5f3d249 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1165,6 +1165,37 @@ error: } 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 print_exception(struct exception_print_context *ctx, PyObject *value) { PyObject *notes = NULL; @@ -1183,7 +1214,7 @@ print_exception(struct exception_print_context *ctx, PyObject *value) /* grab the type and notes now because value can change below */ 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; } |