gh-110721: Remove unused code from suggestions.c after moving PyErr_Display to use the traceback module (#113712)

This commit is contained in:
Pablo Galindo Salgado 2024-01-08 15:10:45 +00:00 committed by GitHub
parent 802d4954f1
commit a03ec20bcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 117 additions and 225 deletions

View File

@ -1164,7 +1164,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seek));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seekable));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(selectors));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(self));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(send));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sep));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sequence));

View File

@ -653,7 +653,6 @@ struct _Py_global_strings {
STRUCT_FOR_ID(seek)
STRUCT_FOR_ID(seekable)
STRUCT_FOR_ID(selectors)
STRUCT_FOR_ID(self)
STRUCT_FOR_ID(send)
STRUCT_FOR_ID(sep)
STRUCT_FOR_ID(sequence)

View File

@ -1162,7 +1162,6 @@ extern "C" {
INIT_ID(seek), \
INIT_ID(seekable), \
INIT_ID(selectors), \
INIT_ID(self), \
INIT_ID(send), \
INIT_ID(sep), \
INIT_ID(sequence), \

View File

@ -1800,9 +1800,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(selectors);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(self);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(send);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);

View File

@ -1497,6 +1497,13 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
if hasattr(self, wrong_name):
return f"self.{wrong_name}"
try:
import _suggestions
except ImportError:
pass
else:
return _suggestions._generate_suggestions(d, wrong_name)
# Compute closest match
if len(d) > _MAX_CANDIDATE_ITEMS:

View File

@ -11,6 +11,7 @@ faulthandler faulthandler.c
posix posixmodule.c
_signal signalmodule.c
_tracemalloc _tracemalloc.c
_suggestions _suggestions.c
# modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
_codecs _codecsmodule.c

63
Modules/_suggestions.c Normal file
View File

@ -0,0 +1,63 @@
#include "Python.h"
#include "pycore_pyerrors.h"
#include "clinic/_suggestions.c.h"
/*[clinic input]
module _suggestions
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e58d81fafad5637b]*/
/*[clinic input]
_suggestions._generate_suggestions
candidates: object
item: unicode
/
Returns the candidate in candidates that's closest to item
[clinic start generated code]*/
static PyObject *
_suggestions__generate_suggestions_impl(PyObject *module,
PyObject *candidates, PyObject *item)
/*[clinic end generated code: output=79be7b653ae5e7ca input=ba2a8dddc654e33a]*/
{
// Check if dir is a list
if (!PyList_Check(candidates)) {
PyErr_SetString(PyExc_TypeError, "candidates must be a list");
return NULL;
}
// Check if all elements in the list are Unicode
Py_ssize_t size = PyList_Size(candidates);
for (Py_ssize_t i = 0; i < size; ++i) {
PyObject *elem = PyList_GetItem(candidates, i);
if (!PyUnicode_Check(elem)) {
PyErr_SetString(PyExc_TypeError, "all elements in 'candidates' must be strings");
return NULL;
}
}
PyObject* result = _Py_CalculateSuggestions(candidates, item);
if (!result && !PyErr_Occurred()) {
Py_RETURN_NONE;
}
return result;
}
static PyMethodDef module_methods[] = {
_SUGGESTIONS__GENERATE_SUGGESTIONS_METHODDEF
{NULL, NULL, 0, NULL} // Sentinel
};
static struct PyModuleDef suggestions_module = {
PyModuleDef_HEAD_INIT,
"_suggestions",
NULL,
-1,
module_methods
};
PyMODINIT_FUNC PyInit__suggestions(void) {
return PyModule_Create(&suggestions_module);
}

41
Modules/clinic/_suggestions.c.h generated Normal file
View File

@ -0,0 +1,41 @@
/*[clinic input]
preserve
[clinic start generated code]*/
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(_suggestions__generate_suggestions__doc__,
"_generate_suggestions($module, candidates, item, /)\n"
"--\n"
"\n"
"Returns the candidate in candidates that\'s closest to item");
#define _SUGGESTIONS__GENERATE_SUGGESTIONS_METHODDEF \
{"_generate_suggestions", _PyCFunction_CAST(_suggestions__generate_suggestions), METH_FASTCALL, _suggestions__generate_suggestions__doc__},
static PyObject *
_suggestions__generate_suggestions_impl(PyObject *module,
PyObject *candidates, PyObject *item);
static PyObject *
_suggestions__generate_suggestions(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *candidates;
PyObject *item;
if (!_PyArg_CheckPositional("_generate_suggestions", nargs, 2, 2)) {
goto exit;
}
candidates = args[0];
if (!PyUnicode_Check(args[1])) {
_PyArg_BadArgument("_generate_suggestions", "argument 2", "str", args[1]);
goto exit;
}
item = args[1];
return_value = _suggestions__generate_suggestions_impl(module, candidates, item);
exit:
return return_value;
}
/*[clinic end generated code: output=1d8e963cdae30b13 input=a9049054013a1b77]*/

View File

@ -424,6 +424,7 @@
<ClInclude Include="..\Modules\_sre\sre_lib.h" />
<ClCompile Include="..\Modules\_stat.c" />
<ClCompile Include="..\Modules\_struct.c" />
<ClCompile Include="..\Modules\_suggestions.c" />
<ClCompile Include="..\Modules\_weakref.c" />
<ClCompile Include="..\Modules\arraymodule.c" />
<ClCompile Include="..\Modules\atexitmodule.c" />

View File

@ -932,6 +932,9 @@
<ClCompile Include="..\Modules\_struct.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_suggestions.c">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="..\Modules\_weakref.c">
<Filter>Modules</Filter>
</ClCompile>

View File

@ -76,6 +76,7 @@ static const char* _Py_stdlib_module_names[] = {
"_string",
"_strptime",
"_struct",
"_suggestions",
"_symtable",
"_sysconfig",
"_thread",

View File

@ -178,225 +178,6 @@ _Py_CalculateSuggestions(PyObject *dir,
return Py_XNewRef(suggestion);
}
static PyObject *
get_suggestions_for_attribute_error(PyAttributeErrorObject *exc)
{
PyObject *name = exc->name; // borrowed reference
PyObject *obj = exc->obj; // borrowed reference
// Abort if we don't have an attribute name or we have an invalid one
if (name == NULL || obj == NULL || !PyUnicode_CheckExact(name)) {
return NULL;
}
PyObject *dir = PyObject_Dir(obj);
if (dir == NULL) {
return NULL;
}
PyObject *suggestions = _Py_CalculateSuggestions(dir, name);
Py_DECREF(dir);
return suggestions;
}
static PyObject *
offer_suggestions_for_attribute_error(PyAttributeErrorObject *exc)
{
PyObject* suggestion = get_suggestions_for_attribute_error(exc);
if (suggestion == NULL) {
return NULL;
}
// Add a trailer ". Did you mean: (...)?"
PyObject* result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
Py_DECREF(suggestion);
return result;
}
static PyObject *
get_suggestions_for_name_error(PyObject* name, PyFrameObject* frame)
{
PyCodeObject *code = PyFrame_GetCode(frame);
assert(code != NULL && code->co_localsplusnames != NULL);
PyObject *varnames = _PyCode_GetVarnames(code);
Py_DECREF(code);
if (varnames == NULL) {
return NULL;
}
PyObject *dir = PySequence_List(varnames);
Py_DECREF(varnames);
if (dir == NULL) {
return NULL;
}
// Are we inside a method and the instance has an attribute called 'name'?
int res = PySequence_Contains(dir, &_Py_ID(self));
if (res < 0) {
goto error;
}
if (res > 0) {
PyObject* locals = PyFrame_GetLocals(frame);
if (!locals) {
goto error;
}
PyObject* self = PyDict_GetItemWithError(locals, &_Py_ID(self)); /* borrowed */
if (!self) {
Py_DECREF(locals);
goto error;
}
res = PyObject_HasAttrWithError(self, name);
Py_DECREF(locals);
if (res < 0) {
goto error;
}
if (res) {
Py_DECREF(dir);
return PyUnicode_FromFormat("self.%U", name);
}
}
PyObject *suggestions = _Py_CalculateSuggestions(dir, name);
Py_DECREF(dir);
if (suggestions != NULL || PyErr_Occurred()) {
return suggestions;
}
dir = PySequence_List(frame->f_frame->f_globals);
if (dir == NULL) {
return NULL;
}
suggestions = _Py_CalculateSuggestions(dir, name);
Py_DECREF(dir);
if (suggestions != NULL || PyErr_Occurred()) {
return suggestions;
}
dir = PySequence_List(frame->f_frame->f_builtins);
if (dir == NULL) {
return NULL;
}
suggestions = _Py_CalculateSuggestions(dir, name);
Py_DECREF(dir);
return suggestions;
error:
Py_DECREF(dir);
return NULL;
}
static bool
is_name_stdlib_module(PyObject* name)
{
const char* the_name = PyUnicode_AsUTF8(name);
Py_ssize_t len = Py_ARRAY_LENGTH(_Py_stdlib_module_names);
for (Py_ssize_t i = 0; i < len; i++) {
if (strcmp(the_name, _Py_stdlib_module_names[i]) == 0) {
return 1;
}
}
return 0;
}
static PyObject *
offer_suggestions_for_name_error(PyNameErrorObject *exc)
{
PyObject *name = exc->name; // borrowed reference
PyTracebackObject *traceback = (PyTracebackObject *) exc->traceback; // borrowed reference
// Abort if we don't have a variable name or we have an invalid one
// or if we don't have a traceback to work with
if (name == NULL || !PyUnicode_CheckExact(name) ||
traceback == NULL || !Py_IS_TYPE(traceback, &PyTraceBack_Type)
) {
return NULL;
}
// Move to the traceback of the exception
while (1) {
PyTracebackObject *next = traceback->tb_next;
if (next == NULL || !Py_IS_TYPE(next, &PyTraceBack_Type)) {
break;
}
else {
traceback = next;
}
}
PyFrameObject *frame = traceback->tb_frame;
assert(frame != NULL);
PyObject* suggestion = get_suggestions_for_name_error(name, frame);
if (suggestion == NULL && PyErr_Occurred()) {
return NULL;
}
// Add a trailer ". Did you mean: (...)?"
PyObject* result = NULL;
if (!is_name_stdlib_module(name)) {
if (suggestion == NULL) {
return NULL;
}
result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
} else if (suggestion == NULL) {
result = PyUnicode_FromFormat(". Did you forget to import %R?", name);
} else {
result = PyUnicode_FromFormat(". Did you mean: %R? Or did you forget to import %R?", suggestion, name);
}
Py_XDECREF(suggestion);
return result;
}
static PyObject *
offer_suggestions_for_import_error(PyImportErrorObject *exc)
{
PyObject *mod_name = exc->name; // borrowed reference
PyObject *name = exc->name_from; // borrowed reference
if (name == NULL || mod_name == NULL || name == Py_None ||
!PyUnicode_CheckExact(name) || !PyUnicode_CheckExact(mod_name)) {
return NULL;
}
PyObject* mod = PyImport_GetModule(mod_name);
if (mod == NULL) {
return NULL;
}
PyObject *dir = PyObject_Dir(mod);
Py_DECREF(mod);
if (dir == NULL) {
return NULL;
}
PyObject *suggestion = _Py_CalculateSuggestions(dir, name);
Py_DECREF(dir);
if (!suggestion) {
return NULL;
}
PyObject* result = PyUnicode_FromFormat(". Did you mean: %R?", suggestion);
Py_DECREF(suggestion);
return result;
}
// Offer suggestions for a given exception. Returns a python string object containing the
// suggestions. This function returns NULL if no suggestion was found or if an exception happened,
// users must call PyErr_Occurred() to disambiguate.
PyObject *
_Py_Offer_Suggestions(PyObject *exception)
{
PyObject *result = NULL;
assert(!PyErr_Occurred());
if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_AttributeError)) {
result = offer_suggestions_for_attribute_error((PyAttributeErrorObject *) exception);
} else if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_NameError)) {
result = offer_suggestions_for_name_error((PyNameErrorObject *) exception);
} else if (Py_IS_TYPE(exception, (PyTypeObject*)PyExc_ImportError)) {
result = offer_suggestions_for_import_error((PyImportErrorObject *) exception);
}
return result;
}
Py_ssize_t
_Py_UTF8_Edit_Cost(PyObject *a, PyObject *b, Py_ssize_t max_cost)
{