8000 [3.10] bpo-46940: Don't override existing AttributeError suggestion i… · python/cpython@3594ebc · GitHub
[go: up one dir, main page]

Skip to content

Commit 3594ebc

Browse files
authored
[3.10] bpo-46940: Don't override existing AttributeError suggestion information (GH-31710) (GH-31724)
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 3b3be05) Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com>
1 parent 8acbb93 commit 3594ebc

File tree

4 files changed

+48
-15
lines changed

4 files changed

+48
-15
lines changed

Lib/test/test_exceptions.py

Lines changed: 18 additions & 0 deletions 10000
Original file line numberDiff line numberDiff line change
@@ -2201,6 +2201,24 @@ def test_attribute_error_with_bad_name(self):
22012201

22022202
self.assertNotIn("?", err.getvalue())
22032203

2204+
def test_attribute_error_inside_nested_getattr(self):
2205+
class A:
2206+
bluch = 1
2207+
2208+
class B:
2209+
def __getattribute__(self, attr):
2210+
a = A()
2211+
return a.blich
2212+
2213+
try:
2214+
B().something
2215+
except AttributeError as exc:
2216+
with support.captured_stderr() as err:
2217+
sys.__excepthook__(*sys.exc_info())
2218+
2219+
self.assertIn("Did you mean", err.getvalue())
2220+
self.assertIn("bluch", err.getvalue())
2221+
22042222

22052223
class ImportErrorTests(unittest.TestCase):
22062224

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Avoid overriding :exc:`AttributeError` metadata information for nested
2+
attribute access calls. Patch by Pablo Galindo.

Objects/object.c

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -890,19 +890,29 @@ set_attribute_error_context(PyObject* v, PyObject* name)
890890
assert(PyErr_Occurred());
891891
_Py_IDENTIFIER(name);
892892
_Py_IDENTIFIER(obj);
893-
// Intercept AttributeError exceptions and augment them to offer
894-
// suggestions later.
895-
if (PyErr_ExceptionMatches(PyExc_AttributeError)){
896-
PyObject *type, *value, *traceback;
897-
PyErr_Fetch(&type, &value, &traceback);
898-
PyErr_NormalizeException(&type, &value, &traceback);
899-
if (PyErr_GivenExceptionMatches(value, PyExc_AttributeError) &&
900-
(_PyObject_SetAttrId(value, &PyId_name, name) ||
901-
_PyObject_SetAttrId(value, &PyId_obj, v))) {
902-
return 1;
903-
}
904-
PyErr_Restore(type, value, traceback);
893+
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
894+
return 0;
895+
}
896+
// Intercept AttributeError exceptions and augment them to offer suggestions later.
897+
PyObject *type, *value, *traceback;
898+
PyErr_Fetch(&type, &value, &traceback);
899+
PyErr_NormalizeException(&type, &value, &traceback);
900+
// Check if the normalized exception is indeed an AttributeError
901+
if (!PyErr_GivenExceptionMatches(value, PyExc_AttributeError)) {
902+
goto restore;
903+
}
904+
PyAttributeErrorObject* the_exc = (PyAttributeErrorObject*) value;
905+
// Check if this exception was already augmented
906+
if (the_exc->name || the_exc->obj) {
907+
goto restore;
908+
}
909+
// Augment the exception with the name and object
910+
if (_PyObject_SetAttrId(value, &PyId_name, name) ||
911+
_PyObject_SetAttrId(value, &PyId_obj, v)) {
912+
return 1;
905913
}
914+
restore:
915+
PyErr_Restore(type, value, traceback);
906916
return 0;
907917
}
908918

Python/ceval.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6261,9 +6261,12 @@ format_exc_check_arg(PyThreadState *tstate, PyObject *exc,
62616261
PyErr_Fetch(&type, &value, &traceback);
62626262
PyErr_NormalizeException(&type, &value, &traceback);
62636263
if (PyErr_GivenExceptionMatches(value, PyExc_NameError)) {
6264-
// We do not care if this fails because we are going to restore the
6265-
// NameError anyway.
6266-
(void)_PyObject_SetAttrId(value, &PyId_name, obj);
6264+
PyNameErrorObject* exc = (PyNameErrorObject*) value;
6265+
if (exc->name == NULL) {
6266+
// We do not care if this fails because we are going to restore the
6267+
// NameError anyway.
6268+
(void)_PyObject_SetAttrId(value, &PyId_name, obj);
6269+
}
62676270
}
62686271
PyErr_Restore(type, value, traceback);
62696272
}

0 commit comments

Comments
 (0)
0