Skip to content

Commit 61d510b

Browse files
committed
Address Victor's feedback
1 parent c9c85b5 commit 61d510b

File tree

7 files changed

+34
-26
lines changed

7 files changed

+34
-26
lines changed

Doc/library/exceptions.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ The following exceptions are the exceptions that are usually raised.
149149
assignment fails. (When an object does not support attribute references or
150150
attribute assignments at all, :exc:`TypeError` is raised.)
151151

152+
The :attr:`name` and :attr:`obj` attributes can be set using keyword-only
153+
arguments to the constructor. When set they represent the name of the attribute
154+
that was attempted to be accessed and the object that was accessed for said
155+
attribute, respectively.
156+
152157

153158
.. exception:: EOFError
154159

Include/internal/pycore_suggestions.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
# error "this header requires Py_BUILD_CORE define"
88
#endif
99

10-
int _Py_offer_suggestions_for_attribute_error(PyAttributeErrorObject* exception_value);
10+
int _Py_offer_suggestions(PyObject* exception, PyObject* value);
1111

12-
13-
#endif /* !Py_INTERNAL_SUGGESTIONS_H */
12+
#endif /* !Py_INTERNAL_SUGGESTIONS_H */

Include/pyerrors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ PyAPI_FUNC(PyObject *) PyErr_NewExceptionWithDoc(
221221
const char *name, const char *doc, PyObject *base, PyObject *dict);
222222
PyAPI_FUNC(void) PyErr_WriteUnraisable(PyObject *);
223223

224+
224225
/* In signalmodule.c */
225226
PyAPI_FUNC(int) PyErr_CheckSignals(void);
226227
PyAPI_FUNC(void) PyErr_SetInterrupt(void);

Objects/exceptions.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,12 +1394,6 @@ AttributeError_traverse(PyAttributeErrorObject *self, visitproc visit, void *arg
13941394
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
13951395
}
13961396

