Commit ed929108 authored by Martin Panter's avatar Martin Panter

Issue #25663: Make rlcompleter avoid duplicate global names

parent bf7b9ede
...@@ -103,13 +103,16 @@ class Completer: ...@@ -103,13 +103,16 @@ class Completer:
""" """
import keyword import keyword
matches = [] matches = []
seen = {"__builtins__"}
n = len(text) n = len(text)
for word in keyword.kwlist: for word in keyword.kwlist:
if word[:n] == text: if word[:n] == text:
seen.add(word)
matches.append(word) matches.append(word)
for nspace in [builtins.__dict__, self.namespace]: for nspace in [self.namespace, builtins.__dict__]:
for word, val in nspace.items(): 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)) matches.append(self._callable_postfix(val, word))
return matches return matches
......
...@@ -85,6 +85,27 @@ class TestRlcompleter(unittest.TestCase): ...@@ -85,6 +85,27 @@ class TestRlcompleter(unittest.TestCase):
self.assertEqual(completer.complete('as', 2), 'assert') self.assertEqual(completer.complete('as', 2), 'assert')
self.assertEqual(completer.complete('an', 0), 'and') self.assertEqual(completer.complete('an', 0), 'and')
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__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -106,6 +106,9 @@ Core and Builtins ...@@ -106,6 +106,9 @@ Core and Builtins
Library 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 #25688: Fixed file leak in ElementTree.iterparse() raising an error.
- Issue #23914: Fixed SystemError raised by unpickler on broken pickle data. - Issue #23914: Fixed SystemError raised by unpickler on broken pickle data.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment