This commit was manufactured by cvs2svn to create tag 'r22a4-fork'.
[python/dscho.git] / Lib / cmd.py
blob2a7be430833506ca792ab39cc1fdfc1d540077c4
1 """A generic class to build line-oriented command interpreters.
3 Interpreters constructed with this class obey the following conventions:
5 1. End of file on input is processed as the command 'EOF'.
6 2. A command is parsed out of each line by collecting the prefix composed
7 of characters in the identchars member.
8 3. A command `foo' is dispatched to a method 'do_foo()'; the do_ method
9 is passed a single argument consisting of the remainder of the line.
10 4. Typing an empty line repeats the last command. (Actually, it calls the
11 method `emptyline', which may be overridden in a subclass.)
12 5. There is a predefined `help' method. Given an argument `topic', it
13 calls the command `help_topic'. With no arguments, it lists all topics
14 with defined help_ functions, broken into up to three topics; documented
15 commands, miscellaneous help topics, and undocumented commands.
16 6. The command '?' is a synonym for `help'. The command '!' is a synonym
17 for `shell', if a do_shell method exists.
18 7. If completion is enabled, completing commands will be done automatically,
19 and completing of commands args is done by calling complete_foo() with
20 arguments text, line, begidx, endidx. text is string we are matching
21 against, all returned matches must begin with it. line is the current
22 input line (lstripped), begidx and endidx are the beginning and end
23 indexes of the text being matched, which could be used to provide
24 different completion depending upon which position the argument is in.
26 The `default' method may be overridden to intercept commands for which there
27 is no do_ method.
29 The `completedefault' method may be overridden to intercept completions for
30 commands that have no complete_ method.
32 The data member `self.ruler' sets the character used to draw separator lines
33 in the help messages. If empty, no ruler line is drawn. It defaults to "=".
35 If the value of `self.intro' is nonempty when the cmdloop method is called,
36 it is printed out on interpreter startup. This value may be overridden
37 via an optional argument to the cmdloop() method.
39 The data members `self.doc_header', `self.misc_header', and
40 `self.undoc_header' set the headers used for the help function's
41 listings of documented functions, miscellaneous topics, and undocumented
42 functions respectively.
44 These interpreters use raw_input; thus, if the readline module is loaded,
45 they automatically support Emacs-like command history and editing features.
46 """
48 import string, sys
50 __all__ = ["Cmd"]
52 PROMPT = '(Cmd) '
53 IDENTCHARS = string.ascii_letters + string.digits + '_'
55 class Cmd:
56 prompt = PROMPT
57 identchars = IDENTCHARS
58 ruler = '='
59 lastcmd = ''
60 cmdqueue = []
61 intro = None
62 doc_leader = ""
63 doc_header = "Documented commands (type help <topic>):"
64 misc_header = "Miscellaneous help topics:"
65 undoc_header = "Undocumented commands:"
66 nohelp = "*** No help on %s"
67 use_rawinput = 1
69 def __init__(self, completekey='tab'):
70 if completekey:
71 try:
72 import readline
73 readline.set_completer(self.complete)
74 readline.parse_and_bind(completekey+": complete")
75 except ImportError:
76 pass
78 def cmdloop(self, intro=None):
79 self.preloop()
80 if intro is not None:
81 self.intro = intro
82 if self.intro:
83 print self.intro
84 stop = None
85 while not stop:
86 if self.cmdqueue:
87 line = self.cmdqueue[0]
88 del self.cmdqueue[0]
89 else:
90 if self.use_rawinput:
91 try:
92 line = raw_input(self.prompt)
93 except EOFError:
94 line = 'EOF'
95 else:
96 sys.stdout.write(self.prompt)
97 line = sys.stdin.readline()
98 if not len(line):
99 line = 'EOF'
100 else:
101 line = line[:-1] # chop \n
102 line = self.precmd(line)
103 stop = self.onecmd(line)
104 stop = self.postcmd(stop, line)
105 self.postloop()
107 def precmd(self, line):
108 return line
110 def postcmd(self, stop, line):
111 return stop
113 def preloop(self):
114 pass
116 def postloop(self):
117 pass
119 def parseline(self, line):
120 line = line.strip()
121 if not line:
122 return None, None, line
123 elif line[0] == '?':
124 line = 'help ' + line[1:]
125 elif line[0] == '!':
126 if hasattr(self, 'do_shell'):
127 line = 'shell ' + line[1:]
128 else:
129 return None, None, line
130 i, n = 0, len(line)
131 while i < n and line[i] in self.identchars: i = i+1
132 cmd, arg = line[:i], line[i:].strip()
133 return cmd, arg, line
135 def onecmd(self, line):
136 cmd, arg, line = self.parseline(line)
137 if not line:
138 return self.emptyline()
139 if cmd is None:
140 return self.default(line)
141 self.lastcmd = line
142 if cmd == '':
143 return self.default(line)
144 else:
145 try:
146 func = getattr(self, 'do_' + cmd)
147 except AttributeError:
148 return self.default(line)
149 return func(arg)
151 def emptyline(self):
152 if self.lastcmd:
153 return self.onecmd(self.lastcmd)
155 def default(self, line):
156 print '*** Unknown syntax:', line
158 def completedefault(self, *ignored):
159 return []
161 def completenames(self, text, *ignored):
162 dotext = 'do_'+text
163 return [a[3:] for a in self.get_names() if a.startswith(dotext)]
165 def complete(self, text, state):
166 """Return the next possible completion for 'text'.
168 If a command has not been entered, then complete against command list.
169 Otherwise try to call complete_<command> to get list of completions.
171 if state == 0:
172 import readline
173 origline = readline.get_line_buffer()
174 line = origline.lstrip()
175 stripped = len(origline) - len(line)
176 begidx = readline.get_begidx() - stripped
177 endidx = readline.get_endidx() - stripped
178 if begidx>0:
179 cmd, args, foo = self.parseline(line)
180 if cmd == '':
181 compfunc = self.completedefault
182 else:
183 try:
184 compfunc = getattr(self, 'complete_' + cmd)
185 except AttributeError:
186 compfunc = self.completedefault
187 else:
188 compfunc = self.completenames
189 self.completion_matches = compfunc(text, line, begidx, endidx)
190 try:
191 return self.completion_matches[state]
192 except IndexError:
193 return None
195 def get_names(self):
196 # Inheritance says we have to look in class and
197 # base classes; order is not important.
198 names = []
199 classes = [self.__class__]
200 while classes:
201 aclass = classes[0]
202 if aclass.__bases__:
203 classes = classes + list(aclass.__bases__)
204 names = names + dir(aclass)
205 del classes[0]
206 return names
208 def complete_help(self, *args):
209 return self.completenames(*args)
211 def do_help(self, arg):
212 if arg:
213 # XXX check arg syntax
214 try:
215 func = getattr(self, 'help_' + arg)
216 except:
217 try:
218 doc=getattr(self, 'do_' + arg).__doc__
219 if doc:
220 print doc
221 return
222 except:
223 pass
224 print self.nohelp % (arg,)
225 return
226 func()
227 else:
228 names = self.get_names()
229 cmds_doc = []
230 cmds_undoc = []
231 help = {}
232 for name in names:
233 if name[:5] == 'help_':
234 help[name[5:]]=1
235 names.sort()
236 # There can be duplicates if routines overridden
237 prevname = ''
238 for name in names:
239 if name[:3] == 'do_':
240 if name == prevname:
241 continue
242 prevname = name
243 cmd=name[3:]
244 if help.has_key(cmd):
245 cmds_doc.append(cmd)
246 del help[cmd]
247 elif getattr(self, name).__doc__:
248 cmds_doc.append(cmd)
249 else:
250 cmds_undoc.append(cmd)
251 print self.doc_leader
252 self.print_topics(self.doc_header, cmds_doc, 15,80)
253 self.print_topics(self.misc_header, help.keys(),15,80)
254 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
256 def print_topics(self, header, cmds, cmdlen, maxcol):
257 if cmds:
258 print header
259 if self.ruler:
260 print self.ruler * len(header)
261 (cmds_per_line,junk)=divmod(maxcol,cmdlen)
262 col=cmds_per_line
263 for cmd in cmds:
264 if col==0: print
265 print (("%-"+`cmdlen`+"s") % cmd),
266 col = (col+1) % cmds_per_line
267 print "\n"