22
33import collections .abc
44import functools
5+ import heapq
56import itertools
67import linecache
78import os
1920
2021from contextlib import suppress
2122lazy import _colorize
23+ lazy import difflib
2224
2325try :
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 = []
0 commit comments