https://github.com/python/cpython/commit/20f7cf2c7f2e3fd170a27a3ded8cc80124ba9f2d
commit: 20f7cf2c7f2e3fd170a27a3ded8cc80124ba9f2d
branch: 3.11
author: Jérome Perrin <[email protected]>
committer: iritkatriel <[email protected]>
date: 2024-01-19T20:35:57Z
summary:

[3.11] gh-113358: Fix rendering tracebacks with exceptions with a broken 
__getattr__ (GH-113359) (#114118)

files:
A Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst
M Lib/test/test_traceback.py
M Lib/traceback.py
M Python/pythonrun.c

diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py
index 2881a3489084a0..a857d61e9b4a5f 100644
--- a/Lib/test/test_traceback.py
+++ b/Lib/test/test_traceback.py
@@ -1601,6 +1601,21 @@ def __repr__(self):
         err_msg = '<note str() failed>'
         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__: 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 ea045e27610d4d..e7026e545cb1b4 100644
--- a/Lib/traceback.py
+++ b/Lib/traceback.py
@@ -733,7 +733,11 @@ 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__ = [
+                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 00000000000000..4afbbda9442676
--- /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 91c2ad3a13d432..d3c0c85680e17b 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -1199,6 +1199,36 @@ print_exception_notes(struct exception_print_context 
*ctx, PyObject *notes)
     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);
+        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)
 {
@@ -1218,7 +1248,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__), &notes) < 0) {
+    if (get_exception_notes(ctx, value, &notes) < 0) {
         goto error;
     }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to