# HG changeset patch # Parent d1c11a78b43a7a56a7c68211306ca5964afdcadf Issue #25590: Complete attribute names even if they are not yet created diff -r d1c11a78b43a Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Tue Nov 10 19:52:20 2015 +0200 +++ b/Doc/whatsnew/3.6.rst Wed Nov 11 01:58:11 2015 +0000 @@ -120,6 +120,10 @@ with underscores. A space or a colon can be added after completed keyword. (Contributed by Serhiy Storchaka in :issue:`25011` and :issue:`25209`.) +Names of most attributes listed by :func:`dir` are now completed. +Previously, names of properties and slots which were not yet created on +an instance were excluded. (Contributed by Martin Panter in :issue:`25590`.) + urllib.robotparser ------------------ diff -r d1c11a78b43a Lib/rlcompleter.py --- a/Lib/rlcompleter.py Tue Nov 10 19:52:20 2015 +0200 +++ b/Lib/rlcompleter.py Wed Nov 11 01:58:11 2015 +0000 @@ -159,11 +159,15 @@ while True: for word in words: if (word[:n] == attr and - not (noprefix and word[:n+1] == noprefix) and - hasattr(thisobject, word)): - val = getattr(thisobject, word) - word = self._callable_postfix(val, "%s.%s" % (expr, word)) - matches.append(word) + not (noprefix and word[:n+1] == noprefix)): + match = "%s.%s" % (expr, word) + try: + val = getattr(thisobject, word) + except Exception: + pass # Include even if attribute not set + else: + match = self._callable_postfix(val, match) + matches.append(match) if matches or not noprefix: break if noprefix == '_': diff -r d1c11a78b43a Lib/test/test_rlcompleter.py --- a/Lib/test/test_rlcompleter.py Tue Nov 10 19:52:20 2015 +0200 +++ b/Lib/test/test_rlcompleter.py Wed Nov 11 01:58:11 2015 +0000 @@ -79,6 +79,29 @@ ['egg.{}('.format(x) for x in dir(str) if x.startswith('s')]) + def test_excessive_getattr(self): + # Ensure getattr() is invoked no more than once per attribute + class Foo: + calls = 0 + @property + def bar(self): + self.calls += 1 + return None + f = Foo() + completer = rlcompleter.Completer(dict(f=f)) + self.assertEqual(completer.complete('f.b', 0), 'f.bar') + self.assertEqual(f.calls, 1) + + def test_uncreated_attr(self): + # Attributes like properties and slots should be completed even when + # they haven't been created on an instance + class Foo: + @property + def bar(self): + raise AttributeError("Attribute doesn't exist") + completer = rlcompleter.Completer(dict(f=Foo())) + self.assertEqual(completer.complete('f.', 0), 'f.bar') + def test_complete(self): completer = rlcompleter.Completer() self.assertEqual(completer.complete('', 0), '\t') diff -r d1c11a78b43a Misc/NEWS --- a/Misc/NEWS Tue Nov 10 19:52:20 2015 +0200 +++ b/Misc/NEWS Wed Nov 11 01:58:11 2015 +0000 @@ -79,6 +79,10 @@ Library ------- +- Issue #25590: In the Readline completer, only call getattr() once per + attribute. Also complete names of attributes such as properties and slots + which are listed by dir() but not yet created on an instance. + - Issue #25584: Added "escape" to the __all__ list in the glob module. - Issue #25584: Fixed recursive glob() with patterns starting with '\*\*'.