Skip to content

Commit b9c5baa

Browse files
committed
Rank tokens before probing for a keyword typo
1 parent 9768834 commit b9c5baa

3 files changed

Lines changed: 26 additions & 18 deletions

File tree

Lib/test/test_traceback.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,7 @@ class TestKeywordTypoSuggestions(unittest.TestCase):
18271827
("function f():", "def"),
18281828
("func f():", "def"),
18291829
("void f():", "def"),
1830+
(f"{"a="*10}0;tpye x = int;{"z="*10}1", "type"),
18301831
]
18311832

18321833
def test_keyword_suggestions_from_file(self):

Lib/traceback.py

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import collections.abc
44
import functools
5+
import heapq
56
import itertools
67
import linecache
78
import os
@@ -19,6 +20,7 @@
1920

2021
from contextlib import suppress
2122
lazy import _colorize
23+
lazy import difflib
2224

2325
try:
2426
from _missing_stdlib_info import _MISSING_STDLIB_MODULE_MESSAGES
@@ -1427,20 +1429,17 @@ def _find_keyword_typos(self):
14271429
if not self._exc_metadata:
14281430
return
14291431

1430-
line, offset, source = self._exc_metadata
1432+
line, _, source = self._exc_metadata
14311433
end_line = int(self.lineno) if self.lineno is not None else 0
14321434
lines = None
1433-
from_filename = False
14341435

14351436
if source is None:
14361437
if self.filename:
14371438
try:
14381439
with open(self.filename) as f:
14391440
lines = f.read().splitlines()
14401441
except Exception:
1441-
line, end_line, offset = 0,1,0
1442-
else:
1443-
from_filename = True
1442+
line, end_line, _ = 0,1,0
14441443
lines = lines if lines is not None else self.text.splitlines()
14451444
else:
14461445
lines = source.splitlines()
@@ -1462,26 +1461,32 @@ def _find_keyword_typos(self):
14621461
return # Original code compiles or is incomplete - can't validate fixes
14631462

14641463
error_lines = error_code.splitlines()
1465-
tokens = tokenize.generate_tokens(io.StringIO(error_code).readline)
1464+
tokens = []
1465+
offset = self.end_offset
1466+
try:
1467+
for token in tokenize.generate_tokens(io.StringIO(error_code).readline):
1468+
if token.type != tokenize.NAME:
1469+
continue
1470+
# Only consider NAME tokens on the same line as the error
1471+
the_end = end_line if line == 0 else end_line + 1
1472+
if token.start[0] + line != the_end:
1473+
continue
1474+
if keyword.iskeyword(token.string):
1475+
continue
1476+
rank = abs(offset - token.end[1])
1477+
heapq.heappush(tokens, (rank, token))
1478+
except Exception:
1479+
pass
14661480
tokens_left_to_process = 10
1467-
import difflib
1468-
for token in tokens:
1469-
start, end = token.start, token.end
1470-
if token.type != tokenize.NAME:
1471-
continue
1472-
# Only consider NAME tokens on the same line as the error
1473-
the_end = end_line if line == 0 else end_line + 1
1474-
if from_filename and token.start[0]+line != the_end:
1475-
continue
1481+
while tokens:
1482+
rank, token = heapq.heappop(tokens)
14761483
wrong_name = token.string
1477-
if wrong_name in keyword.kwlist:
1478-
continue
1479-
14801484
# Limit the number of valid tokens to consider to not spend
14811485
# to much time in this function
14821486
tokens_left_to_process -= 1
14831487
if tokens_left_to_process < 0:
14841488
break
1489+
start, end = token.start, token.end
14851490
# Limit the number of possible matches to try
14861491
max_matches = 3
14871492
matches = []
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When looking for possibly misspelled Python keywords after a :exc:`SyntaxError`,
2+
candidate names are now ranked to improve accuracy. Patch by Bartosz Sławecki.

0 commit comments

Comments
 (0)