| """Word completion for GNU readline. |
| |
| The completer completes keywords, built-ins and globals in a selectable |
| namespace (which defaults to __main__); when completing NAME.NAME..., it |
| evaluates (!) the expression up to the last dot and completes its attributes. |
| |
| It's very cool to do "import sys" type "sys.", hit the completion key (twice), |
| and see the list of names defined by the sys module! |
| |
| Tip: to use the tab key as the completion key, call |
| |
| readline.parse_and_bind("tab: complete") |
| |
| Notes: |
| |
| - Exceptions raised by the completer function are *ignored* (and generally cause |
| the completion to fail). This is a feature -- since readline sets the tty |
| device in raw (or cbreak) mode, printing a traceback wouldn't work well |
| without some complicated hoopla to save, reset and restore the tty state. |
| |
| - The evaluation of the NAME.NAME... form may cause arbitrary application |
| defined code to be executed if an object with a __getattr__ hook is found. |
| Since it is the responsibility of the application (or the user) to enable this |
| feature, I consider this an acceptable risk. More complicated expressions |
| (e.g. function calls or indexing operations) are *not* evaluated. |
| |
| - When the original stdin is not a tty device, GNU readline is never |
| used, and this module (and the readline module) are silently inactive. |
| |
| """ |
| |
| import builtins |
| import __main__ |
| |
| __all__ = ["Completer"] |
| |
| class Completer: |
| def __init__(self, namespace = None): |
| """Create a new completer for the command line. |
| |
| Completer([namespace]) -> completer instance. |
| |
| If unspecified, the default namespace where completions are performed |
| is __main__ (technically, __main__.__dict__). Namespaces should be |
| given as dictionaries. |
| |
| Completer instances should be used as the completion mechanism of |
| readline via the set_completer() call: |
| |
| readline.set_completer(Completer(my_namespace).complete) |
| """ |
| |
| if namespace and not isinstance(namespace, dict): |
| raise TypeError('namespace must be a dictionary') |
| |
| # Don't bind to namespace quite yet, but flag whether the user wants a |
| # specific namespace or to use __main__.__dict__. This will allow us |
| # to bind to __main__.__dict__ at completion time, not now. |
| if namespace is None: |
| self.use_main_ns = 1 |
| else: |
| self.use_main_ns = 0 |
| self.namespace = namespace |
| |
| def complete(self, text, state): |
| """Return the next possible completion for 'text'. |
| |
| This is called successively with state == 0, 1, 2, ... until it |
| returns None. The completion should begin with 'text'. |
| |
| """ |
| if self.use_main_ns: |
| self.namespace = __main__.__dict__ |
| |
| if state == 0: |
| if "." in text: |
| self.matches = self.attr_matches(text) |
| else: |
| self.matches = self.global_matches(text) |
| try: |
| return self.matches[state] |
| except IndexError: |
| return None |
| |
| def _callable_postfix(self, val, word): |
| if callable(val): |
| word = word + "(" |
| return word |
| |
| def global_matches(self, text): |
| """Compute matches when text is a simple name. |
| |
| Return a list of all keywords, built-in functions and names currently |
| defined in self.namespace that match. |
| |
| """ |
| import keyword |
| matches = [] |
| n = len(text) |
| for word in keyword.kwlist: |
| if word[:n] == text: |
| matches.append(word) |
| for nspace in [builtins.__dict__, self.namespace]: |
| 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): |
| """Compute matches when text contains a dot. |
| |
| Assuming the text is of the form NAME.NAME....[NAME], and is |
| evaluable in self.namespace, it will be evaluated and its attributes |
| (as revealed by dir()) are used as possible completions. (For class |
| instances, class members are also considered.) |
| |
| WARNING: this can still invoke arbitrary C code, if an object |
| with a __getattr__ hook is evaluated. |
| |
| """ |
| import re |
| m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) |
| if not m: |
| return [] |
| expr, attr = m.group(1, 3) |
| try: |
| thisobject = eval(expr, self.namespace) |
| except Exception: |
| return [] |
| |
| # get the content of the object, except __builtins__ |
| words = dir(thisobject) |
| if "__builtins__" in words: |
| words.remove("__builtins__") |
| |
| if hasattr(thisobject, '__class__'): |
| words.append('__class__') |
| words.extend(get_class_members(thisobject.__class__)) |
| matches = [] |
| n = len(attr) |
| 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)) |
| matches.append(word) |
| return matches |
| |
| def get_class_members(klass): |
| ret = dir(klass) |
| if hasattr(klass,'__bases__'): |
| for base in klass.__bases__: |
| ret = ret + get_class_members(base) |
| return ret |
| |
| try: |
| import readline |
| except ImportError: |
| pass |
| else: |
| readline.set_completer(Completer().complete) |