Skip to content

Commit a3dd16a

Browse files
committed
Return the suggestion as a object and handle it in print_exception
1 parent dbd88f0 commit a3dd16a

File tree

4 files changed

+24
-65
lines changed

4 files changed

+24
-65
lines changed

Include/internal/pycore_suggestions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
# error "this header requires Py_BUILD_CORE define"
88
#endif
99

10-
int _Py_Offer_Suggestions(PyObject* exception, PyObject* value);
10+
PyObject* _Py_Offer_Suggestions(PyObject* exception);
1111

1212
#endif /* !Py_INTERNAL_SUGGESTIONS_H */

Lib/test/test_exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,7 @@ def __getattr__(self, attr):
15701570
with support.captured_stderr() as err:
15711571
sys.__excepthook__(*sys.exc_info())
15721572

1573-
self.assertNotIn("blech", err.getvalue())
1573+
self.assertIn("blech", err.getvalue())
15741574

15751575

15761576
class ImportErrorTests(unittest.TestCase):

Python/pythonrun.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,14 @@ print_exception(PyObject *f, PyObject *value)
954954
if (err < 0) {
955955
PyErr_Clear();
956956
}
957+
PyObject* suggestions = _Py_Offer_Suggestions(value);
958+
if (suggestions) {
959+
err = PyFile_WriteString(". ", f);
960+
if (err == 0) {
961+
err = PyFile_WriteObject(suggestions, f, Py_PRINT_RAW);
962+
}
963+
Py_DECREF(suggestions);
964+
}
957965
err += PyFile_WriteString("\n", f);
958966
Py_XDECREF(tb);
959967
Py_DECREF(value);
@@ -1082,10 +1090,6 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
10821090
return;
10831091
}
10841092

1085-
if (_Py_Offer_Suggestions(exception, value) != 0) {
1086-
return;
1087-
}
1088-
10891093
Py_INCREF(file);
10901094
_PyErr_Display(file, exception, value, tb);
10911095
Py_DECREF(file);

Python/suggestions.c

Lines changed: 14 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,7 @@ distance(const char *string1, const char *string2)
131131

132132
static inline PyObject*
133133
calculate_suggestions(PyObject* dir,
134-
PyObject* name,
135-
PyObject* oldexceptionvalue)
134+
PyObject* name)
136135
{
137136
assert(!PyErr_Occurred());
138137
assert(PyList_CheckExact(dir));
@@ -164,81 +163,37 @@ calculate_suggestions(PyObject* dir,
164163
if (!suggestion) {
165164
return NULL;
166165
}
167-
return PyUnicode_FromFormat("%S. Did you mean: %U?",
168-
oldexceptionvalue, suggestion);
166+
return PyUnicode_FromFormat("Did you mean: %U?", suggestion);
169167
}
170168

171-
static int
169+
static PyObject*
172170
offer_suggestions_for_attribute_error(PyAttributeErrorObject* exc) {
173-
int return_val = -1;
174-
175171
PyObject* name = exc->name; // borrowed reference
176172
PyObject* obj = exc->obj; // borrowed reference
177173

178174
// Abort if we don't have an attribute name or we have an invalid one
179175
if (name == NULL || obj == NULL || !PyUnicode_CheckExact(name)) {
180-
return -1;
181-
}
182-
183-
PyObject* oldexceptionvalue = NULL;
184-
Py_ssize_t nargs = PyTuple_GET_SIZE(exc->args);
185-
switch (nargs) {
186-
case 0:
187-
oldexceptionvalue = PyUnicode_New(0, 0);
188-
if (oldexceptionvalue == NULL) {
189-
return -1;
190-
}
191-
break;
192-
case 1:
193-
oldexceptionvalue = PyTuple_GET_ITEM(exc->args, 0);
194-
Py_INCREF(oldexceptionvalue);
195-
// Check that the the message is an unicode objects that we can use.
196-
if (!PyUnicode_CheckExact(oldexceptionvalue)) {
197-
goto exit;
198-
}
199-
break;
200-
default:
201-
// Exceptions with more than 1 value in args are not
202-
// formatted using args, so no need to make a new suggestion.
203-
return 0;
176+
return NULL;
204177
}
205178

206179
PyObject* dir = PyObject_Dir(obj);
207180
if (dir == NULL) {
208-
goto exit;
181+
return NULL;
209182
}
210183

211-
PyObject* newexceptionvalue = calculate_suggestions(dir, name, oldexceptionvalue);
184+
PyObject* suggestions = calculate_suggestions(dir, name);
212185
Py_DECREF(dir);
213-
214-
if (newexceptionvalue == NULL) {
215-
// We did't find a suggestion :(
216-
goto exit;
217-
}
218-
219-
PyObject* new_args = PyTuple_Pack(1, newexceptionvalue);
220-
Py_DECREF(newexceptionvalue);
221-
if (new_args == NULL) {
222-
goto exit;
223-
}
224-
Py_SETREF(exc->args, new_args);
225-
exc->args = new_args;
226-
return_val = 0;
227-
228-
exit:
229-
Py_DECREF(oldexceptionvalue);
230-
231-
// Clear any error that may have happened.
232-
PyErr_Clear();
233-
return return_val;
186+
return suggestions;
234187
}
235188

236189

237-
int _Py_Offer_Suggestions(PyObject* exception, PyObject* value) {
238-
if (PyErr_GivenExceptionMatches(exception, PyExc_AttributeError) &&
239-
offer_suggestions_for_attribute_error((PyAttributeErrorObject*) value) != 0) {
240-
PyErr_Clear();
190+
// Offer suggestions for a given exception. This function does not raise exceptions
191+
// and returns NULL if no exception was found.
192+
PyObject* _Py_Offer_Suggestions(PyObject* exception) {
193+
assert(!PyErr_Occurred());
194+
if (PyErr_GivenExceptionMatches(exception, PyExc_AttributeError)) {
195+
return offer_suggestions_for_attribute_error((PyAttributeErrorObject*) exception);
241196
}
242-
return 0;
197+
return NULL;
243198
}
244199

0 commit comments

Comments
 (0)