From a03ec20bcdf757138557127689405b3a525b3c44 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Mon, 8 Jan 2024 15:10:45 +0000 Subject: [PATCH] gh-110721: Remove unused code from suggestions.c after moving PyErr_Display to use the traceback module (#113712) --- .../pycore_global_objects_fini_generated.h | 1 - Include/internal/pycore_global_strings.h | 1 - .../internal/pycore_runtime_init_generated.h | 1 - .../internal/pycore_unicodeobject_generated.h | 3 - Lib/traceback.py | 7 + Modules/Setup.bootstrap.in | 1 + Modules/_suggestions.c | 63 +++++ Modules/clinic/_suggestions.c.h | 41 ++++ PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 + Python/stdlib_module_names.h | 1 + Python/suggestions.c | 219 ------------------ 12 files changed, 117 insertions(+), 225 deletions(-) create mode 100644 Modules/_suggestions.c create mode 100644 Modules/clinic/_suggestions.c.h diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 89ec8cbbbcd..8dbdc399d77 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -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)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 62c3ee3ae2a..1fa2d1d49cb 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -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) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 1defa39f816..8fd922af265 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -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), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index be9baa3eebe..0b5fb7363b4 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -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); diff --git a/Lib/traceback.py b/Lib/traceback.py index 1cf008c7e9d..30b42a4f693 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -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: diff --git a/Modules/Setup.bootstrap.in b/Modules/Setup.bootstrap.in index cd12c1bd0df..aa4e60e2726 100644 --- a/Modules/Setup.bootstrap.in +++ b/Modules/Setup.bootstrap.in @@ -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 diff --git a/Modules/_suggestions.c b/Modules/_suggestions.c new file mode 100644 index 00000000000..30b524d70c1 --- /dev/null +++ b/Modules/_suggestions.c @@ -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); +} + diff --git a/Modules/clinic/_suggestions.c.h b/Modules/clinic/_suggestions.c.h new file mode 100644 index 00000000000..51484b13d5a --- /dev/null +++ b/Modules/clinic/_suggestions.c.h @@ -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]*/ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 163adfdc51c..c599b68e0d8 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -424,6 +424,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index a45a0881f71..13582dff3f4 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -932,6 +932,9 @@ Modules + + Modules + Modules diff --git a/Python/stdlib_module_names.h b/Python/stdlib_module_names.h index 701bfc35cc8..2445a5c838a 100644 --- a/Python/stdlib_module_names.h +++ b/Python/stdlib_module_names.h @@ -76,6 +76,7 @@ static const char* _Py_stdlib_module_names[] = { "_string", "_strptime", "_struct", +"_suggestions", "_symtable", "_sysconfig", "_thread", diff --git a/Python/suggestions.c b/Python/suggestions.c index 1ad359b1892..69c3e4fe4d2 100644 --- a/Python/suggestions.c +++ b/Python/suggestions.c @@ -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) {