summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJérome Perrin <perrinjerome@gmail.com>2024-01-21 17:12:17 (GMT)
committerGitHub <noreply@github.com>2024-01-21 17:12:17 (GMT)
commit00e8c9ce9ef73fb6f64dde7f97a75e17fcaa7541 (patch)
tree8b233f29d6fb5921189af68067339a30a0935cfe
parentafefa4a74cecc505a54094c6a0f129fcdc02060a (diff)
downloadcpython-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.py15
-rw-r--r--Lib/traceback.py6
-rw-r--r--Misc/NEWS.d/next/Library/2023-12-21-14-55-06.gh-issue-113358.nRkiSL.rst1
-rw-r--r--Python/pythonrun.c33
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__), &notes) < 0) {
+ if (get_exception_notes(ctx, value, &notes) < 0) {
goto error;
}