Skip to content

Commit d85a8ea

Browse files
committed
gh-110721: Remove unused code from suggestions.c after moving PyErr_Display to use the traceback module
Signed-off-by: Pablo Galindo <[email protected]>
1 parent 1ae7ceb commit d85a8ea

File tree

7 files changed

+116
-219
lines changed

7 files changed

+116
-219
lines changed

Lib/traceback.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,13 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
14971497
if hasattr(self, wrong_name):
14981498
return f"self.{wrong_name}"
14991499

1500+
try:
1501+
import _suggestions
1502+
except ImportError:
1503+
pass
1504+
else:
1505+
return _suggestions._generate_suggestions(d, wrong_name)
1506+
15001507
# Compute closest match
15011508

15021509
if len(d) > _MAX_CANDIDATE_ITEMS:

Modules/Setup.bootstrap.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ faulthandler faulthandler.c
1111
posix posixmodule.c
1212
_signal signalmodule.c
1313
_tracemalloc _tracemalloc.c
14+
_suggestions _suggestions.c
1415

1516
# modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
1617
_codecs _codecsmodule.c

Modules/_suggestions.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include "Python.h"
2+
#include "pycore_pyerrors.h"
3+
#include "clinic/_suggestions.c.h"
4+
5+
/*[clinic input]
6+
module _suggestions
7+
[clinic start generated code]*/
8+
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e58d81fafad5637b]*/
9+
10+
/*[clinic input]
11+
_suggestions._generate_suggestions
12+
candidates: object
13+
item: unicode
14+
/
15+
Returns the candidate in candidates that's closest to item
16+
[clinic start generated code]*/
17+
18+
static PyObject *
19+
_suggestions__generate_suggestions_impl(PyObject *module,
20+
PyObject *candidates, PyObject *item)
21+
/*[clinic end generated code: output=79be7b653ae5e7ca input=ba2a8dddc654e33a]*/
22+
{
23+
// Check if dir is a list
24+
if (!PyList_Check(candidates)) {
25+
PyErr_SetString(PyExc_TypeError, "candidates must be a list");
26+
return NULL;
27+
}
28+
29+
// Check if all elements in the list are Unicode
30+
Py_ssize_t size = PyList_Size(candidates);
31+
for (Py_ssize_t i = 0; i < size; ++i) {
32+
PyObject *elem = PyList_GetItem(candidates, i);
33+
if (!PyUnicode_Check(elem)) {
34+
PyErr_SetString(PyExc_TypeError, "all elements in 'candidates' must be strings");
35+
return NULL;
36+
}
37+
}
38+
39+
PyObject* result = _Py_CalculateSuggestions(candidates, item);
40+
if (!result && !PyErr_Occurred()) {
41+
Py_RETURN_NONE;
42+
}
43+
return result;
44+
}
45+
46+
47+
static PyMethodDef module_methods[] = {
48+
_SUGGESTIONS__GENERATE_SUGGESTIONS_METHODDEF
49+
{NULL, NULL, 0, NULL} // Sentinel
50+
};
51+
52+
static struct PyModuleDef suggestions_module = {
53+
PyModuleDef_HEAD_INIT,
54+
"_suggestions",
55+
NULL,
56+
-1,
57+
module_methods
58+
};
59+
60+
PyMODINIT_FUNC PyInit__suggestions(void) {
61+
return PyModule_Create(&suggestions_module);
62+
}
63+

Modules/clinic/_suggestions.c.h

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PCbuild/pythoncore.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@
424424
<ClInclude Include="..\Modules\_sre\sre_lib.h" />
425425
<ClCompile Include="..\Modules\_stat.c" />
426426
<ClCompile Include="..\Modules\_struct.c" />
427+
<ClCompile Include="..\Modules\_suggestions.c" />
427428
<ClCompile Include="..\Modules\_weakref.c" />
428429
<ClCompile Include="..\Modules\arraymodule.c" />
429430
<ClCompile Include="..\Modules\atexitmodule.c" />

PCbuild/pythoncore.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,9 @@
932932
<ClCompile Include="..\Modules\_struct.c">
933933
<Filter>Modules</Filter>
934934
</ClCompile>
935+
<ClCompile Include="..\Modules\_suggestions.c">
936+
<Filter>Modules</Filter>
937+
</ClCompile>
935938
<ClCompile Include="..\Modules\_weakref.c">
936939
<Filter>Modules</Filter>
937940
</ClCompile>

