# Cross-language method suggestions for AttributeError

**URL:** <https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632>\
**Category:** Ideas\
**Tags:** builtins, error-handling\
**Created:** [March 21, 2026, 6:05pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632 "2026-03-21T18:05:42Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 21, 2026, 6:05pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/1 "2026-03-21T18:05:42Z")

</div>

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:

```python
[1, 2, 3].push(4)

```

You get:

```python
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'`](https://stackoverflow.com/questions/44159342) - JS/Ruby developers
- [`'str' object has no attribute 'contains'`](https://stackoverflow.com/questions/61835678) - Java developers
- [`'str' object has no attribute 'trim'`](https://stackoverflow.com/questions/47568565) - JS/Java developers

The search patterns tell the same story. [“Why do python lists have pop() but not push?”](https://stackoverflow.com/questions/1566266) has 320K views. [“How do I trim whitespace?”](https://stackoverflow.com/questions/761804) 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.lstrip` and `str.rstrip` methods.”

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:

```python
# 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:

1. **Static table for builtins only.** Small. Simple. Covers the highest-traffic cases. Similar to how `print`/`exec` detection is hardcoded for exactly 2 candidates.

2. **Registration API.** Something like `sys.register_attr_suggestion(type, wrong_name, right_name)`. Libraries register their own mappings. Adds API surface.

3. **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.

4. **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.

---

<div class="post-metadata">

**Author:** ![nedbat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nedbat/32/8744_2.png) [@nedbat](https://discuss.python.org/u/nedbat)\
**Post date:** [March 21, 2026, 9:01pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/2 "2026-03-21T21:01:13Z")

</div>

This sounds great to me. It only kicks in when the current behavior has nothing to suggest, so it’s a definite improvement.

---

<div class="post-metadata">

**Author:** ![dr\_carlos](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dr_carlos/32/30461_2.png) [@dr\_carlos](https://discuss.python.org/u/dr_carlos)\
**Post date:** [March 21, 2026, 9:03pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/3 "2026-03-21T21:03:34Z")

</div>

Seems like an excellent suggestion, and a natural extension of the error message improvements we’ve been getting over the last few Python versions!

One thing to note is that there are only 4 custom `SyntaxError`s from other languages, but your `AttributeError` list already has 11 items. This isn’t a bad thing, but I could see this getting out of hand quickly if there isn’t at least some simple guidelines around adding new items to the list (do we need a new way to spell ‘append’)?

So I’d say that option 1 is the best way to go:

> [@mvanhorn](#):
>
> **Static table for builtins only.** Small. Simple. Covers the highest-traffic cases. Similar to how `print`/`exec` detection is hardcoded for exactly 2 candidates.

If at some point in the future we find that extensibility is actually useful here, it can be added later.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 21, 2026, 9:36pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/4 "2026-03-21T21:36:20Z")

</div>

Thanks Ned. Yeah the “only when current suggestions have nothing” part felt important - it’s purely additive. I have a sense of how this could fit into the existing suggestion machinery and would be interested in taking a crack at a PR if more people feel the same way.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 21, 2026, 9:37pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/5 "2026-03-21T21:37:00Z")

</div>

Good call on the mapping count. I think the right bar is: only add an entry when there’s clear evidence of real confusion (SO traffic, survey data), not just because two methods do similar things. In practice that’s maybe 15-20 entries across list/str/dict/set, living in one place in the C source. Agreed on Option 1.

---

<div class="post-metadata">

**Author:** ![csm10495](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/csm10495/32/9731_2.png) [@csm10495](https://discuss.python.org/u/csm10495)\
**Post date:** [March 21, 2026, 10:07pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/6 "2026-03-21T22:07:46Z")

</div>

I like this idea though how far should we go?

When I first learned C++ we used `std::list` (and `std::vector`) which are sort of like a `list` in Python. Those have `push_back` for example. That’s more or less like `append` in Python. Similar idea with `push_front`(in `std::list`) and `insert(0, X)`. We also have stuff like `merge` in C++ that can sort of be done with `extend` or list comprehension.

.. how far should we go? Is there a point where it’s too far?

---

<div class="post-metadata">

**Author:** ![bwoodsend](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwoodsend/32/4027_2.png) [@bwoodsend](https://discuss.python.org/u/bwoodsend)\
**Post date:** [March 21, 2026, 10:28pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/7 "2026-03-21T22:28:06Z")

</div>

Note that most of the cited links are false positives. The only one that genuinely has the user using an equivalent Java method name in place of a Python one and got an `AttributeError` and it wasn’t really due to either an unexpected `None` or unrelated type confusion is for `"string".contains()` which requires more than just a mapping of names to accommodate.

It’s hard to say how many people really will see the AttributeErrors and how many will instead type `my_list.pu<tab>` in their IDEs, get zero hits and be just as in the dark as before.

(I still support the overall idea – at least if limited to the really obvious ones like `list.push()`)

---

<div class="post-metadata">

**Author:** ![storchaka](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/storchaka/32/217_2.png) [@storchaka](https://discuss.python.org/u/storchaka)\
**Post date:** [March 22, 2026, 12:02pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/8 "2026-03-22T12:02:51Z")

</div>

> [@mvanhorn](#):
>
> `"add" -> "append" # Java, C#`

It can also mean that you passed a list instead of set. Changing “add” to “append” will not fix the error, it will introduce yet one error.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 22, 2026, 1:39pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/9 "2026-03-22T13:39:41Z")

</div>

Good point. The `list.add()` case is tricky because it could mean “I have a list but expected a set” not “I’m coming from Java.”

The way I see it: this only fires when `difflib.get_close_matches` already came up empty. So `set.add()` works fine today and wouldn’t trigger anything. The suggestion would only appear for `list.add()` where there’s genuinely no close match on `list`.

To handle the ambiguity, the message could include the source language hint: `did you mean 'append'? ('add' is the equivalent in Java/C#)`. That way if someone really did mean a set, the Java/C# context is a signal this isn’t their situation.

But if there are cases where even that framing is confusing, I’m happy to drop `add -> append` from the initial table entirely. Better to ship 12 unambiguous mappings than 15 with edge cases.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 22, 2026, 1:55pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/10 "2026-03-22T13:55:14Z")

</div>

I’d keep it tight. JavaScript/Java/C#/Ruby cover the vast majority of “I just switched to Python” developers. C++ methods like `push_back` are possible but feel more niche.

The table is easy to extend later. Starting small and adding based on real confusion data (Stack Overflow questions, CPython issue reports) seems better than trying to cover everything upfront.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 22, 2026, 1:58pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/11 "2026-03-22T13:58:52Z")

</div>

Fair point on the SO links. You’re right that most of them are better explained by `None` values or type confusion than cross-language habits. The `contains()` one is the clearest genuine case. I should’ve been more careful with those citations.

That said, there is stronger evidence outside Stack Overflow:

**Real AttributeError bugs in production repos:** `list.push()` ([acode/lib-python #3](https://github.com/acode/lib-python/issues/3)), `str.startsWith()` ([falkTX/Carla #68](https://github.com/falkTX/Carla/issues/68)), and another `list.push()` in Mozilla’s WebThingsIO/webthing-python #29. These are unambiguous cross-language confusion, not type errors.

**On IDEs:** Autocomplete helps when you don’t know the method name. It doesn’t help when you’re _confident_ you know it. You’ve been writing JavaScript for three years. `push` is second nature. You type `.push()`, hit run, bare `AttributeError`, no suggestion. That’s the gap.

And a lot of Python happens outside IDEs entirely. Jupyter notebooks, Google Colab, Replit, terminal REPL. PEP 762 overhauled the built-in REPL for 3.13 because it’s still heavily used. IDLE ships with Python, recommended for beginners, no cross-language autocomplete. The JetBrains/PSF 2024 survey: 40% of Python-primary devs also use JavaScript.

**On precedent:** CPython already does this! Since 3.12, `import os from sys` gives you `Did you mean 'from ... import ...'?` That’s a JavaScript ES6 pattern getting a helpful redirect. Same idea here, just for AttributeError, and only when Levenshtein already found nothing.

On scope: agreed. Only mappings with a direct 1:1 equivalent, real confusion data backing them up, and too far from existing methods for Levenshtein to catch. ~15 entries total.

---

<div class="post-metadata">

**Author:** ![tjreedy](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/tjreedy/32/137_2.png) [@tjreedy](https://discuss.python.org/u/tjreedy)\
**Post date:** [March 22, 2026, 10:44pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/12 "2026-03-22T22:44:15Z")

</div>

FWIW, IDLE prints exception messages as received from the interpreter. Hence,  
`NameError: name 'imt' is not defined. Did you mean: 'int'?`.

When a method exists for another, related, Python class, I think it better to suggest the other class rather than assume a foreign language match. Hence list.add =\> (something like) “Did you mean to use a set?”.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 23, 2026, 1:00pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/13 "2026-03-23T13:00:37Z")

</div>

Thats a good point - the “wrong container” case is genuinely different from the cross-language muscle memory case. Someone who meant to use a set vs. someone who’s been writing Java all week.

I think they could layer together though. Check for a matching method on a related builtin type first, then fall back to the cross-language table if nothing fits. That way `list.add` gets “did you mean to use a set?” but `list.push` goes straight to the JS/Java hint.

---

<div class="post-metadata">

**Author:** ![pf\_moore](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pf_moore/32/35_2.png) [@pf\_moore](https://discuss.python.org/u/pf_moore)\
**Post date:** [March 23, 2026, 1:19pm UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/14 "2026-03-23T13:19:14Z")

</div>

I’m not sure there’s any need to check - if we’re going for option 1 (static table for builtins), the table can simply map the method name and object type onto a suggestion. So `add` on a list is mapped straight to “did you mean to use a set?” No need to check whether `add` is a set method - you _know_ it is already.

Same for any of the registration options. Just let the user register the text of a suggestion, and don’t worry about trying to introspect what the user might have meant.

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 24, 2026, 1:24am UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/15 "2026-03-24T01:24:05Z")

</div>

Good point - a flat table keeps it simple. Each (type, method) entry just carries the  
right suggestion text, no layering needed… think this is ready for me to take a stab at? Sorry this is my first feature try, I’m a little overexcited!

---

<div class="post-metadata">

**Author:** ![mvanhorn](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/mvanhorn/32/34825_2.png) [@mvanhorn](https://discuss.python.org/u/mvanhorn)\
**Post date:** [March 25, 2026, 7:23am UTC](https://discuss.python.org/t/cross-language-method-suggestions-for-attributeerror/106632/16 "2026-03-25T07:23:56Z")

</div>

Just submitted a PR on this feature, would love any feedback..

> <https://github.com/python/cpython/pull/146407>
>
> \## Summary
> 
> When an \`AttributeError\` on a builtin type (\`list\`, \`str\`, \`dict\`) h…as no
> Levenshtein-based suggestion, check a static table of common method names
> from other languages.
> 
> \*\*Before:\*\*
> \`\`\`python
> \>\>\> \[1, 2, 3\].push(4)
> AttributeError: 'list' object has no attribute 'push'
> \`\`\`
> 
> \*\*After:\*\*
> \`\`\`python
> \>\>\> \[1, 2, 3\].push(4)
> AttributeError: 'list' object has no attribute 'push'. Did you mean '.append' instead of '.push'?
> \`\`\`
> 
> \## Discourse discussion
> 
> Discussed at https://discuss.python.org/t/106632 (420 views, 25 likes, 15 posts, 3 core devs).
> 
> Design decisions from the thread:
> 
> \- \*\*Flat table\*\* per @pf\_moore (\[post 14\](https://discuss.python.org/t/106632/14), 4 likes):
> \> "the table can simply map the method name and object type onto a suggestion. So \`add\` on a list is mapped straight to 'did you mean to use a set?' No need to check whether \`add\` is a set method - you know it is already."
> 
> \- \*\*list.add() suggests set, not append\*\* per @storchaka (\[post 8\](https://discuss.python.org/t/106632/8)):
> \> "It can also mean that you passed a list instead of set. Changing 'add' to 'append' will not fix the error, it will introduce yet one error."
> 
> Reinforced by @tjreedy (\[post 12\](https://discuss.python.org/t/106632/12)):
> \> "When a method exists for another, related, Python class, I think it better to suggest the other class rather than assume a foreign language match."
> 
> \- \*\*Scope guardrails\*\* per @dr\_carlos (\[post 3\](https://discuss.python.org/t/106632/3)): only add entries backed by real confusion evidence, not just because methods are similar.
> 
> \- \*\*Static table for builtins only (Option 1)\*\* - community consensus. 11 entries covering JavaScript, Java, C#, and Ruby.
> 
> \## Verification
> 
> Before (system Python 3.13, no cross-language hints):
> \`\`\`
> \>\>\> \[1, 2, 3\].push(4)
> AttributeError: 'list' object has no attribute 'push'
> 
> \>\>\> 'hello'.toUpperCase()
> AttributeError: 'str' object has no attribute 'toUpperCase'
> 
> \>\>\> {}.keySet()
> AttributeError: 'dict' object has no attribute 'keySet'
> 
> \>\>\> \[\].add(1)
> AttributeError: 'list' object has no attribute 'add'
> \`\`\`
> 
> After (this PR):
> \`\`\`
> \>\>\> \[1, 2, 3\].push(4)
> AttributeError: 'list' object has no attribute 'push'. Did you mean '.append' instead of '.push'?
> 
> \>\>\> 'hello'.toUpperCase()
> AttributeError: 'str' object has no attribute 'toUpperCase'. Did you mean '.upper' instead of '.toUpperCase'?
> 
> \>\>\> {}.keySet()
> AttributeError: 'dict' object has no attribute 'keySet'. Did you mean '.keys' instead of '.keySet'?
> 
> \>\>\> \[\].add(1)
> AttributeError: 'list' object has no attribute 'add'. Did you mean to use a set? Sets have an .add() method
> \`\`\`
> 
> Levenshtein still takes priority (trim-\>strip, indexOf-\>index already work and are not in the table). Only exact builtin types are matched - subclasses are not affected.
> 
> \## Changes
> 
> \- \`Lib/traceback.py\`: Added \`\_CROSS\_LANGUAGE\_HINTS\` dict (11 entries), \`\_get\_cross\_language\_hint()\` function, and a 4-line fallback hook in \`TracebackException.\_\_init\_\_\` that runs only when Levenshtein found nothing.
> \- \`Lib/test/test\_traceback.py\`: 15 test cases covering all entries, priority ordering, unknown attrs, and subclass exclusion.
> \- \`Misc/NEWS.d/\`: NEWS entry.
> 
> \## Evidence
> 
> | Source | Evidence |
> |--------|----------|
> | \[SO: push vs append\](https://stackoverflow.com/questions/1566266) | 320K views |
> | \[SO: trim whitespace\](https://stackoverflow.com/questions/761804) | 1.5M views |
> | \[JetBrains/PSF Survey 2024\](https://lp.jetbrains.com/python-developers-survey-2024/) | 40% of Python devs also use JS |
> | CPython precedent | \`elseif\`-\>\`elif\` (gh-132449), \`import x from y\`-\>\`from x import y\` (gh-98931) |
> | Discourse thread | 420 views, 25 likes, 3 core devs supportive |
> 
> Fixes #146406
> 
> 
> \* Issue: gh-146406

11 entries covering list/str/dict. Levenshtein takes priority - a few entries I originally planned (indexOf, trim) turned out to be redundant since Levenshtein already catches them. The list.add case suggests using a set per @storchaka’s feedback, and the whole thing is a flat table per @pf_moore’s suggestion - no introspection.
