-
-
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 2 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,11 @@ 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'): | ||
| with suppress(Exception): | ||
|
||
| 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 +1654,45 @@ 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 not be catchable by Levenshtein distance (too different from | ||
| # the correct Python method name) | ||
| # 3. 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 string. | ||
| # If the suggestion is a Python method name, the standard "Did you mean" | ||
| # format is used. If it contains a space, it's rendered as a full hint. | ||
| # | ||
| # See https://discuss.python.org/t/106632 for the design discussion. | ||
|
||
| _CROSS_LANGUAGE_HINTS = { | ||
|
||
| # list -- JavaScript/Ruby equivalents | ||
| (list, "push"): "append", | ||
| (list, "concat"): "extend", | ||
|
||
| # list -- Java/C# equivalents | ||
| (list, "addAll"): "extend", | ||
| # list -- wrong-type suggestion (per Serhiy Storchaka, Terry Reedy, | ||
| # Paul Moore: list.add() more likely means the user expected a set) | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| (list, "add"): "Did you mean to use a set? Sets have an .add() method", | ||
| # str -- JavaScript equivalents | ||
| (str, "toUpperCase"): "upper", | ||
| (str, "toLowerCase"): "lower", | ||
| (str, "trimStart"): "lstrip", | ||
| (str, "trimEnd"): "rstrip", | ||
| # dict -- Java equivalents | ||
| (dict, "keySet"): "keys", | ||
| (dict, "entrySet"): "items", | ||
| (dict, "putAll"): "update", | ||
| # Note: indexOf, trim, and getOrDefault are not included because | ||
| # Levenshtein distance already catches them (indexOf->index, | ||
| # trim->strip, getOrDefault->setdefault). | ||
| } | ||
|
|
||
|
|
||
| def _substitution_cost(ch_a, ch_b): | ||
| if ch_a == ch_b: | ||
|
|
@@ -1711,6 +1755,23 @@ 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. | ||
| """ | ||
| hint = _CROSS_LANGUAGE_HINTS.get((type(obj), wrong_name)) | ||
| if hint is None: | ||
| return None | ||
| if ' ' in hint: | ||
| # Full custom hint (e.g., wrong-type suggestion for list.add) | ||
| return hint | ||
vstinner marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # Direct method equivalent -- format like Levenshtein suggestions | ||
| return f"Did you mean '.{hint}' instead of '.{wrong_name}'?" | ||
|
|
||
|
|
||
| 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,5 @@ | ||||||||
| 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`` (JavaScript), and | ||||||||
| ``"".toUpperCase()`` suggests ``upper``. The ``list.add()`` case suggests | ||||||||
| using a set instead, following feedback from the community discussion. | ||||||||
|
||||||||
| ``"".toUpperCase()`` suggests ``upper``. The ``list.add()`` case suggests | |
| using a set instead, following feedback from the community discussion. | |
| ``"".toUpperCase()`` suggests ``upper``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied your suggestion.
Uh oh!
There was an error while loading. Please reload this page.