summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Panter <vadmium+py@gmail.com>2015-11-23 23:50:26 (GMT)
committerMartin Panter <vadmium+py@gmail.com>2015-11-23 23:50:26 (GMT)
commited92910852ad7a3006bc264ef6cb061868e5a82c (patch)
tree8b56a31fff59f7fffbdb0f5cb5218025eda534ca
parentbf7b9ede1a7264009f5f35d357112ed3c3c38209 (diff)
downloadcpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.zip
cpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.tar.gz
cpython-ed92910852ad7a3006bc264ef6cb061868e5a82c.tar.bz2
Issue #25663: Make rlcompleter avoid duplicate global names
-rw-r--r--Lib/rlcompleter.py7
-rw-r--r--Lib/test/test_rlcompleter.py21
-rw-r--r--Misc/NEWS3
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()
diff --git a/Misc/NEWS b/Misc/NEWS
index e336023..c98cd96 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.