]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-113358: Fix rendering tracebacks with exceptions with a broken __getattr__...
authorJérome Perrin <perrinjerome@gmail.com>
Sun, 21 Jan 2024 17:12:17 +0000 (02:12 +0900)
committerGitHub <noreply@github.com>
Sun, 21 Jan 2024 17:12:17 +0000 (17:12 +0000)
Lib/test/test_traceback.py
Lib/traceback.py
Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst [new file with mode: 0644]
Python/pythonrun.c

index e0ef9e03f1ff9f2990935bf874aa3bcdae322530..61eb0a7ef93d7f5d4c8b9f8b949e48153260cd3a 100644 (file)
@@ -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):
index f61d5db0621ab5b6836ab1a13239bf52c9b2eb9d..8247d8ff8cd1e7631440225d0606195dd1f071b7 100644 (file)
@@ -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 (file)
index 0000000..4afbbda
--- /dev/null
@@ -0,0 +1 @@
+Fix rendering tracebacks for exceptions with a broken ``__getattr__``.
index f4c5d39c59b166a25ae0d67f803077990b8e937c..5f3d249df4581495f83a3a970c56845655615afb 100644 (file)
@@ -1164,6 +1164,37 @@ error:
     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
 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 */
     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;
     }