From bc10ba21ee605c9727d447537119b7f89731f3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Tue, 16 Jan 2024 18:49:24 +0900 Subject: [PATCH] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__ (GH-113359) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cherry picked from commit 04fabe22dd98b4d87f672254b743fbadd5206352 Adjusted for 3.11, because exception printing also happens in C code. For simplicity, instead of including the repr of the exception that happened during __getattr__, this just prints that an error occured. Co-authored-by: Jérome Perrin Co-authored-by: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> --- Lib/test/test_traceback.py | 15 +++++++++++++++ Lib/traceback.py | 5 ++++- ...2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst | 1 + Python/pythonrun.c | 6 +++++- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 2881a3489084a08..b81419c29243f29 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1601,6 +1601,21 @@ def __repr__(self): err_msg = '' self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\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__\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 ea045e27610d4d5..92e517467d5c3b0 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -733,7 +733,10 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, # 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__ = ['Ignored error getting __notes__'] 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 000000000000000..76416553a231a0d --- /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 with exceptions with a broken __getattr__ diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 91c2ad3a13d4327..6431c69bc5eb0a1 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -1219,7 +1219,11 @@ 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) { - goto error; + PyErr_Clear(); + notes = Py_BuildValue("[s]", "Ignored error getting __notes__"); + if (notes == NULL) { + goto error; + } } if (print_exception_file_and_line(ctx, &value) < 0) {