Hi - I’ve had a couple PRs merged into CPython recently (defaultdict repr fix, man page wrapping fix) and have been poking around the error suggestion system. Found something worth talking about.
Python’s “Did you mean?” works great for typos. items.appnd → append. But it doesn’t help when the wrong name comes from another language.
Try this:
[1, 2, 3].push(4)
You get:
AttributeError: 'list' object has no attribute 'push'
No suggestion. push and append are too far apart for Levenshtein to match. Same story with str.toUpperCase() (Python: upper()), str.trim() (Python: strip()), dict.keySet() (Python: keys()).
This actually happens
Stack Overflow threads where people hit this exact AttributeError:
'list' object has no attribute 'push'- JS/Ruby developers'str' object has no attribute 'contains'- Java developers'str' object has no attribute 'trim'- JS/Java developers
The search patterns tell the same story. “Why do python lists have pop() but not push?” has 320K views. “How do I trim whitespace?” has 1.5M views - people are searching for trim, not strip.
The JetBrains/PSF Developer Survey 2024: 40% of Python-primary developers also use JavaScript. 64% of web-focused Python devs use JS. That’s a lot of people carrying method-name habits from other ecosystems.
PEP 616 (removeprefix/removesuffix) exists because of this exact class of confusion. From the PEP:
“There have been repeated issues on Python-Ideas, Python-Dev, the Bug Tracker, and StackOverflow, related to user confusion about the existing
str.lstripandstr.rstripmethods.”
MIT and the University of Washington maintain explicit Java-to-Python method mapping tables for students. List.add(e) → list.append(e), toLowerCase → lower, indexOf → find. Universities building translation cheatsheets tells you the gap is structural.
CPython already does this for SyntaxError
This isn’t new territory. CPython has been adding cross-language suggestions for years:
print "hello"→print("hello")(Python 2 migration, Nick Coghlan 2014, moved to PEG parser by Pablo Galindo 2021)exec "code"→exec("code")(same mechanism)elseif→elif(C/JS/PHP keyword, Pablo Galindo 2025, gh-132449)import x from y→from x import y(JS syntax, Pablo Galindo 2022, gh-98931)
All cases where CPython detects syntax from another language and points you to the Python way. Extending this from SyntaxError to AttributeError feels like a natural next step.
The proposal
A fallback in the existing suggestion system: when Levenshtein finds no match, check a mapping of common method names from other languages to their Python equivalents.
For builtins:
# list
"push" -> "append" # JS, Ruby
"add" -> "append" # Java, C#
"addAll" -> "extend" # Java
"indexOf" -> "index" # JS, Java
# str
"toUpperCase" -> "upper" # JS, Java
"toLowerCase" -> "lower"
"trim" -> "strip"
"trimStart" -> "lstrip" # JS
# dict
"keySet" -> "keys" # Java
"entrySet" -> "items" # Java
"putAll" -> "update" # Java
Typo suggestions always take priority. The cross-language lookup only runs when Levenshtein finds nothing.
Open question: extensibility
A static table only covers builtins. A few possible directions:
-
Static table for builtins only. Small. Simple. Covers the highest-traffic cases. Similar to how
print/execdetection is hardcoded for exactly 2 candidates. -
Registration API. Something like
sys.register_attr_suggestion(type, wrong_name, right_name). Libraries register their own mappings. Adds API surface. -
Convention-based. A
__attr_suggestions__class attribute (dict mapping wrong names to right names). Follows the dunder pattern. Libraries opt in per-class, no global registry. -
Builtins now, extensibility later. Ship the static table. Design extensibility based on real usage data.
No strong preference here. Option 1 is the least controversial. Option 3 is interesting but might be over-engineering. Curious what others think.
What this doesn’t cover
- Syntax equivalents like
list.length→len(list)(different message format needed) - Operator confusion like
++→+= 1(SyntaxError, not AttributeError) - Custom class support (see above)
Interested in hearing whether this is worth pursuing and which direction makes sense.