From a899e7992f0d6b170f0347b95c4643ce58ac086d Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:37:33 -0700 Subject: [PATCH 1/2] gh-146239: Add cross-language method suggestions to AttributeError messages (#146239) When an attribute name from another programming language is used on a builtin type (e.g., list.push from JavaScript, str.toUpperCase from Java), Python now suggests the equivalent Python method (e.g., append, upper). This runs as a fallback after the existing Levenshtein-based suggestion matching. --- Lib/test/test_traceback.py | 52 +++++++++++++++++++ Lib/traceback.py | 49 +++++++++++++++++ ...20-cross-language-attributeerror-hints.rst | 6 +++ 3 files changed, 107 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst diff --git a/Lib/test/test_traceback.py b/Lib/test/test_traceback.py index 7124e49b22e361..a9adbf0318e5df 100644 --- a/Lib/test/test_traceback.py +++ b/Lib/test/test_traceback.py @@ -4564,6 +4564,58 @@ def __init__(self): actual = self.get_suggestion(Outer(), 'target') self.assertIn("'.normal.target'", actual) + def test_cross_language_list_suggestions(self): + # Common list method names from other languages that have no + # close Levenshtein match in list's attributes. + cases = [ + ([1, 2, 3], "push", "append"), # JavaScript / Ruby + ([1, 2, 3], "addAll", "extend"), # Java + ] + for obj, wrong, expected in cases: + with self.subTest(wrong=wrong): + actual = self.get_suggestion(obj, wrong) + self.assertIn(f"'.{expected}'", actual) + + def test_cross_language_str_suggestions(self): + # Common str method names from other languages that have no + # close Levenshtein match in str's attributes. + cases = [ + ("hello", "toUpperCase", "upper"), # JavaScript / Java + ("hello", "toLowerCase", "lower"), # JavaScript / Java + ("hello", "trimStart", "lstrip"), # JavaScript + ] + for obj, wrong, expected in cases: + with self.subTest(wrong=wrong): + actual = self.get_suggestion(obj, wrong) + self.assertIn(f"'.{expected}'", actual) + + def test_cross_language_dict_suggestions(self): + # Common dict method names from other languages that have no + # close Levenshtein match in dict's attributes. + cases = [ + ({"a": 1}, "putAll", "update"), # Java + ({"a": 1}, "entrySet", "items"), # Java + ] + for obj, wrong, expected in cases: + with self.subTest(wrong=wrong): + actual = self.get_suggestion(obj, wrong) + self.assertIn(f"'.{expected}'", actual) + + def test_cross_language_no_suggestion_for_subclasses(self): + # Cross-language hints only trigger for exact builtin types, + # not subclasses, to avoid false positives on custom classes. + class CustomList(list): + pass + + actual = self.get_suggestion(CustomList([1, 2, 3]), 'push') + self.assertNotIn("'.append'", actual) + + def test_cross_language_no_suggestion_for_unknown_attr(self): + # Attributes not in the cross-language table should not get + # suggestions from it (and may get no suggestion at all). + actual = self.get_suggestion([1, 2, 3], 'frobulate') + self.assertNotIn("Did you mean", actual) + def make_module(self, code): tmpdir = Path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, tmpdir) diff --git a/Lib/traceback.py b/Lib/traceback.py index 56a72ce7f5b293..5202f6eb5058d1 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -1715,6 +1715,47 @@ def _get_safe___dir__(obj): ) +# Mapping of common method names from other programming languages to their +# Python equivalents. Used as a fallback when Levenshtein-based suggestion +# matching finds no close match. Only covers cases where a real Python +# method exists on the same builtin type. +_CROSS_LANGUAGE_ATTR_HINTS = { + # JavaScript / Ruby -> Python (list) + (list, "push"): "append", + (list, "indexOf"): "index", + (list, "unshift"): "insert", + (list, "concat"): "extend", + (list, "add"): "append", # Java / C# + (list, "addAll"): "extend", # Java + # JavaScript / Java -> Python (str) + (str, "toUpperCase"): "upper", + (str, "toLowerCase"): "lower", + (str, "indexOf"): "index", + (str, "trim"): "strip", + (str, "trimStart"): "lstrip", + (str, "trimEnd"): "rstrip", + (str, "replaceAll"): "replace", + # Java / C# -> Python (dict) + (dict, "putAll"): "update", + (dict, "keySet"): "keys", + (dict, "entrySet"): "items", + (dict, "getOrDefault"): "get", + (dict, "remove"): "pop", +} + + +def _check_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 custom subclasses that may intentionally omit methods. + """ + obj_type = type(obj) + if obj_type not in (list, str, dict): + return None + return _CROSS_LANGUAGE_ATTR_HINTS.get((obj_type, wrong_name)) + + def _compute_suggestion_error(exc_value, tb, wrong_name): if wrong_name is None or not isinstance(wrong_name, str): return None @@ -1830,6 +1871,14 @@ def _compute_suggestion_error(exc_value, tb, wrong_name): if nested_suggestion: return nested_suggestion + # If still no suggestion, check for common method names from other + # programming languages (e.g., push -> append, trim -> strip). + if not suggestion and isinstance(exc_value, AttributeError): + with suppress(Exception): + cross_lang = _check_cross_language_hint(exc_value.obj, wrong_name) + if cross_lang is not None: + return cross_lang + return suggestion diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst new file mode 100644 index 00000000000000..98caca1b75cf6f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst @@ -0,0 +1,6 @@ +Added cross-language method suggestions to :exc:`AttributeError` messages. +When an attribute name from another programming language is used on a +builtin type (e.g., ``list.push`` from JavaScript, ``str.toUpperCase`` from +Java), Python now suggests the equivalent Python method (e.g., ``append``, +``upper``). This runs as a fallback after the existing Levenshtein-based +suggestion matching. From 407111d557af2617bada7c45f44c09dbbc18da40 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 20 Mar 2026 22:39:25 -0700 Subject: [PATCH 2/2] gh-146239: Fix NEWS entry filename format Use the blurb-compatible naming convention: YYYY-MM-DD-HH-MM-SS.gh-issue-NNNNN.NONCE.rst --- ...r-hints.rst => 2026-03-20-12-00-00.gh-issue-146239.BMESfJ.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Misc/NEWS.d/next/Core_and_Builtins/{2026-03-20-cross-language-attributeerror-hints.rst => 2026-03-20-12-00-00.gh-issue-146239.BMESfJ.rst} (100%) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-12-00-00.gh-issue-146239.BMESfJ.rst similarity index 100% rename from Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-cross-language-attributeerror-hints.rst rename to Misc/NEWS.d/next/Core_and_Builtins/2026-03-20-12-00-00.gh-issue-146239.BMESfJ.rst