]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-139819: rlcompleter – avoid suggesting attributes not accessible on instances...
authorPeter <peter86225@gmail.com>
Tue, 9 Jun 2026 18:00:23 +0000 (02:00 +0800)
committerGitHub <noreply@github.com>
Tue, 9 Jun 2026 18:00:23 +0000 (11:00 -0700)
Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
Lib/rlcompleter.py
Lib/test/test_rlcompleter.py
Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst [new file with mode: 0644]

index 6c6d9bb6b34244ea1ae6bbe6dff181520162de39..271b77a322fdd2bde530623b14d06d8e560eaa2f 100644 (file)
@@ -40,6 +40,9 @@ import types
 
 __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.
@@ -194,14 +197,14 @@ class Completer:
                         and
                         isinstance(thisobject.__dict__.get(word),
                                    types.LazyImportType)
-                    ):
+                       ):
                         value = thisobject.__dict__.get(word)
                     else:
-                        value = getattr(thisobject, word, None)
+                        value = getattr(thisobject, word, _SENTINEL)
 
-                    if value is not None:
+                    if value 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
index e6d727d417b2985c5321aac7b7a1efbe21a52a74..c0b5a4da8cb25695e714d7b776780afb0080ca3a 100644 (file)
@@ -107,19 +107,35 @@ class TestRlcompleter(unittest.TestCase):
         # we use __dir__ and __getattr__ in class Foo to create a "magic"
         # class attribute 'bar'. This forces `getattr` to call __getattr__
         # (which is doesn't necessarily do).
-        class Foo:
+        # Test 1: Attribute returns None
+        class FooReturnsNone:
             calls = 0
-            bar = ''
+            bar = None
             def __getattribute__(self, name):
                 if name == 'bar':
                     self.calls += 1
                     return None
                 return super().__getattribute__(name)
 
-        f = Foo()
-        completer = rlcompleter.Completer(dict(f=f))
-        self.assertEqual(completer.complete('f.b', 0), 'f.bar')
-        self.assertEqual(f.calls, 1)
+        f1 = FooReturnsNone()
+        completer1 = rlcompleter.Completer(dict(f=f1))
+        self.assertEqual(completer1.complete('f.b', 0), 'f.bar')
+        self.assertEqual(f1.calls, 1)
+
+        # Test 2: Attribute returns non-None value
+        class FooReturnsValue:
+            calls = 0
+            bar = ''
+            def __getattribute__(self, name):
+                if name == 'bar':
+                    self.calls += 1
+                    return ''
+                return super().__getattribute__(name)
+
+        f2 = FooReturnsValue()
+        completer2 = rlcompleter.Completer(dict(f=f2))
+        self.assertEqual(completer2.complete('f.b', 0), 'f.bar')
+        self.assertEqual(f2.calls, 1)
 
     def test_property_method_not_called(self):
         class Foo:
@@ -196,6 +212,7 @@ class TestRlcompleter(unittest.TestCase):
         completer = rlcompleter.Completer(dict(f=Foo()))
         self.assertEqual(completer.complete('f.', 0), 'f.bar')
 
+
     @unittest.mock.patch('rlcompleter._readline_available', False)
     def test_complete(self):
         completer = rlcompleter.Completer()
diff --git a/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst b/Misc/NEWS.d/next/Library/2025-10-09-12-13-29.gh-issue-139819.YxUDyH.rst
new file mode 100644 (file)
index 0000000..ab3a8c6
--- /dev/null
@@ -0,0 +1,3 @@
+:mod:`rlcompleter`: Avoid suggesting attributes that are not accessible on
+instances (e.g., Enum members showing ``__name__``). Patch by Peter
+(ttw225).