diff options
author | Martin Panter <vadmium+py@gmail.com> | 2015-11-23 23:50:26 (GMT) |
---|---|---|
committer | Martin Panter <vadmium+py@gmail.com> | 2015-11-23 23:50:26 (GMT) |
commit | ed92910852ad7a3006bc264ef6cb061868e5a82c (patch) | |
tree | 8b56a31fff59f7fffbdb0f5cb5218025eda534ca | |
parent | bf7b9ede1a7264009f5f35d357112ed3c3c38209 (diff) | |
download | cpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.zip cpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.tar.gz cpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.tar.bz2 |
Issue #25663: Make rlcompleter avoid duplicate global names
-rw-r--r-- | Lib/rlcompleter.py | 7 | ||||
-rw-r--r-- | Lib/test/test_rlcompleter.py | 21 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
3 files changed, 29 insertions, 2 deletions
diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py index be8aee0..378f5aa 100644 --- a/Lib/rlcompleter.py +++ b/Lib/rlcompleter.py @@ -103,13 +103,16 @@ class Completer: """ 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 [builtins.__dict__, self.namespace]: + for nspace in [self.namespace, builtins.__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 --git a/Lib/test/test_rlcompleter.py b/Lib/test/test_rlcompleter.py index 927df34..0b3918b 100644 --- a/Lib/test/test_rlcompleter.py +++ b/Lib/test/test_rlcompleter.py @@ -85,6 +85,27 @@ class TestRlcompleter(unittest.TestCase): self.assertEqual(completer.complete('as', 2), 'assert') 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__': unittest.main() @@ -106,6 +106,9 @@ Core and Builtins 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 unpickler on broken pickle data. |