--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DebugClients/Python/FlexCompleter.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,247 @@ +# -*- coding: utf-8 -*- + +""" +Word completion for the eric4 shell + +<h4>NOTE for eric4 variant</h4> + + This version is a re-implementation of FlexCompleter + as found in the PyQwt package. It is modified to work with the eric4 debug + clients. + + +<h4>NOTE for the PyQwt variant</h4> + + This version is a re-implementation of FlexCompleter + with readline support for PyQt&sip-3.6 and earlier. + + Full readline support is present in PyQt&sip-snapshot-20030531 and later. + + +<h4>NOTE for FlexCompleter</h4> + + This version is a re-implementation of rlcompleter with + selectable namespace. + + The problem with rlcompleter is that it's hardwired to work with + __main__.__dict__, and in some cases one may have 'sandboxed' namespaces. So + this class is a ripoff of rlcompleter, with the namespace to work in as an + optional parameter. + + This class can be used just like rlcompleter, but the Completer class now has + a constructor with the optional 'namespace' parameter. + + A patch has been submitted to Python@sourceforge for these changes to go in + the standard Python distribution. + + +<h4>Original rlcompleter documentation</h4> + + This requires the latest extension to the readline module (the + completes keywords, built-ins and globals in __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 string" type "string.", hit the + completion key (twice), and see the list of names defined by the + string module! + + Tip: to use the tab key as the completion key, call + + 'readline.parse_and_bind("tab: complete")' + + <b>Notes</b>: + <ul> + <li> + 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. + </li> + <li> + 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. + </li> + <li> + GNU readline is also used by the built-in functions input() and + raw_input(), and thus these also benefit/suffer from the completer + features. Clearly an interactive application can benefit by + specifying its own completer function and using raw_input() for all + its input. + </li> + <li> + When the original stdin is not a tty device, GNU readline is never + used, and this module (and the readline module) are silently inactive. + </li> + </ul> +""" + +#***************************************************************************** +# +# Since this file is essentially a minimally modified copy of the rlcompleter +# module which is part of the standard Python distribution, I assume that the +# proper procedure is to maintain its copyright as belonging to the Python +# Software Foundation: +# +# Copyright (C) 2001 Python Software Foundation, www.python.org +# +# Distributed under the terms of the Python Software Foundation license. +# +# Full text available at: +# +# http://www.python.org/2.1/license.html +# +#***************************************************************************** + +import __builtin__ +import __main__ + +__all__ = ["Completer"] + +class Completer(object): + """ + Class implementing the command line completer object. + """ + 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) + + @param namespace The namespace for the completer. + """ + + if namespace and type(namespace) != type({}): + 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'. + + @param text The text to be completed. (string) + @param state The state of the completion. (integer) + @return The possible completions as a list of strings. + """ + 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 global_matches(self, text): + """ + Compute matches when text is a simple name. + + @param text The text to be completed. (string) + @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 list in [keyword.kwlist, + __builtin__.__dict__.keys(), + self.namespace.keys()]: + for word in list: + if word[:n] == text and word != "__builtins__" and not word in matches: + matches.append(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 + evaluatable 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 are also considered.) + + <b>WARNING</b>: this can still invoke arbitrary C code, if an object + with a __getattr__ hook is evaluated. + + @param text The text to be completed. (string) + @return A list of all matches. + """ + import re + + # Testing. This is the original code: + #m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text) + + # Modified to catch [] in expressions: + #m = re.match(r"([\w\[\]]+(\.[\w\[\]]+)*)\.(\w*)", text) + + # Another option, seems to work great. Catches things like ''.<tab> + m = re.match(r"(\S+(\.\w+)*)\.(\w*)", text) + + if not m: + return + expr, attr = m.group(1, 3) + object = eval(expr, self.namespace) + words = dir(object) + if hasattr(object,'__class__'): + words.append('__class__') + words = words + get_class_members(object.__class__) + matches = [] + n = len(attr) + for word in words: + try: + if word[:n] == attr and word != "__builtins__": + match = "%s.%s" % (expr, word) + if not match in matches: + matches.append(match) + except: + # some badly behaved objects pollute dir() with non-strings, + # which cause the completion to fail. This way we skip the + # bad entries and can still continue processing the others. + pass + return matches + +def get_class_members(klass): + """ + Module function to retrieve the class members. + + @param klass The class object to be analysed. + @return A list of all names defined in the class. + """ + # PyQwt's hack for PyQt&sip-3.6 and earlier + if hasattr(klass, 'getLazyNames'): + return klass.getLazyNames() + # vanilla Python stuff + ret = dir(klass) + if hasattr(klass,'__bases__'): + for base in klass.__bases__: + ret = ret + get_class_members(base) + return ret