mirror of
https://github.com/python/cpython
synced 2024-09-15 22:26:46 +00:00
gh-110721: Remove unused code from suggestions.c after moving PyErr_Display to use the traceback module (#113712)
This commit is contained in:
parent
802d4954f1
commit
a03ec20bcd
|
@ -1164,7 +1164,6 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seek));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seek));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seekable));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seekable));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(selectors));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(selectors));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(self));
|
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(send));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(send));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sep));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sep));
|
||||||
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sequence));
|
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(sequence));
|
||||||
|
|
|
@ -653,7 +653,6 @@ struct _Py_global_strings {
|
||||||
STRUCT_FOR_ID(seek)
|
STRUCT_FOR_ID(seek)
|
||||||
STRUCT_FOR_ID(seekable)
|
STRUCT_FOR_ID(seekable)
|
||||||
STRUCT_FOR_ID(selectors)
|
STRUCT_FOR_ID(selectors)
|
||||||
STRUCT_FOR_ID(self)
|
|
||||||
STRUCT_FOR_ID(send)
|
STRUCT_FOR_ID(send)
|
||||||
STRUCT_FOR_ID(sep)
|
STRUCT_FOR_ID(sep)
|
||||||
STRUCT_FOR_ID(sequence)
|
STRUCT_FOR_ID(sequence)
|
||||||
|
|
1
Include/internal/pycore_runtime_init_generated.h
generated
1
Include/internal/pycore_runtime_init_generated.h
generated
|
@ -1162,7 +1162,6 @@ extern "C" {
|
||||||
INIT_ID(seek), \
|
INIT_ID(seek), \
|
||||||
INIT_ID(seekable), \
|
INIT_ID(seekable), \
|
||||||
INIT_ID(selectors), \
|
INIT_ID(selectors), \
|
||||||
INIT_ID(self), \
|
|
||||||
INIT_ID(send), \
|
INIT_ID(send), \
|
||||||
INIT_ID(sep), \
|
INIT_ID(sep), \
|
||||||
INIT_ID(sequence), \
|
INIT_ID(sequence), \
|
||||||
|
|
|
@ -1800,9 +1800,6 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||||
string = &_Py_ID(selectors);
|
string = &_Py_ID(selectors);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
string = &_Py_ID(self);
|
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
|
||||||
string = &_Py_ID(send);
|
string = &_Py_ID(send);
|
||||||
assert(_PyUnicode_CheckConsistency(string, 1));
|
assert(_PyUnicode_CheckConsistency(string, 1));
|
||||||
_PyUnicode_InternInPlace(interp, &string);
|
_PyUnicode_InternInPlace(interp, &string);
|
||||||
|
|
|
@ -1497,6 +1497,13 @@ def _compute_suggestion_error(exc_value, tb, wrong_name):
|
||||||
if hasattr(self, wrong_name):
|
if hasattr(self, wrong_name):
|
||||||
return f"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
|
# Compute closest match
|
||||||
|
|
||||||
if len(d) > _MAX_CANDIDATE_ITEMS:
|
if len(d) > _MAX_CANDIDATE_ITEMS:
|
||||||
|
|
|
@ -11,6 +11,7 @@ faulthandler faulthandler.c
|
||||||
posix posixmodule.c
|
posix posixmodule.c
|
||||||
_signal signalmodule.c
|
_signal signalmodule.c
|
||||||
_tracemalloc _tracemalloc.c
|
_tracemalloc _tracemalloc.c
|
||||||
|
_suggestions _suggestions.c
|
||||||
|
|
||||||
# modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
|
# modules used by importlib, deepfreeze, freeze, runpy, and sysconfig
|
||||||
_codecs _codecsmodule.c
|
_codecs _codecsmodule.c
|
||||||
|
|
63
Modules/_suggestions.c
Normal file
63
Modules/_suggestions.c
Normal 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
41
Modules/clinic/_suggestions.c.h
generated
Normal 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]*/
|
|
@ -424,6 +424,7 @@
|
||||||
<ClInclude Include="..\Modules\_sre\sre_lib.h" />
|
<ClInclude Include="..\Modules\_sre\sre_lib.h" />
|
||||||
<ClCompile Include="..\Modules\_stat.c" />
|
<ClCompile Include="..\Modules\_stat.c" />
|
||||||
<ClCompile Include="..\Modules\_struct.c" />
|
<ClCompile Include="..\Modules\_struct.c" />
|
||||||
|
<ClCompile Include="..\Modules\_suggestions.c" />
|
||||||
<ClCompile Include="..\Modules\_weakref.c" />
|
<ClCompile Include="..\Modules\_weakref.c" />
|
||||||
<ClCompile Include="..\Modules\arraymodule.c" />
|
<ClCompile Include="..\Modules\arraymodule.c" />
|
||||||
<ClCompile Include="..\Modules\atexitmodule.c" />
|
<ClCompile Include="..\Modules\atexitmodule.c" />
|
||||||
|
|
|
@ -932,6 +932,9 @@
|
||||||
<ClCompile Include="..\Modules\_struct.c">
|
<ClCompile Include="..\Modules\_struct.c">
|
||||||
<Filter>Modules</Filter>
|
<Filter>Modules</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\Modules\_suggestions.c">
|
||||||
|
<Filter>Modules</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\Modules\_weakref.c">
|
<ClCompile Include="..\Modules\_weakref.c">
|
||||||
<Filter>Modules</Filter>
|
<Filter>Modules</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|
1
Python/stdlib_module_names.h
generated
1
Python/stdlib_module_names.h
generated
|
@ -76,6 +76,7 @@ static const char* _Py_stdlib_module_names[] = {
|
||||||
"_string",
|
"_string",
|
||||||
"_strptime",
|
"_strptime",
|
||||||
"_struct",
|
"_struct",
|
||||||
|
"_suggestions",
|
||||||
"_symtable",
|
"_symtable",
|
||||||
"_sysconfig",
|
"_sysconfig",
|
||||||
"_thread",
|
"_thread",
|
||||||
|
|
|
@ -178,225 +178,6 @@ _Py_CalculateSuggestions(PyObject *dir,
|
||||||
return Py_XNewRef(suggestion);
|
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_ssize_t
|
||||||
_Py_UTF8_Edit_Cost(PyObject *a, PyObject *b, Py_ssize_t max_cost)
|
_Py_UTF8_Edit_Cost(PyObject *a, PyObject *b, Py_ssize_t max_cost)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue