changeset: 99320:d3a9b055206c branch: 2.7 parent: 99312:09a8ac75b351 user: Martin Panter date: Mon Nov 23 23:50:26 2015 +0000 files: Lib/rlcompleter.py Lib/test/test_rlcompleter.py Misc/NEWS description: Issue #25663: Make rlcompleter avoid duplicate global names diff -r 09a8ac75b351 -r d3a9b055206c Lib/rlcompleter.py --- a/Lib/rlcompleter.py Mon Nov 23 15:46:36 2015 +0200 +++ b/Lib/rlcompleter.py Mon Nov 23 23:50:26 2015 +0000 @@ -102,13 +102,16 @@ """ import keyword matches = [] + seen = {"__builtins__"} n = len(text) for word in keyword.kwlist: if word[:n] == text: + seen.add(word) matches.append(word) - for nspace in [__builtin__.__dict__, self.namespace]: + for nspace in [self.namespace, __builtin__.__dict__]: for word, val in nspace.items(): - if word[:n] == text and word != "__builtins__": + if word[:n] == text and word not in seen: + seen.add(word) matches.append(self._callable_postfix(val, word)) return matches diff -r 09a8ac75b351 -r d3a9b055206c Lib/test/test_rlcompleter.py --- a/Lib/test/test_rlcompleter.py Mon Nov 23 15:46:36 2015 +0200 +++ b/Lib/test/test_rlcompleter.py Mon Nov 23 23:50:26 2015 +0000 @@ -81,6 +81,27 @@ def test_main(): support.run_unittest(TestRlcompleter) + def test_duplicate_globals(self): + namespace = { + 'False': None, # Keyword vs builtin vs namespace + 'assert': None, # Keyword vs namespace + 'try': lambda: None, # Keyword vs callable + 'memoryview': None, # Callable builtin vs non-callable + 'Ellipsis': lambda: None, # Non-callable builtin vs callable + } + completer = rlcompleter.Completer(namespace) + self.assertEqual(completer.complete('False', 0), 'False') + self.assertIsNone(completer.complete('False', 1)) # No duplicates + self.assertEqual(completer.complete('assert', 0), 'assert') + self.assertIsNone(completer.complete('assert', 1)) + self.assertEqual(completer.complete('try', 0), 'try') + self.assertIsNone(completer.complete('try', 1)) + # No opening bracket "(" because we overrode the built-in class + self.assertEqual(completer.complete('memoryview', 0), 'memoryview') + self.assertIsNone(completer.complete('memoryview', 1)) + self.assertEqual(completer.complete('Ellipsis', 0), 'Ellipsis(') + self.assertIsNone(completer.complete('Ellipsis', 1)) + if __name__ == '__main__': test_main() diff -r 09a8ac75b351 -r d3a9b055206c Misc/NEWS --- a/Misc/NEWS Mon Nov 23 15:46:36 2015 +0200 +++ b/Misc/NEWS Mon Nov 23 23:50:26 2015 +0000 @@ -13,6 +13,9 @@ Library ------- +- Issue #25663: In the Readline completer, avoid listing duplicate global + names, and search the global namespace before searching builtins. + - Issue #25688: Fixed file leak in ElementTree.iterparse() raising an error. - Issue #23914: Fixed SystemError raised by CPickle unpickler on broken data.