Skip to content

Commit 196dbe4

Browse files
committed
Address review feedback from @picnixz
- Use (hint, is_raw) tuples instead of space-based raw detection - Shorten list.add hint to "Did you mean to use a 'set' object?" - Use d[k] = v instead of dict[key] = value for dict.put hint - Add dict.entries -> items (JavaScript) - Remove Levenshtein guardrail from code comment (belongs on issue) - Add periods to raw hint messages - Add test for dict.entries
1 parent 8a39e32 commit 196dbe4

3 files changed

Lines changed: 34 additions & 36 deletions

File tree

Doc/whatsnew/3.15.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ Improved error messages
445445
>>> {}.put("a", 1) # doctest: +ELLIPSIS
446446
Traceback (most recent call last):
447447
...
448-
AttributeError: 'dict' object has no attribute 'put'. Use dict[key] = value for item assignment
448+
AttributeError: 'dict' object has no attribute 'put'. Use d[k] = v for item assignment.
449449

450450
(Contributed by Matt Van Horn in :gh:`146406`.)
451451

Lib/test/test_traceback.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4583,7 +4583,7 @@ def test_cross_language_list_contains_suggests_in(self):
45834583

45844584
def test_cross_language_list_add_suggests_set(self):
45854585
actual = self.get_suggestion([], 'add')
4586-
self.assertIn("Did you mean to use a set?", actual)
4586+
self.assertIn("Did you mean to use a 'set' object?", actual)
45874587

45884588
def test_cross_language_str_toUpperCase_suggests_upper(self):
45894589
actual = self.get_suggestion('', 'toUpperCase')
@@ -4613,9 +4613,13 @@ def test_cross_language_dict_putAll_suggests_update(self):
46134613
actual = self.get_suggestion({}, 'putAll')
46144614
self.assertIn("'.update'", actual)
46154615

4616+
def test_cross_language_dict_entries_suggests_items(self):
4617+
actual = self.get_suggestion({}, 'entries')
4618+
self.assertIn("'.items'", actual)
4619+
46164620
def test_cross_language_dict_put_suggests_bracket(self):
46174621
actual = self.get_suggestion({}, 'put')
4618-
self.assertIn("dict[key] = value", actual)
4622+
self.assertIn("d[k] = v", actual)
46194623

46204624
def test_cross_language_levenshtein_takes_priority(self):
46214625
# Levenshtein catches trim->strip and indexOf->index before

Lib/traceback.py

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,40 +1658,35 @@ def print(self, *, file=None, chain=True, **kwargs):
16581658
#
16591659
# Inclusion criteria:
16601660
# 1. Must have evidence of real cross-language confusion (Stack Overflow
1661-
# traffic, bug reports in production repos, developer survey data)
1662-
# 2. Must not be catchable by Levenshtein distance (too different from
1663-
# the correct Python method name)
1664-
# 3. Must be from a top-4 language by Python co-usage: JavaScript, Java,
1665-
# C#, or Ruby (JetBrains/PSF Developer Survey 2024)
1661+
# traffic, bug reports in production repos, developer survey data).
1662+
# 2. Must be from a top-4 language by Python co-usage: JavaScript, Java,
1663+
# C#, or Ruby (JetBrains/PSF Developer Survey 2024).
16661664
#
1667-
# Each entry maps (builtin_type, wrong_name) to a suggestion string.
1668-
# If the suggestion is a Python method name, the standard "Did you mean"
1669-
# format is used. If it contains a space, it's rendered as a full hint.
1665+
# Each entry maps (builtin_type, wrong_name) to a (suggestion, is_raw) tuple.
1666+
# If is_raw is False, the suggestion is wrapped in "Did you mean '.X'?".
1667+
# If is_raw is True, the suggestion is rendered as-is.
16701668
#
1671-
# See https://github.com/python/cpython/issues/146406 for the design discussion.
1669+
# See https://github.com/python/cpython/issues/146406.
16721670
_CROSS_LANGUAGE_HINTS = {
16731671
# list -- JavaScript/Ruby equivalents
1674-
(list, "push"): "append",
1675-
(list, "concat"): "extend",
1672+
(list, "push"): ("append", False),
1673+
(list, "concat"): ("extend", False),
16761674
# list -- Java/C# equivalents
1677-
(list, "addAll"): "extend",
1678-
(list, "contains"): "Use 'x in list' to check membership",
1679-
# list -- wrong-type suggestion (per Serhiy Storchaka, Terry Reedy,
1680-
# Paul Moore: list.add() more likely means the user expected a set)
1681-
(list, "add"): "Did you mean to use a set? Sets have an .add() method",
1675+
(list, "addAll"): ("extend", False),
1676+
(list, "contains"): ("Use 'x in list' to check membership.", True),
1677+
# list -- wrong-type suggestion more likely means the user expected a set
1678+
(list, "add"): ("Did you mean to use a 'set' object?", True),
16821679
# str -- JavaScript equivalents
1683-
(str, "toUpperCase"): "upper",
1684-
(str, "toLowerCase"): "lower",
1685-
(str, "trimStart"): "lstrip",
1686-
(str, "trimEnd"): "rstrip",
1687-
# dict -- Java equivalents
1688-
(dict, "keySet"): "keys",
1689-
(dict, "entrySet"): "items",
1690-
(dict, "putAll"): "update",
1691-
(dict, "put"): "Use dict[key] = value for item assignment",
1692-
# Note: indexOf, trim, and getOrDefault are not included because
1693-
# Levenshtein distance already catches them (indexOf->index,
1694-
# trim->strip, getOrDefault->setdefault).
1680+
(str, "toUpperCase"): ("upper", False),
1681+
(str, "toLowerCase"): ("lower", False),
1682+
(str, "trimStart"): ("lstrip", False),
1683+
(str, "trimEnd"): ("rstrip", False),
1684+
# dict -- Java/JavaScript equivalents
1685+
(dict, "keySet"): ("keys", False),
1686+
(dict, "entrySet"): ("items", False),
1687+
(dict, "entries"): ("items", False),
1688+
(dict, "putAll"): ("update", False),
1689+
(dict, "put"): ("Use d[k] = v for item assignment.", True),
16951690
}
16961691

16971692

@@ -1763,13 +1758,12 @@ def _get_cross_language_hint(obj, wrong_name):
17631758
positives on subclasses that may intentionally lack these methods.
17641759
Returns a formatted hint string, or None.
17651760
"""
1766-
hint = _CROSS_LANGUAGE_HINTS.get((type(obj), wrong_name))
1767-
if hint is None:
1761+
entry = _CROSS_LANGUAGE_HINTS.get((type(obj), wrong_name))
1762+
if entry is None:
17681763
return None
1769-
if ' ' in hint:
1770-
# Full custom hint (e.g., wrong-type suggestion for list.add)
1764+
hint, is_raw = entry
1765+
if is_raw:
17711766
return hint
1772-
# Direct method equivalent
17731767
return f"Did you mean '.{hint}'?"
17741768

17751769

0 commit comments

Comments
 (0)