Index: Lib/rlcompleter.py =================================================================== --- Lib/rlcompleter.py (revisione 83774) +++ Lib/rlcompleter.py (copia locale) @@ -66,7 +66,7 @@ self.use_main_ns = 0 self.namespace = namespace - def complete(self, text, state): + def complete(self, text, state, use_builtins=True): """Return the next possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it @@ -78,9 +78,9 @@ if state == 0: if "." in text: - self.matches = self.attr_matches(text) + self.matches = self.attr_matches(text, use_builtins) else: - self.matches = self.global_matches(text) + self.matches = self.global_matches(text, use_builtins) try: return self.matches[state] except IndexError: @@ -91,7 +91,7 @@ word = word + "(" return word - def global_matches(self, text): + def global_matches(self, text, use_builtins=True): """Compute matches when text is a simple name. Return a list of all keywords, built-in functions and names currently @@ -104,13 +104,18 @@ for word in keyword.kwlist: if word[:n] == text: matches.append(word) - for nspace in [builtins.__dict__, self.namespace]: + + nspaces = [self.namespace] + if use_builtins: + nspaces.append(builtins.__dict__) + + for nspace in nspaces: for word, val in nspace.items(): if word[:n] == text and word != "__builtins__": matches.append(self._callable_postfix(val, word)) return matches - def attr_matches(self, text): + def attr_matches(self, text, use_builtins=True): """Compute matches when text contains a dot. Assuming the text is of the form NAME.NAME....[NAME], and is @@ -122,14 +127,27 @@ with a __getattr__ hook is evaluated. """ - import re - m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) - if not m: + base, *attrs = text.split('.') + + if not base or not attrs or any(not x for x in attrs): return [] - expr, attr = m.group(1, 3) + + if base in self.namespace: + thisobject = self.namespace[base] + elif use_builtins and base in builtins.__dict__: + thisobject = builtins.__dict__[base] + else: + return [] + + # Go deeply through object try: - thisobject = eval(expr, self.namespace) - except Exception: + nextattr = attrs.pop(0) + while attrs: + thisobject = getattr(thisobject, nextattr) + base += '.' + nextattr + nextattr = attrs.pop(0) + attr = nextattr + except AttributeError: return [] # get the content of the object, except __builtins__ @@ -145,7 +163,7 @@ for word in words: if word[:n] == attr and hasattr(thisobject, word): val = getattr(thisobject, word) - word = self._callable_postfix(val, "%s.%s" % (expr, word)) + word = self._callable_postfix(val, "%s.%s" % (base, word)) matches.append(word) return matches Index: Lib/test/test_rlcompleter.py =================================================================== --- Lib/test/test_rlcompleter.py (revisione 83774) +++ Lib/test/test_rlcompleter.py (copia locale) @@ -42,7 +42,6 @@ ['CompleteMe(']) self.assertEqual(self.completer.global_matches('eg'), ['egg(']) - # XXX: see issue5256 self.assertEqual(self.completer.global_matches('CompleteM'), ['CompleteMe(']) @@ -65,6 +64,26 @@ ['egg.{}('.format(x) for x in dir(str) if x.startswith('s')]) + def test_use_builtins(self): + """ Provide a set of tests for use_builtins option in attr_matches and + global_matches. See issue5256. """ + + # tests with global_matches + self.assertEqual(self.completer.global_matches('int'), ['int(']) + res = self.completer.global_matches('eva', use_builtins=False) + self.assertNotEqual(res, ['eval(']) + + # tests with attr_matches + self.assertNotEqual(self.completer.attr_matches('str.s', False), + ['str.{}('.format(x) for x in dir(str) + if x.startswith('s')]) + self.assertEqual(self.completer.attr_matches('int.deno'), + ['int.denominator']) + self.assertEqual(self.completer.attr_matches('.spam.f', False), []) + self.assertEqual(self.completer.attr_matches('foo.bar.', True), []) + self.assertEqual(self.stdcompleter.attr_matches('int.egg.spam'), []) + self.assertEqual(self.completer.attr_matches(''), []) + def test_main(): support.run_unittest(TestRlcompleter)