diff options
-rw-r--r-- | Lib/test/test_exceptions.py | 18 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-03-06-20-16-13.bpo-46940._X47Hx.rst | 2 | ||||
-rw-r--r-- | Objects/object.c | 34 | ||||
-rw-r--r-- | Python/ceval.c | 9 |
4 files changed, 48 insertions, 15 deletions
diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 4973972..2bdd721 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -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 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 ff816cd..47c352e 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -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; } diff --git a/Python/ceval.c b/Python/ceval.c index ab10b41..21674e0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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); } |