summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_exceptions.py18
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst2
-rw-r--r--Objects/object.c34
-rw-r--r--Python/ceval.c9
4 files changed, 48 insertions, 15 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py
index e3897a0..a75b7fa 100644
--- a/Lib/test/test_exceptions.py
+++ b/Lib/test/test_exceptions.py
@@ -2272,6 +2272,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
index 0000000..fabc946
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst
@@ -0,0 +1,2 @@
+Avoid overriding :exc:`AttributeError` metadata information for nested
+attribute access calls. Patch by Pablo Galindo.
diff --git a/Objects/object.c b/Objects/object.c
index 38919ff..f029a72 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -875,19 +875,29 @@ static inline int
set_attribute_error_context(PyObject* v, PyObject* name)
{
assert(PyErr_Occurred());
- // 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_SetAttr(value, &_Py_ID(name), name) ||
- PyObject_SetAttr(value, &_Py_ID(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_SetAttr(value, &_Py_ID(name), name) ||
+ PyObject_SetAttr(value, &_Py_ID(obj), v)) {
+ return 1;
}
+restore:
+ PyErr_Restore(type, value, traceback);
return 0;
}
diff --git a/Python/ceval.c b/Python/ceval.c
index 0743894..7439710 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -7607,9 +7607,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_SetAttr(value, &_Py_ID(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_SetAttr(value, &_Py_ID(name), obj);
+ }
}
PyErr_Restore(type, value, traceback);
}