]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-44752: Make rlcompleter not call `@property` methods (GH-27401)
authorJack DeVries <58614260+jdevries3133@users.noreply.github.com>
Thu, 29 Jul 2021 11:40:29 +0000 (07:40 -0400)
committerGitHub <noreply@github.com>
Thu, 29 Jul 2021 11:40:29 +0000 (13:40 +0200)
* rlcompleter was calling these methods to identify whether to add
  parenthesis to the completion, based on if the attribute is callable.
* for property objects, completion with parenthesis are never desirable.
* property methods with print statements behaved very strangely, which
  was especially unfriendly to language newcomers. <tab> could suddenly
  produce output unexpectedly.

Lib/rlcompleter.py
Lib/test/test_rlcompleter.py
Misc/NEWS.d/next/Library/2021-07-27-22-11-29.bpo-44752._bvbrZ.rst [new file with mode: 0644]

index c06388e8d9c2dd8d5f2c113f838234f9620c3aeb..34b259916e8e7286efd02f14bac19720aaa343c0 100644 (file)
@@ -176,6 +176,16 @@ class Completer:
                 if (word[:n] == attr and
                     not (noprefix and word[:n+1] == noprefix)):
                     match = "%s.%s" % (expr, word)
+                    if isinstance(getattr(type(thisobject), word, None),
+                                  property):
+                        # bpo-44752: thisobject.word is a method decorated by
+                        # `@property`. What follows applies a postfix if
+                        # thisobject.word is callable, but know we know that
+                        # this is not callable (because it is a property).
+                        # Also, getattr(thisobject, word) will evaluate the
+                        # property method, which is not desirable.
+                        matches.append(match)
+                        continue
                     try:
                         val = getattr(thisobject, word)
                     except Exception:
index ee3019d8782d17f88f0b3cbfa91d5a3a4880e3b7..1f7a6ed3f639e06374fb2de956f8e222bd83d4b0 100644 (file)
@@ -81,17 +81,41 @@ class TestRlcompleter(unittest.TestCase):
                               if x.startswith('s')])
 
     def test_excessive_getattr(self):
-        # Ensure getattr() is invoked no more than once per attribute
+        """Ensure getattr() is invoked no more than once per attribute"""
+
+        # note the special case for @property methods below; that is why
+        # 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:
             calls = 0
+            bar = ''
+            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)
+
+    def test_property_method_not_called(self):
+        class Foo:
+            _bar = 0
+            property_called = False
+
             @property
             def bar(self):
-                self.calls += 1
-                return None
+                self.property_called = True
+                return self._bar
+
         f = Foo()
         completer = rlcompleter.Completer(dict(f=f))
         self.assertEqual(completer.complete('f.b', 0), 'f.bar')
-        self.assertEqual(f.calls, 1)
+        self.assertFalse(f.property_called)
+
 
     def test_uncreated_attr(self):
         # Attributes like properties and slots should be completed even when
diff --git a/Misc/NEWS.d/next/Library/2021-07-27-22-11-29.bpo-44752._bvbrZ.rst b/Misc/NEWS.d/next/Library/2021-07-27-22-11-29.bpo-44752._bvbrZ.rst
new file mode 100644 (file)
index 0000000..0d8a2cd
--- /dev/null
@@ -0,0 +1,2 @@
+:mod:`rcompleter` does not call :func:`getattr` on :class:`property` objects
+to avoid the side-effect of  evaluating the corresponding method.