Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
bpo-38530: Offer suggestions on NameError#25397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -187,6 +187,23 @@ raised from: | ||
| (Contributed by Pablo Galindo in :issue:`38530`.) | ||
| NameErrors | ||
| ~~~~~~~~~~ | ||
| When printing :exc:`NameError` raised by the interpreter, :c:func:`PyErr_Display` | ||
| will offer suggestions of simmilar variable names in the function that the exception | ||
pablogsal marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| was raised from: | ||
| .. code-block:: python | ||
| >>> schwarzschild_black_hole = None | ||
| >>> schwarschild_black_hole | ||
| Traceback (most recent call last): | ||
| File "<stdin>", line 1, in <module> | ||
| NameError: name 'schwarschild_black_hole' is not defined. Did you mean: schwarzschild_black_hole? | ||
| (Contributed by Pablo Galindo in :issue:`38530`.) | ||
| PEP 626: Precise line numbers for debugging and other tools | ||
| ----------------------------------------------------------- | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1413,6 +1413,129 @@ class TestException(MemoryError): | ||
| gc_collect() | ||
| global_for_suggestions = None | ||
| class NameErrorTests(unittest.TestCase): | ||
| def test_name_error_has_name(self): | ||
| try: | ||
| bluch | ||
| except NameError as exc: | ||
| self.assertEqual("bluch", exc.name) | ||
| def test_name_error_suggestions(self): | ||
| def Substitution(): | ||
| noise = more_noise = a = bc = None | ||
| blech = None | ||
| print(bluch) | ||
| def Elimination(): | ||
| noise = more_noise = a = bc = None | ||
| blch = None | ||
| print(bluch) | ||
| def Addition(): | ||
| noise = more_noise = a = bc = None | ||
| bluchin = None | ||
| print(bluch) | ||
| def SubstitutionOverElimination(): | ||
| blach = None | ||
| bluc = None | ||
| print(bluch) | ||
| def SubstitutionOverAddition(): | ||
| blach = None | ||
| bluchi = None | ||
| print(bluch) | ||
| def EliminationOverAddition(): | ||
| blucha = None | ||
| bluc = None | ||
| print(bluch) | ||
| for func, suggestion in [(Substitution, "blech?"), | ||
| (Elimination, "blch?"), | ||
| (Addition, "bluchin?"), | ||
| (EliminationOverAddition, "blucha?"), | ||
| (SubstitutionOverElimination, "blach?"), | ||
| (SubstitutionOverAddition, "blach?")]: | ||
| err = None | ||
| try: | ||
| func() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertIn(suggestion, err.getvalue()) | ||
| def test_name_error_suggestions_from_globals(self): | ||
| def func(): | ||
| print(global_for_suggestio) | ||
| try: | ||
| func() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertIn("global_for_suggestions?", err.getvalue()) | ||
| def test_name_error_suggestions_do_not_trigger_for_long_names(self): | ||
| def f(): | ||
| somethingverywronghehehehehehe = None | ||
| print(somethingverywronghe) | ||
| try: | ||
| f() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertNotIn("somethingverywronghehe", err.getvalue()) | ||
isidentical marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def test_name_error_suggestions_do_not_trigger_for_big_dicts(self): | ||
| def f(): | ||
| # Mutating locals() is unreliable, so we need to do it by hand | ||
pablogsal marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = a10 = a11 = a12 = a13 = \ | ||
| a14 = a15 = a16 = a17 = a18 = a19 = a20 = a21 = a22 = a23 = a24 = a25 = \ | ||
| a26 = a27 = a28 = a29 = a30 = a31 = a32 = a33 = a34 = a35 = a36 = a37 = \ | ||
| a38 = a39 = a40 = a41 = a42 = a43 = a44 = a45 = a46 = a47 = a48 = a49 = \ | ||
| a50 = a51 = a52 = a53 = a54 = a55 = a56 = a57 = a58 = a59 = a60 = a61 = \ | ||
| a62 = a63 = a64 = a65 = a66 = a67 = a68 = a69 = a70 = a71 = a72 = a73 = \ | ||
| a74 = a75 = a76 = a77 = a78 = a79 = a80 = a81 = a82 = a83 = a84 = a85 = \ | ||
| a86 = a87 = a88 = a89 = a90 = a91 = a92 = a93 = a94 = a95 = a96 = a97 = \ | ||
| a98 = a99 = a100 = a101 = a102 = a103 = None | ||
| print(a0) | ||
| try: | ||
| f() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertNotIn("a10", err.getvalue()) | ||
| def test_name_error_with_custom_exceptions(self): | ||
| def f(): | ||
| blech = None | ||
| raise NameError() | ||
| try: | ||
| f() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertNotIn("blech", err.getvalue()) | ||
| def f(): | ||
| blech = None | ||
| raise NameError | ||
| try: | ||
| f() | ||
| except NameError as exc: | ||
| with support.captured_stderr() as err: | ||
| sys.__excepthook__(*sys.exc_info()) | ||
| self.assertNotIn("blech", err.getvalue()) | ||
| class AttributeErrorTests(unittest.TestCase): | ||
| def test_attributes(self): | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| When printing :exc:`NameError` raised by the interpreter, | ||
| :c:func:`PyErr_Display` will offer suggestions of similar variable names in | ||
| the function that the exception was raised from. Patch by Pablo Galindo |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -6319,6 +6319,20 @@ format_exc_check_arg(PyThreadState *tstate, PyObject *exc, | ||
| return; | ||
| _PyErr_Format(tstate, exc, format_str, obj_str); | ||
| if (exc == PyExc_NameError) { | ||
| // Include the name in the NameError exceptions to offer suggestions later. | ||
| _Py_IDENTIFIER(name); | ||
| PyObject *type, *value, *traceback; | ||
| PyErr_Fetch(&type, &value, &traceback); | ||
| PyErr_NormalizeException(&type, &value, &traceback); | ||
pablogsal marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (PyErr_GivenExceptionMatches(value, PyExc_NameError)) { | ||
| // We do not care if this fails because we are going to restore the | ||
| // NameError anyway. | ||
| (void)_PyObject_SetAttrId(value, &PyId_name, obj); | ||
| } | ||
| PyErr_Restore(type, value, traceback); | ||
| } | ||
| } | ||
| static void | ||
Uh oh!
There was an error while loading. Please reload this page.