]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.10] bpo-46940: Don't override existing AttributeError suggestion information ...
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Mon, 7 Mar 2022 13:18:36 +0000 (13:18 +0000)
committerGitHub <noreply@github.com>
Mon, 7 Mar 2022 13:18:36 +0000 (13:18 +0000)
When an exception is created in a nested call to PyObject_GetAttr, any
external calls will override the context information of the
AttributeError that we have already placed in the most internal call.
This will cause the suggestions we create to nor work properly as the
attribute name and object that we will be using are the incorrect ones.

To avoid this, we need to check first if these attributes are already
set and bail out if that's the case..
(cherry picked from commit 3b3be05a164da43f201e35b6dafbc840993a4d18)

Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
Lib/test/test_exceptions.py
Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst [new file with mode: 0644]
Objects/object.c
Python/ceval.c

index 49739723ee4731af2c006086a12b1ba0200b73ba..2bdd7214b0505b8a9b171ebebf56649ea8b0f73d 100644 (file)
@@ -2201,6 +2201,24 @@ class AttributeErrorTests(unittest.TestCase):
 
         self.assertNotIn("?", err.getvalue())
 
+    def test_attribute_error_inside_nested_getattr(self):
+        class A:
+            bluch = 1
+
+        class B:
+            def __getattribute__(self, attr):
+                a = A()
+                return a.blich
+
+        try:
+            B().something
+        except AttributeError as exc:
+            with support.captured_stderr() as err:
+                sys.__excepthook__(*sys.exc_info())
+
+        self.assertIn("Did you mean", err.getvalue())
+        self.assertIn("bluch", err.getvalue())
+
 
 class ImportErrorTests(unittest.TestCase):
 
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst
new file mode 100644 (file)
index 0000000..fabc946
--- /dev/null
@@ -0,0 +1,2 @@
+Avoid overriding :exc:`AttributeError` metadata information for nested
+attribute access calls. Patch by Pablo Galindo.
index ff816cd5b9a60508e59c41351195134743d7b09d..47c352e3d6405825e047309e0c27f7353f65f438 100644 (file)
@@ -890,19 +890,29 @@ set_attribute_error_context(PyObject* v, PyObject* name)
     assert(PyErr_Occurred());
     _Py_IDENTIFIER(name);
     _Py_IDENTIFIER(obj);
-    // Intercept AttributeError exceptions and augment them to offer
-    // suggestions later.
-    if (PyErr_ExceptionMatches(PyExc_AttributeError)){
-        PyObject *type, *value, *traceback;
-        PyErr_Fetch(&type, &value, &traceback);
-        PyErr_NormalizeException(&type, &value, &traceback);
-        if (PyErr_GivenExceptionMatches(value, PyExc_AttributeError) &&
-            (_PyObject_SetAttrId(value, &PyId_name, name) ||
-             _PyObject_SetAttrId(value, &PyId_obj, v))) {
-            return 1;
-        }
-        PyErr_Restore(type, value, traceback);
+    if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
+        return 0;
+    }
+    // Intercept AttributeError exceptions and augment them to offer suggestions later.
+    PyObject *type, *value, *traceback;
+    PyErr_Fetch(&type, &value, &traceback);
+    PyErr_NormalizeException(&type, &value, &traceback);
+    // Check if the normalized exception is indeed an AttributeError
+    if (!PyErr_GivenExceptionMatches(value, PyExc_AttributeError)) {
+        goto restore;
+    }
+    PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) value;
+    // Check if this exception was already augmented
+    if (the_exc->name || the_exc->obj) {
+        goto restore;
+    }
+    // Augment the exception with the name and object
+    if (_PyObject_SetAttrId(value, &PyId_name, name) ||
+        _PyObject_SetAttrId(value, &PyId_obj, v)) {
+        return 1;
     }
+restore:
+    PyErr_Restore(type, value, traceback);
     return 0;
 }
 
index ab10b4166d6d216523431ea21cf13f8ff59dd67f..21674e0be13240d67db569ca335df7d69c94de79 100644 (file)
@@ -6261,9 +6261,12 @@ format_exc_check_arg(PyThreadState *tstate, PyObject *exc,
         PyErr_Fetch(&type, &value, &traceback);
         PyErr_NormalizeException(&type, &value, &traceback);
         if (PyErr_GivenExceptionMatches(value, PyExc_NameError)) {
-            // We do not care if this fails because we are going to restore the
-            // NameError anyway.
-            (void)_PyObject_SetAttrId(value, &PyId_name, obj);
+            PyNameErrorObject* exc = (PyNameErrorObject*) value;
+            if (exc->name == NULL) {
+                // We do not care if this fails because we are going to restore the
+                // NameError anyway.
+                (void)_PyObject_SetAttrId(value, &PyId_name, obj);
+            }
         }
         PyErr_Restore(type, value, traceback);
     }