diff -r a094eee9f862 -r b7fe69c6cb1c DebugClients/Python2/FlexCompleter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DebugClients/Python2/FlexCompleter.py Sat Sep 03 18:12:12 2016 +0200 @@ -0,0 +1,275 @@ +# -*- coding: utf-8 -*- + +""" +Word completion for the eric6 shell. + +<h4>NOTE for eric6 variant</h4> + + This version is a re-implementation of FlexCompleter + as found in the PyQwt package. It is modified to work with the eric6 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): + """ + Constructor + + 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 namespace for the completer + @exception TypeError raised to indicate a wrong namespace structure + """ + 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): + """ + Public method to 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 _callable_postfix(self, val, word): + """ + Protected method to check for a callable. + + @param val value to check (object) + @param word word to ammend (string) + @return ammended word (string) + """ + if hasattr(val, '__call__'): + word = word + "(" + return word + + def global_matches(self, text): + """ + Public method to 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 word in keyword.kwlist: + if word[:n] == text: + matches.append(word) + for nspace in [__builtin__.__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): + """ + Public method to 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) + 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(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 hasattr(thisobject, word): + val = getattr(thisobject, word) + word = self._callable_postfix( + val, "%s.%s" % (expr, word)) + matches.append(word) + except Exception: + # 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 + +# +# eflag: FileType = Python2 +# eflag: noqa = M601, M702, M111