--- ../orig/rlcompleter.py 2008-07-21 18:32:10.000000000 +0200 +++ rlcompleter.py 2010-11-09 10:55:07.631596001 +0100 @@ -79,6 +79,8 @@ if state == 0: if "." in text: self.matches = self.attr_matches(text) + elif "[" in text: + self.matches = self.dict_key_matches(text) else: self.matches = self.global_matches(text) try: @@ -149,6 +151,47 @@ matches.append(word) return matches + def dict_key_matches(self, text): + """Compute matches when text contains a [. + + The evaluation of the part before the '[' could be enhanced. + """ + + import re + m = re.match(r"(\w+(\.\w+)*)\[(([0-9]+)|((u?)([\'\"])([^\'\"\]]*))|(.+))?", text) + + if not m: + return [] + expr, int_num, literal_prefix, quote, key_start, not_valid = m.group(1, 4, 6, 7, 8, 9) + + try: + thisobject = eval(expr, self.namespace) + except Exception: + return [] + + matches = [] + + if int_num: + num_keys = (k for k in thisobject.keys() if isinstance(k, int)) + for k in num_keys: + if ('%d' % k).startswith(int_num): + matches.append("%s[%d]" % (expr, k)) + elif key_start != None: + char_keys = [k for k in thisobject.keys() if isinstance(k, str)] + for k in char_keys: + if k.startswith(key_start): + matches.append("%s[%s%s%s%s]" % (expr, literal_prefix, quote, k, quote)) + elif not_valid: + ## any actions on invalid input? + pass + else: + for k in thisobject.keys(): + if isinstance(k, str): + matches.append("%s[\'%s\']" % (expr, k)) + else: + matches.append("%s[%s]" % (expr, k)) + return matches + def get_class_members(klass): ret = dir(klass) if hasattr(klass,'__bases__'): --- ../orig/test_rlcompleter.py 2010-10-21 09:40:03.000000000 +0200 +++ test_rlcompleter.py 2010-11-09 11:04:13.421596002 +0100 @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- from test import support import unittest import builtins @@ -8,12 +9,19 @@ spam = 1 +# A trivial dicts used in testing rlcompleter.Completer +DictCompleteMe = { 'spam' : 1, + 1984 : 42, + 'öh, вау!' : 'yep world is not ascii only'} + + class TestRlcompleter(unittest.TestCase): def setUp(self): self.stdcompleter = rlcompleter.Completer() self.completer = rlcompleter.Completer(dict(spam=int, - egg=str, - CompleteMe=CompleteMe)) + egg=str, + CompleteMe=CompleteMe, + DictCompleteMe=DictCompleteMe)) # forces stdcompleter to bind builtins namespace self.stdcompleter.complete('', 0) @@ -46,6 +54,7 @@ self.assertEqual(self.completer.global_matches('CompleteM'), ['CompleteMe(']) + def test_attr_matches(self): # test with builtins namespace self.assertEqual(self.stdcompleter.attr_matches('str.s'), @@ -65,6 +74,39 @@ ['egg.{}('.format(x) for x in dir(str) if x.startswith('s')]) + + def test_dict_key_matches(self): + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe['), + ['DictCompleteMe[1984]', + 'DictCompleteMe[\'öh, вау!\']', + 'DictCompleteMe[\'spam\']']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[1'), + ['DictCompleteMe[1984]']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[2'), + []) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[s'), + []) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[\''), + ['DictCompleteMe[\'öh, вау!\']', + 'DictCompleteMe[\'spam\']']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[\'s'), + ['DictCompleteMe[\'spam\']']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[\"s'), + ['DictCompleteMe[\"spam\"]']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[\'ö'), + ['DictCompleteMe[\'öh, вау!\']']) + + self.assertEqual(self.completer.dict_key_matches('DictCompleteMe[\''), + ['DictCompleteMe[\'öh, вау!\']', + "DictCompleteMe['spam']"]) + def test_main(): support.run_unittest(TestRlcompleter)