Skip to content

Commit 67d733f

Browse files
committed
bpo-38530: Clean exceptions if dir() fails when making suggestions for AttributeError
1 parent 5bf8bf2 commit 67d733f

File tree

3 files changed

+17
-7
lines changed

3 files changed

+17
-7
lines changed

Lib/test/test_exceptions.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,6 +1695,19 @@ def __getattr__(self, attr):
16951695

16961696
self.assertIn("blech", err.getvalue())
16971697

1698+
def test_attribute_error_with_failing_dict(self):
1699+
class T:
1700+
bluch = 1
1701+
def __dir__(self):
1702+
raise AttributeError("oh no!")
1703+
1704+
try:
1705+
T().blich
1706+
except AttributeError as exc:
1707+
with support.captured_stderr() as err:
1708+
sys.__excepthook__(*sys.exc_info())
1709+
1710+
self.assertNotIn("blech", err.getvalue())
16981711

16991712
class ImportErrorTests(unittest.TestCase):
17001713

Python/pythonrun.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,8 @@ print_exception(PyObject *f, PyObject *value)
962962
err += PyFile_WriteString("?", f);
963963
}
964964
Py_DECREF(suggestions);
965+
} else if (PyErr_Occurred()) {
966+
PyErr_Clear();
965967
}
966968
err += PyFile_WriteString("\n", f);
967969
Py_XDECREF(tb);

Python/suggestions.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,12 @@ calculate_suggestions(PyObject *dir,
8989
PyObject *suggestion = NULL;
9090
const char *name_str = PyUnicode_AsUTF8(name);
9191
if (name_str == NULL) {
92-
PyErr_Clear();
9392
return NULL;
9493
}
9594
for (int i = 0; i < dir_size; ++i) {
9695
PyObject *item = PyList_GET_ITEM(dir, i);
9796
const char *item_str = PyUnicode_AsUTF8(item);
9897
if (item_str == NULL) {
99-
PyErr_Clear();
10098
return NULL;
10199
}
102100
Py_ssize_t current_distance = levenshtein_distance(name_str, item_str);
@@ -156,7 +154,6 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) {
156154
assert(code != NULL && code->co_varnames != NULL);
157155
PyObject *dir = PySequence_List(code->co_varnames);
158156
if (dir == NULL) {
159-
PyErr_Clear();
160157
return NULL;
161158
}
162159

@@ -168,7 +165,6 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) {
168165

169166
dir = PySequence_List(frame->f_globals);
170167
if (dir == NULL) {
171-
PyErr_Clear();
172168
return NULL;
173169
}
174170
suggestions = calculate_suggestions(dir, name);
@@ -178,16 +174,15 @@ offer_suggestions_for_name_error(PyNameErrorObject *exc) {
178174
}
179175

180176
// Offer suggestions for a given exception. Returns a python string object containing the
181-
// suggestions. This function does not raise exceptions and returns NULL if no suggestion was found.
177+
// suggestions. This function returns NULL if no suggestion was found or if an exception happened,
178+
// users must call PyErr_Occurred() to disambiguate.
182179
PyObject *_Py_Offer_Suggestions(PyObject *exception) {
183180
PyObject *result = NULL;
184-
assert(!PyErr_Occurred()); // Check that we are not going to clean any existing exception
185181
if (PyErr_GivenExceptionMatches(exception, PyExc_AttributeError)) {
186182
result = offer_suggestions_for_attribute_error((PyAttributeErrorObject *) exception);
187183
} else if (PyErr_GivenExceptionMatches(exception, PyExc_NameError)) {
188184
result = offer_suggestions_for_name_error((PyNameErrorObject *) exception);
189185
}
190-
assert(!PyErr_Occurred());
191186
return result;
192187
}
193188

0 commit comments

Comments
 (0)