You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
rlcompleter suggests attributes not accessible on instances (e.g., Enum member __name__) #139819
While looking into gh-139398, I noticed a separate issue: some tab-completion suggestions are offered even though they do not actually exist / are not accessible.
For example, on an Enum member, __name__ is suggested but accessing it raises AttributeError. Tracing the behavior shows rlcompleter includes class-level names even when they aren’t accessible from the instance; adding a guard in rlcompleter fixes it (PR incoming).
Reproduce
fromenumimportEnumclassColor(Enum):
BLUE=1# In the REPL, press <TAB> after typing:Color.BLUE.__# '__name__' appears in the suggestions.Color.BLUE.__name__# AttributeError: 'Color' object has no attribute '__name__'
Expected
Only attributes actually accessible on the instance should be suggested.
Actual
Class-level names show up for instances (e.g., Enum members), leading to AttributeError when accessed.
Fix
diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py
index 23eb0020f42..521f41a869d 100644
--- a/Lib/rlcompleter.py+++ b/Lib/rlcompleter.py@@ -39,6 +39,9 @@
__all__ = ["Completer"]
+# Sentinel object to distinguish "missing" from "present but None"+_SENTINEL = object()+
class Completer:
def __init__(self, namespace = None):
"""Create a new completer for the command line.
@@ -188,9 +191,9 @@ def attr_matches(self, text):
# property method, which is not desirable.
matches.append(match)
continue
- if (value := getattr(thisobject, word, None)) is not None:+ if (value := getattr(thisobject, word, _SENTINEL)) is not _SENTINEL:
matches.append(self._callable_postfix(value, match))
- else:+ elif word in getattr(type(thisobject), '__slots__', ()):
matches.append(match)
if matches or not noprefix:
break
Also observed (bogus suggestions)
['Color.BLUE.__iter__',
'Color.BLUE.__getitem__',
'Color.BLUE.__members__',
'Color.BLUE.__contains__',
'Color.BLUE.__qualname__',
'Color.BLUE.__len__',
'Color.BLUE.__name__']
# Additionally seen among bogus suggestions:
'Enum.__abstractmethods__'
I would rather change __dir__ of Enum instead. The reason is that we actually have a custom __dir__ for enums so the reason why __name__ is here may be because of that.
The spurious completions appear because rlcompleter.attr_matches() also considers attributes from the class while completing on an instance, without guarding that those names are actually accessible from the instance. This makes the behavior incorrect in mixed class/instance or inheritance scenarios (e.g., Enum members).
I think the fix belongs in rlcompleter, but I’m open to alternatives.
For context, the current logic in attr_matches() is:
if (value:=getattr(thisobject, word, None)) isnotNone:
matches.append(self._callable_postfix(value, match))
else:
matches.append(match)
The else path is intended to include attributes whose value is None, but it does so without verifying that the attribute actually exists on the instance. This is what lets class-level names leak into instance completions.
Ah I see. I can understand why it's an issue. For instance, with int, the reason why __name__ doesn't show up in x.__<TAB> when x is an int is because dir(int) doesn't contain __name__.
This is what lets class-level names leak into instance completions.
However, this should be accepted. As I can see on the PR, the tests are failing because we don't want to invoke __getattribute__ more than once per attribute (otherwise it may become VERY slow/or have undesirable side-effects). An hasattr check unfortunately calls it as well.
An alternative would be to try to get __dict__ of the instance, if any and check for name in __dict__. For instances that do not have a __dict__, we should indeed try to use hasattr. At least, we wouldn't need to trigger many __getattribute__ that could have undesirable side-effects.
Thanks a lot for the thoughtful suggestions — I completely agree about avoiding multiple __getattribute__ calls per candidate.
I’ve updated the PR with a sentinel-based approach so we keep correctness while sticking to a single lookup:
Define a module-level _SENTINEL = object() and use if (value := getattr(thisobject, word, _SENTINEL)) is not _SENTINEL:
This treats “present but `None`” as present, so we can drop the old elif hasattr(...) branch and still avoid a second lookup.
Keep the early return for @property to avoid evaluating properties.
Preserve the slots behavior per the existing tests: elif word in getattr(type(thisobject), '__slots__', ()): matches.append(match)
from test: test_uncreated_attr
Attributes like properties and slots should be completed even when
they haven't been created on an instance
The PR has been updated and tests are passing.
As a side note on __dict__: it can be a useful positive hint, but it’s not complete. For example, __name__ exists on a type but won’t show up in its __dict__:
Activity
picnixz commentedon Oct 9, 2025
I would rather change
__dir__ofEnuminstead. The reason is that we actually have a custom__dir__for enums so the reason why__name__is here may be because of that.ttw225 commentedon Oct 9, 2025
Thanks @picnixz ! Yes,
Enumdefines a custom__dir__.And I previously verified that Enum’s custom dir behaves correctly:
The spurious completions appear because
rlcompleter.attr_matches()also considers attributes from the class while completing on an instance, without guarding that those names are actually accessible from the instance. This makes the behavior incorrect in mixed class/instance or inheritance scenarios (e.g., Enum members).I think the fix belongs in rlcompleter, but I’m open to alternatives.
ttw225 commentedon Oct 9, 2025
For context, the current logic in
attr_matches()is:The
elsepath is intended to include attributes whose value isNone, but it does so without verifying that the attribute actually exists on the instance. This is what lets class-level names leak into instance completions.picnixz commentedon Oct 9, 2025
Ah I see. I can understand why it's an issue. For instance, with
int, the reason why__name__doesn't show up inx.__<TAB>whenxis an int is becausedir(int)doesn't contain__name__.However, this should be accepted. As I can see on the PR, the tests are failing because we don't want to invoke
__getattribute__more than once per attribute (otherwise it may become VERY slow/or have undesirable side-effects). Anhasattrcheck unfortunately calls it as well.An alternative would be to try to get
__dict__of the instance, if any and check forname in __dict__. For instances that do not have a__dict__, we should indeed try to usehasattr. At least, we wouldn't need to trigger many__getattribute__that could have undesirable side-effects.ttw225 commentedon Oct 9, 2025
Reply to @picnixz
Thanks a lot for the thoughtful suggestions — I completely agree about avoiding multiple
__getattribute__calls per candidate.I’ve updated the PR with a sentinel-based approach so we keep correctness while sticking to a single lookup:
_SENTINEL = object()and useif (value := getattr(thisobject, word, _SENTINEL)) is not _SENTINEL:This treats “present but `None`” as present, so we can drop the old
elif hasattr(...)branch and still avoid a second lookup.@propertyto avoid evaluating properties.elif word in getattr(type(thisobject), '__slots__', ()): matches.append(match)from test:
test_uncreated_attrThe PR has been updated and tests are passing.
As a side note on
__dict__: it can be a useful positive hint, but it’s not complete. For example,__name__exists on a type but won’t show up in its__dict__:Thanks again for the review and pointers!
gh-139819: rlcompleter – avoid suggesting attributes not accessible o…
pythongh-139819: rlcompleter – avoid suggesting attributes not access…
Merge branch 'main' into pythongh-139819-sentinel
Merge branch 'main' into pythongh-139819-sentinel
gh-139819: Use builtin `sentinel` in `rlcompleter` (GH-151222)
pythongh-139819: rlcompleter – avoid suggesting attributes not access…
pythongh-139819: Use builtin `sentinel` in `rlcompleter` (pythonGH-15…