| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | # Copyright (c) 2009 www.stani.be (GPL license) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | # it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | # the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | # (at your option) any later version. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | # along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-12-13 14:38:30 +00:00
										 |  |  | # <pep8-80 compliant> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | """Autocomplete with the standard library""" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | import rlcompleter | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | RE_INCOMPLETE_INDEX = re.compile('(.*?)\[[^\]]+$') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | TEMP = '__tEmP__'  # only \w characters are allowed! | 
					
						
							|  |  |  | TEMP_N = len(TEMP) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  | def is_dict(obj): | 
					
						
							|  |  |  |     """Returns whether obj is a dictionary""" | 
					
						
							|  |  |  |     return hasattr(obj, 'keys') and hasattr(getattr(obj, 'keys'), '__call__') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def complete_names(word, namespace): | 
					
						
							|  |  |  |     """Complete variable names or attributes
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param word: word to be completed | 
					
						
							|  |  |  |     :type word: str | 
					
						
							|  |  |  |     :param namespace: namespace | 
					
						
							|  |  |  |     :type namespace: dict | 
					
						
							|  |  |  |     :returns: completion matches | 
					
						
							|  |  |  |     :rtype: list of str | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     >>> complete_names('fo', {'foo': 'bar'}) | 
					
						
							|  |  |  |     ['foo', 'for', 'format('] | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     # start completer | 
					
						
							|  |  |  |     completer = rlcompleter.Completer(namespace) | 
					
						
							|  |  |  |     # find matches with std library (don't try to implement this yourself) | 
					
						
							|  |  |  |     completer.complete(word, 0) | 
					
						
							|  |  |  |     return sorted(set(completer.matches)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def complete_indices(word, namespace, obj=None, base=None): | 
					
						
							|  |  |  |     """Complete a list or dictionary with its indices:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     * integer numbers for list | 
					
						
							|  |  |  |     * any keys for dictionary | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param word: word to be completed | 
					
						
							|  |  |  |     :type word: str | 
					
						
							|  |  |  |     :param namespace: namespace | 
					
						
							|  |  |  |     :type namespace: dict | 
					
						
							|  |  |  |     :param obj: object evaluated from base | 
					
						
							|  |  |  |     :param base: substring which can be evaluated into an object | 
					
						
							|  |  |  |     :type base: str | 
					
						
							|  |  |  |     :returns: completion matches | 
					
						
							|  |  |  |     :rtype: list of str | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     >>> complete_indices('foo', {'foo': range(5)}) | 
					
						
							|  |  |  |     ['foo[0]', 'foo[1]', 'foo[2]', 'foo[3]', 'foo[4]'] | 
					
						
							|  |  |  |     >>> complete_indices('foo', {'foo': {'bar':0, 1:2}}) | 
					
						
							|  |  |  |     ['foo[1]', "foo['bar']"] | 
					
						
							|  |  |  |     >>> complete_indices("foo['b", {'foo': {'bar':0, 1:2}}, base='foo') | 
					
						
							|  |  |  |     ["foo['bar']"] | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     #FIXME: 'foo["b' | 
					
						
							|  |  |  |     if base is None: | 
					
						
							|  |  |  |         base = word | 
					
						
							|  |  |  |     if obj is None: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             obj = eval(base, namespace) | 
					
						
							|  |  |  |         except Exception: | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |     if not hasattr(obj, '__getitem__'): | 
					
						
							|  |  |  |         # obj is not a list or dictionary | 
					
						
							|  |  |  |         return [] | 
					
						
							|  |  |  |     if is_dict(obj): | 
					
						
							|  |  |  |         # dictionary type | 
					
						
							|  |  |  |         matches = ['%s[%r]' % (base, key) for key in sorted(obj.keys())] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # list type | 
					
						
							|  |  |  |         matches = ['%s[%d]' % (base, idx) for idx in range(len(obj))] | 
					
						
							|  |  |  |     if word != base: | 
					
						
							|  |  |  |         matches = [match for match in matches if match.startswith(word)] | 
					
						
							|  |  |  |     return matches | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | def complete(word, namespace, private=True): | 
					
						
							|  |  |  |     """Complete word within a namespace with the standard rlcompleter
 | 
					
						
							|  |  |  |     module. Also supports index or key access []. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     :param word: word to be completed | 
					
						
							|  |  |  |     :type word: str | 
					
						
							|  |  |  |     :param namespace: namespace | 
					
						
							|  |  |  |     :type namespace: dict | 
					
						
							|  |  |  |     :param private: whether private attribute/methods should be returned | 
					
						
							|  |  |  |     :type private: bool | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     :returns: completion matches | 
					
						
							|  |  |  |     :rtype: list of str | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     >>> complete('foo[1', {'foo': range(14)}) | 
					
						
							|  |  |  |     ['foo[1]', 'foo[10]', 'foo[11]', 'foo[12]', 'foo[13]'] | 
					
						
							|  |  |  |     >>> complete('foo[0]', {'foo': [range(5)]}) | 
					
						
							|  |  |  |     ['foo[0][0]', 'foo[0][1]', 'foo[0][2]', 'foo[0][3]', 'foo[0][4]'] | 
					
						
							|  |  |  |     >>> complete('foo[0].i', {'foo': [range(5)]}) | 
					
						
							|  |  |  |     ['foo[0].index(', 'foo[0].insert('] | 
					
						
							|  |  |  |     >>> complete('rlcompleter', {'rlcompleter': rlcompleter}) | 
					
						
							|  |  |  |     ['rlcompleter.'] | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     # | 
					
						
							|  |  |  |     # if word is empty -> nothing to complete | 
					
						
							|  |  |  |     if not word: | 
					
						
							|  |  |  |         return [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     re_incomplete_index = RE_INCOMPLETE_INDEX.search(word) | 
					
						
							|  |  |  |     if re_incomplete_index: | 
					
						
							|  |  |  |         # ignore incomplete index at the end, e.g 'a[1' -> 'a' | 
					
						
							|  |  |  |         matches = complete_indices(word, namespace, | 
					
						
							|  |  |  |                     base=re_incomplete_index.group(1)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif not('[' in word): | 
					
						
							|  |  |  |         matches = complete_names(word, namespace) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif word[-1] == ']': | 
					
						
							|  |  |  |         matches = [word] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif '.' in word: | 
					
						
							|  |  |  |         # brackets are normally not allowed -> work around | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |         # remove brackets by using a temp var without brackets | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |         obj, attr = word.rsplit('.', 1) | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             # do not run the obj expression in the console | 
					
						
							|  |  |  |             namespace[TEMP] = eval(obj, namespace) | 
					
						
							|  |  |  |         except Exception: | 
					
						
							|  |  |  |             return [] | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |         matches = complete_names(TEMP + '.' + attr, namespace) | 
					
						
							|  |  |  |         matches = [obj + match[TEMP_N:] for match in matches] | 
					
						
							|  |  |  |         del namespace[TEMP] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |         # safety net, but when would this occur? | 
					
						
							|  |  |  |         return [] | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     if not matches: | 
					
						
							|  |  |  |         return [] | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-30 09:34:57 +00:00
										 |  |  |     # add '.', '('  or '[' if no match has been found | 
					
						
							|  |  |  |     elif len(matches) == 1 and matches[0] == word: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # try to retrieve the object | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             obj = eval(word, namespace) | 
					
						
							|  |  |  |         except Exception: | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |         # ignore basic types | 
					
						
							|  |  |  |         if type(obj) in (bool, float, int, str): | 
					
						
							|  |  |  |             return [] | 
					
						
							|  |  |  |         # an extra char '[', '(' or '.' will be added | 
					
						
							|  |  |  |         if hasattr(obj, '__getitem__'): | 
					
						
							|  |  |  |             # list or dictionary | 
					
						
							|  |  |  |             matches = complete_indices(word, namespace, obj) | 
					
						
							|  |  |  |         elif hasattr(obj, '__call__'): | 
					
						
							|  |  |  |             # callables | 
					
						
							|  |  |  |             matches = [word + '('] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # any other type | 
					
						
							|  |  |  |             matches = [word + '.'] | 
					
						
							| 
									
										
										
										
											2009-10-29 20:55:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # separate public from private | 
					
						
							|  |  |  |     public_matches = [match for match in matches if not('._' in match)] | 
					
						
							|  |  |  |     if private: | 
					
						
							|  |  |  |         private_matches = [match for match in matches if '._' in match] | 
					
						
							|  |  |  |         return public_matches + private_matches | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return public_matches |