Python/suggestions.c

Lines changed: 0 additions & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -178,225 +178,6 @@ _Py_CalculateSuggestions(PyObject *dir,
178178
return Py_XNewRef(suggestion);
179179
}
180180

181-
static PyObject *
182-
get_suggestions_for_attribute_error(PyAttributeErrorObject *exc)
183-
{
184-
PyObject *name = exc->name; // borrowed reference
185-
PyObject *obj = exc->obj; // borrowed reference
186-
187-
// Abort if we don't have an attribute name or we have an invalid one
188-
if (name == NULL || obj == NULL || !PyUnicode_CheckExact(name)) {
189-
return NULL;
190-
}
191-
192-
PyObject *dir = PyObject_Dir(obj);
193-
if (dir == NULL) {
194-
return NULL;
195-
}
196-
197-
PyObject *suggestions = _Py_CalculateSuggestions(dir, name);
198-
Py_DECREF(dir);
199-
return suggestions;
200-
}
201-
202-
static PyObject *
203-
offer_suggestions_for_attribute_error(PyAttributeErrorObject *exc)
204-
{
205-
PyObject* suggestion = get_suggestions_for_attribute_error(exc);
206-
if (suggestion == NULL) {
207-
return NULL;
208-
}
209-
// Add a trailer ". Did you mean: (...)?"
210-
PyObject* result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
211-
Py_DECREF(suggestion);
212-
return result;
213-
}
214-
215-
static PyObject *
216-
get_suggestions_for_name_error(PyObject* name, PyFrameObject* frame)
217-
{
218-
PyCodeObject *code = PyFrame_GetCode(frame);
219-
assert(code != NULL && code->co_localsplusnames != NULL);
220-
221-
PyObject *varnames = _PyCode_GetVarnames(code);
222-
Py_DECREF(code);
223-
if (varnames == NULL) {
224-
return NULL;
225-
}
226-
PyObject *dir = PySequence_List(varnames);
227-
Py_DECREF(varnames);
228-
if (dir == NULL) {
229-
return NULL;
230-
}
231-
232-
// Are we inside a method and the instance has an attribute called 'name'?
233-
int res = PySequence_Contains(dir, &_Py_ID(self));
234-
if (res < 0) {
235-
goto error;
236-
}
237-
if (res > 0) {
238-
PyObject* locals = PyFrame_GetLocals(frame);
239-
if (!locals) {
240-
goto error;
241-
}
242-
PyObject* self = PyDict_GetItemWithError(locals, &_Py_ID(self)); /* borrowed */
243-
if (!self) {
244-
Py_DECREF(locals);
245-
goto error;
246-
}
247-
248-
res = PyObject_HasAttrWithError(self, name);
249-
Py_DECREF(locals);
250-
if (res < 0) {
251-
goto error;
252-
}
253-
if (res) {
254-
Py_DECREF(dir);
255-
return PyUnicode_FromFormat("self.%U", name);
256-
}
257-
}
258-
259-
PyObject *suggestions = _Py_CalculateSuggestions(dir, name);
260-
Py_DECREF(dir);
261-
if (suggestions != NULL || PyErr_Occurred()) {
262-
return suggestions;
263-
}
264-
265-
dir = PySequence_List(frame->f_frame->f_globals);
266-
if (dir == NULL) {
267-
return NULL;
268-
}
269-
suggestions = _Py_CalculateSuggestions(dir, name);
270-
Py_DECREF(dir);
271-
if (suggestions != NULL || PyErr_Occurred()) {
272-
return suggestions;
273-
}
274-
275-
dir = PySequence_List(frame->f_frame->f_builtins);
276-
if (dir == NULL) {
277-
return NULL;
278-
}
279-
suggestions = _Py_CalculateSuggestions(dir, name);
280-
Py_DECREF(dir);
281-
282-
return suggestions;
283-
284-
error:
285-
Py_DECREF(dir);
286-
return NULL;
287-
}
288-
289-
static bool
290-
is_name_stdlib_module(PyObject* name)
291-
{
292-
const char* the_name = PyUnicode_AsUTF8(name);
293-
Py_ssize_t len = Py_ARRAY_LENGTH(_Py_stdlib_module_names);
294-
for (Py_ssize_t i = 0; i < len; i++) {
295-
if (strcmp(the_name, _Py_stdlib_module_names[i]) == 0) {
296-
return 1;
297-
}
298-
}
299-
return 0;
300-
}
301-
302-
static PyObject *
303-
offer_suggestions_for_name_error(PyNameErrorObject *exc)
304-
{
305-
PyObject *name = exc->name; // borrowed reference
306-
PyTracebackObject *traceback = (PyTracebackObject *) exc->traceback; // borrowed reference
307-
// Abort if we don't have a variable name or we have an invalid one
308-
// or if we don't have a traceback to work with
309-
if (name == NULL || !PyUnicode_CheckExact(name) ||
310-
traceback == NULL || !Py_IS_TYPE(traceback, &PyTraceBack_Type)
311-
) {
312-
return NULL;
313-
}
314-
315-
// Move to the traceback of the exception
316-
while (1) {
317-
PyTracebackObject *next = traceback->tb_next;
318-
if (next == NULL || !Py_IS_TYPE(next, &PyTraceBack_Type)) {
319-
break;
320-
}
321-
else {
322-
traceback = next;
323-
}
324-
}
325-
326-
PyFrameObject *frame = traceback->tb_frame;
327-
assert(frame != NULL);
328-
329-
PyObject* suggestion = get_suggestions_for_name_error(name, frame);
330-
if (suggestion == NULL && PyErr_Occurred()) {
331-
return NULL;
332-
}
333-
334-
// Add a trailer ". Did you mean: (...)?"
335-
PyObject* result = NULL;
336-
if (!is_name_stdlib_module(name)) {
337-
if (suggestion == NULL) {
338-
return NULL;
339-
}
340-
result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
341-
} else if (suggestion == NULL) {
342-
result = PyUnicode_FromFormat(". Did you forget to import %R?", name);
343-
} else {
344-
result = PyUnicode_FromFormat(". Did you mean: %R? Or did you forget to import %R?", suggestion, name);
345-
}
346-
Py_XDECREF(suggestion);
347-
return result;
348-
}
349-
350-
static PyObject *
351-
offer_suggestions_for_import_error(PyImportErrorObject *exc)
352-
{
353-
PyObject *mod_name = exc->name; // borrowed reference
354-
PyObject *name = exc->name_from; // borrowed reference
355-
if (name == NULL || mod_name == NULL || name == Py_None ||
356-
!PyUnicode_CheckExact(name) || !PyUnicode_CheckExact(mod_name)) {
357-
return NULL;
358-
}
359-
360-
PyObject* mod = PyImport_GetModule(mod_name);
361-
if (mod == NULL) {
362-
return NULL;
363-
}
364-
365-
PyObject *dir = PyObject_Dir(mod);
366-
Py_DECREF(mod);
367-
if (dir == NULL) {
368-
return NULL;
369-
}
370-
371-
PyObject *suggestion = _Py_CalculateSuggestions(dir, name);
372-
Py_DECREF(dir);
373-
if (!suggestion) {
374-
return NULL;
375-
}
376-
377-
PyObject* result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
378-
Py_DECREF(suggestion);
379-
return result;
380-
}
381-
382-
// Offer suggestions for a given exception. Returns a python string object containing the
383-
// suggestions. This function returns NULL if no suggestion was found or if an exception happened,
384-
// users must call PyErr_Occurred() to disambiguate.
385-
PyObject *
386-
_Py_Offer_Suggestions(PyObject *exception)
387-
{
388-
PyObject *result = NULL;
389-
assert(!PyErr_Occurred());
390-
if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_AttributeError)) {
391-
result = offer_suggestions_for_attribute_error((PyAttributeErrorObject *) exception);
392-
} else if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_NameError)) {
393-
result = offer_suggestions_for_name_error((PyNameErrorObject *) exception);
394-
} else if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_ImportError)) {
395-
result = offer_suggestions_for_import_error((PyImportErrorObject *) exception);
396-
}
397-
return result;
398-
}
399-
400181
Py_ssize_t
401182
_Py_UTF8_Edit_Cost(PyObject *a, PyObject *b, Py_ssize_t max_cost)
402183
{

0 commit comments

Comments
 (0)