1397-
static PyObject *
1398-
AttributeError_str(PyAttributeErrorObject *self)
1399-
{
1400-
return BaseException_str((PyBaseExceptionObject *)self);
1401-
}
1402-
14031397
static PyMemberDef AttributeError_members[] = {
14041398
{"name", T_OBJECT, offsetof(PyAttributeErrorObject, name), 0, PyDoc_STR("attribute name")},
14051399
{"obj", T_OBJECT, offsetof(PyAttributeErrorObject, obj), 0, PyDoc_STR("object")},
@@ -1413,7 +1407,7 @@ static PyMethodDef AttributeError_methods[] = {
14131407
ComplexExtendsException(PyExc_Exception, AttributeError,
14141408
AttributeError, 0,
14151409
AttributeError_methods, AttributeError_members,
1416-
0, AttributeError_str, "Attribute not found.");
1410+
0, BaseException_str, "Attribute not found.");
14171411

14181412
/*
14191413
* SyntaxError extends Exception

Objects/object.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ _PyObject_SetAttrId(PyObject *v, _Py_Identifier *name, PyObject *w)
886886
}
887887

888888
static inline int
889-
add_context_to_attribute_error_exception(PyObject* v, PyObject* name)
889+
set_attribute_error_context(PyObject* v, PyObject* name)
890890
{
891891
assert(PyErr_Occurred());
892892
// Intercept AttributeError exceptions and augment them to offer
@@ -917,8 +917,9 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
917917
Py_TYPE(name)->tp_name);
918918
return NULL;
919919
}
920-
if (tp->tp_getattro != NULL)
920+
if (tp->tp_getattro != NULL) {
921921
result = (*tp->tp_getattro)(v, name);
922+
}
922923
else if (tp->tp_getattr != NULL) {
923924
const char *name_str = PyUnicode_AsUTF8(name);
924925
if (name_str == NULL)
@@ -930,7 +931,7 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
930931
tp->tp_name, name);
931932
}
932933

933-
if (!result && add_context_to_attribute_error_exception(v, name)) {
934+
if (!result && set_attribute_error_context(v, name)) {
934935
return NULL;
935936
}
936937

@@ -1194,7 +1195,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
11941195
"'%.50s' object has no attribute '%U'",
11951196
tp->tp_name, name);
11961197

1197-
if (add_context_to_attribute_error_exception(obj, name)) {
1198+
if (set_attribute_error_context(obj, name)) {
11981199
return 0;
11991200
}
12001201

Python/pythonrun.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,9 +1082,8 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
10821082
return;
10831083
}
10841084

1085-
if (PyErr_GivenExceptionMatches(exception, PyExc_AttributeError) &&
1086-
_Py_offer_suggestions_for_attribute_error((PyAttributeErrorObject*) value) != 0) {
1087-
PyErr_Clear();
1085+
if (_Py_offer_suggestions(exception, value) != 0) {
1086+
return;
10881087
}
10891088

10901089
Py_INCREF(file);

Python/suggestions.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#define MAX_GETATTR_PREDICT_ITEMS 100
77
#define MAX_GETATTR_STRING_SIZE 20
88

9-
static size_t
9+
/* Calculate the Levenshtein distance between string1 and string2 */
10+
static Py_ssize_t
1011
distance(const char *string1, const char *string2)
1112
{
1213
Py_ssize_t len1 = strlen(string1);
@@ -137,7 +138,7 @@ calculate_suggestions(PyObject* dir,
137138
return NULL;
138139
}
139140

140-
int suggestion_distance = PyUnicode_GetLength(name);
141+
Py_ssize_t suggestion_distance = PyUnicode_GetLength(name);
141142
PyObject* suggestion = NULL;
142143
for (int i = 0; i < dir_size; ++i) {
143144
PyObject *item = PyList_GET_ITEM(dir, i);
@@ -146,25 +147,25 @@ calculate_suggestions(PyObject* dir,
146147
PyErr_Clear();
147148
continue;
148149
}
149-
int current_distance = distance(PyUnicode_AsUTF8(name),
150-
PyUnicode_AsUTF8(item));
150+
Py_ssize_t current_distance = distance(PyUnicode_AsUTF8(name),
151+
PyUnicode_AsUTF8(item));
151152
if (current_distance > MAX_GETATTR_PREDICT_DIST){
152153
continue;
153154
}
154155
if (!suggestion || current_distance < suggestion_distance) {
155156
suggestion = item;
156157
suggestion_distance = current_distance;
157-
}
158+
}
158159
}
159160
if (!suggestion) {
160161
return NULL;
161162
}
162-
return PyUnicode_FromFormat("%S\n\nDid you mean: %U?",
163+
return PyUnicode_FromFormat("%S. Did you mean: %U?",
163164
oldexceptionvalue, suggestion);
164165
}
165166

166-
int
167-
_Py_offer_suggestions_for_attribute_error(PyAttributeErrorObject* exc) {
167+
static int
168+
offer_suggestions_for_attribute_error(PyAttributeErrorObject* exc) {
168169
int return_val = 0;
169170

170171
PyObject* name = exc->name;
@@ -174,7 +175,7 @@ _Py_offer_suggestions_for_attribute_error(PyAttributeErrorObject* exc) {
174175
if ((name == NULL) || (v == NULL) || !PyUnicode_CheckExact(name)) {
175176
return -1;
176177
}
177-
178+
178179
PyObject* oldexceptionvalue = NULL;
179180
Py_ssize_t nargs = PyTuple_GET_SIZE(exc->args);
180181
switch (nargs) {
@@ -228,3 +229,11 @@ _Py_offer_suggestions_for_attribute_error(PyAttributeErrorObject* exc) {
228229
}
229230

230231

232+
int _Py_offer_suggestions(PyObject* exception, PyObject* value) {
233+
if (PyErr_GivenExceptionMatches(exception, PyExc_AttributeError) &&
234+
offer_suggestions_for_attribute_error((PyAttributeErrorObject*) value) != 0) {
235+
PyErr_Clear();
236+
}
237+
return 0;
238+
}
239+

0 commit comments

Comments
 (0)