summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/rlcompleter.py108
1 files changed, 108 insertions, 0 deletions
diff --git a/Lib/rlcompleter.py b/Lib/rlcompleter.py
new file mode 100644
index 0000000..285faed
--- /dev/null
+++ b/Lib/rlcompleter.py
@@ -0,0 +1,108 @@
+"""Word completion for GNU readline 2.0.
+
+This requires the latest extension to the readline module (the
+set_completer() function). When completing a simple identifier, it
+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")
+
+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.
+
+- 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.
+
+- 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 readline
+import keyword
+import __builtin__
+import __main__
+import string
+import re
+import traceback
+
+class Completer:
+
+ 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 state == 0:
+ if "." in text:
+ self.matches = self.attr_matches(text)
+ else:
+ self.matches = self.global_matches(text)
+ return self.matches[state]
+
+ 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 defines in __main__ that match.
+
+ """
+ matches = []
+ n = len(text)
+ for list in [keyword.kwlist,
+ __builtin__.__dict__.keys(),
+ __main__.__dict__.keys()]:
+ for word in list:
+ if word[:n] == text:
+ 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
+ evaluabable in the globals of __main__, it will be evaluated
+ and its attributes (as revealed by dir()) are used as possible
+ completions.
+
+ WARNING: this can still invoke arbitrary C code, if an object
+ with a __getattr__ hook is evaluated.
+
+ """
+ m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
+ if not m:
+ return
+ expr, attr = m.group(1, 3)
+ words = dir(eval(expr, __main__.__dict__))
+ matches = []
+ n = len(attr)
+ for word in words:
+ if word[:n] == attr:
+ matches.append("%s.%s" % (expr, word))
+ return matches
+
+readline.set_completer(Completer().complete)