From 21e81800855f3df0b61d410e6801746ec80a08f7 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sat, 6 Dec 2025 00:13:27 +0000 Subject: [PATCH 1/2] gh-142236: Fix incorrect keyword suggestions for syntax errors The keyword typo suggestion mechanism in traceback would incorrectly suggest replacements when the extracted source code was merely incomplete rather than containing an actual typo. For example, when a missing comma caused a syntax error, the system would suggest replacing 'print' with 'not' because the incomplete code snippet happened to pass validation. The fix adds a validation step that first checks whether the original extracted code raises a SyntaxError. If the code compiles successfully or is simply incomplete (compile_command returns None), the function returns early since there is no way to verify that a keyword replacement would actually fix the problem. --- Lib/test/test_traceback.py | 17 +++++++++++++++++ Lib/traceback.py | 9 +++++++++ ...25-12-06-00-16-43.gh-issue-142236.m3EF9E.rst | 4 ++++ 3 files changed, 30 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-00-16-43.gh-issue-142236.m3EF9E.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 3876f1a74bbc1a..d107ad925941fe 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -1784,6 +1784,23 @@ def test_keyword_suggestions_from_command_string(self): stderr_text = stderr.decode('utf-8') self.assertIn(f"Did you mean '{expected_kw}'", stderr_text) + def test_no_keyword_suggestion_for_comma_errors(self): + # When the parser identifies a missing comma, don't suggest + # bogus keyword replacements like 'print' -> 'not' + code = '''\ +import sys +print( + "line1" + "line2" + file=sys.stderr +) +''' + source = textwrap.dedent(code).strip() + rc, stdout, stderr = assert_python_failure('-c', source) + stderr_text = stderr.decode('utf-8') + self.assertIn("Perhaps you forgot a comma", stderr_text) + self.assertNotIn("Did you mean", stderr_text) + @requires_debug_ranges() @force_not_colorized_test_class class PurePythonTracebackErrorCaretTests( diff --git a/Lib/traceback.py b/Lib/traceback.py index 8a3e0f77e765dc..944eb1e01a2e75 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1340,6 +1340,15 @@ def _find_keyword_typos(self): if len(error_code) > 1024: return + # If the original code doesn't raise SyntaxError, we can't validate + # that a keyword replacement actually fixes anything + try: + codeop.compile_command(error_code, symbol="exec") + except SyntaxError: + pass # Good - the original code has a syntax error we might fix + else: + return # Original code compiles or is incomplete - can't validate fixes + error_lines = error_code.splitlines() tokens = tokenize.generate_tokens(io.StringIO(error_code).readline) tokens_left_to_process = 10 diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-00-16-43.gh-issue-142236.m3EF9E.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-00-16-43.gh-issue-142236.m3EF9E.rst new file mode 100644 index 00000000000000..b5c6a27fd6a259 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-06-00-16-43.gh-issue-142236.m3EF9E.rst @@ -0,0 +1,4 @@ +Fix incorrect keyword suggestions for syntax errors in :mod:`traceback`. The +keyword typo suggestion mechanism would incorrectly suggest replacements when +the extracted source code was incomplete rather than containing an actual typo. +Patch by Pablo Galindo. From b74eedc778f66a826be31689109c0d061d2b259b Mon Sep 17 00:00:00 2001 From: Pablo Galindo Salgado Date: Sat, 6 Dec 2025 14:50:27 +0000 Subject: [PATCH 2/2] Update Lib/traceback.py --- Lib/traceback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/traceback.py b/Lib/traceback.py index 944eb1e01a2e75..c1052adeed25a1 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1343,7 +1343,7 @@ def _find_keyword_typos(self): # If the original code doesn't raise SyntaxError, we can't validate # that a keyword replacement actually fixes anything try: - codeop.compile_command(error_code, symbol="exec") + codeop.compile_command(error_code, symbol="exec", flags=codeop.PyCF_ONLY_AST) except SyntaxError: pass # Good - the original code has a syntax error we might fix else: