Skip to content
Merged
4 changes: 4 additions & 0 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,10 @@ class TestKeywordTypoSuggestions(unittest.TestCase):
("[x for x\nin range(3)\nof x]", "if"),
("[123 fur x\nin range(3)\nif x]", "for"),
("for x im n:\n pass", "in"),
("switch x:\n case:", "match"),
("delete x", "del"),
("function f():", "def"),
("func f():", "def"),
]

def test_keyword_suggestions_from_file(self):
Expand Down
31 changes: 29 additions & 2 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,11 +1485,22 @@ def _find_keyword_typos(self):
# Limit the number of possible matches to try
max_matches = 3
matches = []

hint = _get_cross_language_keyword_hint(wrong_name)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
hint = _get_cross_language_keyword_hint(wrong_name)
hint = _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name)

If it is only used once, and is marked as a "private" method, I would suggest calling the body directly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. I still think a get method looks nicer..

if hint:
matches.append(hint)
if _suggestions is not None:
suggestion = _suggestions._generate_suggestions(keyword.kwlist, wrong_name)
suggestion = _suggestions._generate_suggestions(keyword.kwlist + keyword.softkwlist, wrong_name)
if suggestion:
matches.append(suggestion)
matches.extend(difflib.get_close_matches(wrong_name, keyword.kwlist, n=max_matches, cutoff=0.5))
matches.extend(
difflib.get_close_matches(
wrong_name,
keyword.kwlist + keyword.softkwlist,
n=max_matches,
cutoff=0.5
)
)
matches = matches[:max_matches]
for suggestion in matches:
if not suggestion or suggestion == wrong_name:
Expand Down Expand Up @@ -1787,6 +1798,16 @@ def print(self, *, file=None, chain=True, **kwargs):
})


# Cross-language keyword suggestions.
_CROSS_LANGUAGE_KEYWORD_HINTS = frozendict({
# C/C++ equivalents
'switch': 'match',
'delete': 'del',
# function define equivalents
'function': 'def',
'func': 'def',
Comment thread
serhiy-storchaka marked this conversation as resolved.
})

def _substitution_cost(ch_a, ch_b):
if ch_a == ch_b:
return 0
Expand Down Expand Up @@ -1866,6 +1887,12 @@ def _get_cross_language_hint(obj, wrong_name):
return None


def _get_cross_language_keyword_hint(wrong_name):
"""Check if wrong_name is a common keyword from another language
"""
return _CROSS_LANGUAGE_KEYWORD_HINTS.get(wrong_name)


def _get_safe___dir__(obj):
# Use obj.__dir__() to avoid a TypeError when calling dir(obj).
# See gh-131001 and gh-139933.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Cross-language keyword suggestions are now shown for :exc:`SyntaxError`
messages. For example, ``switch x:`` suggests ``match``, ``delete x``
suggests ``del``, ``function f():`` suggests ``def``.
Loading