-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
gh-146406: Add cross-language method suggestions for builtin AttributeError #146407
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
base: main
Are you sure you want to change the base?
Changes from 5 commits
7adad8b
aecaacb
6d58cdc
8a39e32
196dbe4
99e106a
57a4d39
2a2c0d5
f702e79
3ad54c8
0089761
e2c12ec
564ca3a
e1eb6f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1153,6 +1153,10 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, | |
| self._str += f". Did you mean '.{suggestion}' instead of '.{wrong_name}'?" | ||
| else: | ||
| self._str += f". Did you mean '.{suggestion}' ({suggestion!a}) instead of '.{wrong_name}' ({wrong_name!a})?" | ||
| elif hasattr(exc_value, 'obj'): | ||
| hint = _get_cross_language_hint(exc_value.obj, wrong_name) | ||
| if hint: | ||
| self._str += f". {hint}" | ||
| elif exc_type and issubclass(exc_type, NameError) and \ | ||
| getattr(exc_value, "name", None) is not None: | ||
| wrong_name = getattr(exc_value, "name", None) | ||
|
|
@@ -1649,6 +1653,42 @@ def print(self, *, file=None, chain=True, **kwargs): | |
| _MOVE_COST = 2 | ||
| _CASE_COST = 1 | ||
|
|
||
| # Cross-language method suggestions for builtin types. | ||
| # Consulted as a fallback when Levenshtein-based suggestions find no match. | ||
| # | ||
| # Inclusion criteria: | ||
| # 1. Must have evidence of real cross-language confusion (Stack Overflow | ||
vstinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # traffic, bug reports in production repos, developer survey data). | ||
| # 2. Must be from a top-4 language by Python co-usage: JavaScript, Java, | ||
| # C#, or Ruby (JetBrains/PSF Developer Survey 2024). | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # | ||
| # Each entry maps (builtin_type, wrong_name) to a (suggestion, is_raw) tuple. | ||
| # If is_raw is False, the suggestion is wrapped in "Did you mean '.X'?". | ||
| # If is_raw is True, the suggestion is rendered as-is. | ||
| # | ||
| # See https://github.com/python/cpython/issues/146406. | ||
| _CROSS_LANGUAGE_HINTS = { | ||
|
||
| # list -- JavaScript/Ruby equivalents | ||
| (list, "push"): ("append", False), | ||
| (list, "concat"): ("extend", False), | ||
| # list -- Java/C# equivalents | ||
| (list, "addAll"): ("extend", False), | ||
| (list, "contains"): ("Use 'x in list' to check membership.", True), | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # list -- wrong-type suggestion more likely means the user expected a set | ||
| (list, "add"): ("Did you mean to use a 'set' object?", True), | ||
| # str -- JavaScript equivalents | ||
| (str, "toUpperCase"): ("upper", False), | ||
| (str, "toLowerCase"): ("lower", False), | ||
| (str, "trimStart"): ("lstrip", False), | ||
| (str, "trimEnd"): ("rstrip", False), | ||
| # dict -- Java/JavaScript equivalents | ||
| (dict, "keySet"): ("keys", False), | ||
| (dict, "entrySet"): ("items", False), | ||
| (dict, "entries"): ("items", False), | ||
| (dict, "putAll"): ("update", False), | ||
| (dict, "put"): ("Use d[k] = v for item assignment.", True), | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
|
|
||
| def _substitution_cost(ch_a, ch_b): | ||
| if ch_a == ch_b: | ||
|
|
@@ -1711,6 +1751,22 @@ def _check_for_nested_attribute(obj, wrong_name, attrs): | |
| return None | ||
|
|
||
|
|
||
| def _get_cross_language_hint(obj, wrong_name): | ||
| """Check if wrong_name is a common method name from another language. | ||
|
|
||
| Only checks exact builtin types (list, str, dict) to avoid false | ||
| positives on subclasses that may intentionally lack these methods. | ||
| Returns a formatted hint string, or None. | ||
| """ | ||
| entry = _CROSS_LANGUAGE_HINTS.get((type(obj), wrong_name)) | ||
| if entry is None: | ||
| return None | ||
| hint, is_raw = entry | ||
| if is_raw: | ||
| return hint | ||
| return f"Did you mean '.{hint}'?" | ||
|
|
||
|
|
||
| def _get_safe___dir__(obj): | ||
| # Use obj.__dir__() to avoid a TypeError when calling dir(obj). | ||
| # See gh-131001 and gh-139933. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| Cross-language method suggestions are now shown for :exc:`AttributeError` on | ||
| builtin types when the existing Levenshtein-based suggestions find no match. | ||
| For example, ``[].push()`` now suggests ``append`` and | ||
| ``"".toUpperCase()`` suggests ``upper``. |
Uh oh!
There was an error while loading. Please reload this page.