blob: 592990310ba4d9baa137b4803562c75e24c0e86e [file] [log] [blame]
Guido van Rossum4acc25b2000-02-02 15:10:15 +00001"""A generic class to build line-oriented command interpreters.
2
3Interpreters constructed with this class obey the following conventions:
4
51. End of file on input is processed as the command 'EOF'.
62. A command is parsed out of each line by collecting the prefix composed
7 of characters in the identchars member.
83. 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.
104. Typing an empty line repeats the last command. (Actually, it calls the
11 method `emptyline', which may be overridden in a subclass.)
125. 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.
166. The command '?' is a synonym for `help'. The command '!' is a synonym
17 for `shell', if a do_shell method exists.
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000187. 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
Tim Petersab9ba272001-08-09 21:40:30 +000023 indexes of the text being matched, which could be used to provide
Martin v. Löwis66b6e192001-07-28 14:44:03 +000024 different completion depending upon which position the argument is in.
Guido van Rossum4acc25b2000-02-02 15:10:15 +000025
26The `default' method may be overridden to intercept commands for which there
27is no do_ method.
28
Martin v. Löwis66b6e192001-07-28 14:44:03 +000029The `completedefault' method may be overridden to intercept completions for
Tim Petersab9ba272001-08-09 21:40:30 +000030commands that have no complete_ method.
Martin v. Löwis66b6e192001-07-28 14:44:03 +000031
Guido van Rossum4acc25b2000-02-02 15:10:15 +000032The data member `self.ruler' sets the character used to draw separator lines
33in the help messages. If empty, no ruler line is drawn. It defaults to "=".
34
35If the value of `self.intro' is nonempty when the cmdloop method is called,
36it is printed out on interpreter startup. This value may be overridden
37via an optional argument to the cmdloop() method.
38
39The data members `self.doc_header', `self.misc_header', and
40`self.undoc_header' set the headers used for the help function's
41listings of documented functions, miscellaneous topics, and undocumented
42functions respectively.
43
44These interpreters use raw_input; thus, if the readline module is loaded,
45they automatically support Emacs-like command history and editing features.
46"""
Guido van Rossumb53e6781992-01-24 01:12:17 +000047
Guido van Rossumbfb91842001-03-22 21:59:20 +000048import string, sys
Guido van Rossumb53e6781992-01-24 01:12:17 +000049
Skip Montanaroe99d5ea2001-01-20 19:54:20 +000050__all__ = ["Cmd"]
51
Guido van Rossumb53e6781992-01-24 01:12:17 +000052PROMPT = '(Cmd) '
Fred Drake79e75e12001-07-20 19:05:50 +000053IDENTCHARS = string.ascii_letters + string.digits + '_'
Guido van Rossumb53e6781992-01-24 01:12:17 +000054
55class Cmd:
Raymond Hettingeraef22fb2002-05-29 16:18:42 +000056 """A simple framework for writing line-oriented command interpreters.
57
58 These are often useful for test harnesses, administrative tools, and
59 prototypes that will later be wrapped in a more sophisticated interface.
60
61 A Cmd instance or subclass instance is a line-oriented interpreter
62 framework. There is no good reason to instantiate Cmd itself; rather,
63 it's useful as a superclass of an interpreter class you define yourself
64 in order to inherit Cmd's methods and encapsulate action methods.
65
66 """
Guido van Rossum4acc25b2000-02-02 15:10:15 +000067 prompt = PROMPT
68 identchars = IDENTCHARS
69 ruler = '='
70 lastcmd = ''
Guido van Rossum4acc25b2000-02-02 15:10:15 +000071 intro = None
72 doc_leader = ""
73 doc_header = "Documented commands (type help <topic>):"
74 misc_header = "Miscellaneous help topics:"
75 undoc_header = "Undocumented commands:"
76 nohelp = "*** No help on %s"
Guido van Rossumbfb91842001-03-22 21:59:20 +000077 use_rawinput = 1
Guido van Rossum9b3bc711993-06-20 21:02:22 +000078
Tim Petersab9ba272001-08-09 21:40:30 +000079 def __init__(self, completekey='tab'):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +000080 """Instantiate a line-oriented interpreter framework.
81
82 The optional argument is the readline name of a completion key;
83 it defaults to the Tab key. If completekey is not None and the
84 readline module is available, command completion is done
85 automatically.
86
87 """
Guido van Rossum87fec222003-01-13 21:18:54 +000088 self.cmdqueue = []
Michael W. Hudson35a92ce2003-02-03 11:04:27 +000089 self.completekey = completekey
Guido van Rossum030eb111998-07-01 22:53:04 +000090
Guido van Rossum4acc25b2000-02-02 15:10:15 +000091 def cmdloop(self, intro=None):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +000092 """Repeatedly issue a prompt, accept input, parse an initial prefix
93 off the received input, and dispatch to action methods, passing them
94 the remainder of the line as argument.
95
96 """
97
Guido van Rossum4acc25b2000-02-02 15:10:15 +000098 self.preloop()
Fred Drake8152d322000-12-12 23:20:45 +000099 if intro is not None:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000100 self.intro = intro
101 if self.intro:
102 print self.intro
103 stop = None
104 while not stop:
105 if self.cmdqueue:
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000106 line = self.cmdqueue.pop(0)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000107 else:
Guido van Rossumbfb91842001-03-22 21:59:20 +0000108 if self.use_rawinput:
109 try:
110 line = raw_input(self.prompt)
111 except EOFError:
112 line = 'EOF'
113 else:
114 sys.stdout.write(self.prompt)
Neil Schemenauerd03c3422002-03-23 20:43:59 +0000115 sys.stdout.flush()
Guido van Rossumbfb91842001-03-22 21:59:20 +0000116 line = sys.stdin.readline()
117 if not len(line):
118 line = 'EOF'
119 else:
120 line = line[:-1] # chop \n
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000121 line = self.precmd(line)
122 stop = self.onecmd(line)
123 stop = self.postcmd(stop, line)
124 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +0000125
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000126 def precmd(self, line):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000127 """Hook method executed just before the command line is
128 interpreted, but after the input prompt is generated and issued.
129
130 """
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000131 return line
Guido van Rossum80884071998-06-29 17:58:55 +0000132
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000133 def postcmd(self, stop, line):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000134 """Hook method executed just after a command dispatch is finished."""
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000135 return stop
Guido van Rossum80884071998-06-29 17:58:55 +0000136
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000137 def preloop(self):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000138 """Hook method executed once when the cmdloop() method is called."""
Michael W. Hudson35a92ce2003-02-03 11:04:27 +0000139 if self.completekey:
140 try:
141 import readline
142 self.old_completer = readline.get_completer()
143 readline.set_completer(self.complete)
144 readline.parse_and_bind(self.completekey+": complete")
145 except ImportError:
146 pass
Guido van Rossum80884071998-06-29 17:58:55 +0000147
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000148 def postloop(self):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000149 """Hook method executed once when the cmdloop() method is about to
150 return.
151
152 """
Michael W. Hudson35a92ce2003-02-03 11:04:27 +0000153 if self.completekey:
154 try:
155 import readline
156 readline.set_completer(self.old_completer)
157 except ImportError:
158 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +0000159
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000160 def parseline(self, line):
Eric S. Raymond20e44232001-02-09 04:52:11 +0000161 line = line.strip()
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000162 if not line:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000163 return None, None, line
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000164 elif line[0] == '?':
165 line = 'help ' + line[1:]
166 elif line[0] == '!':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000167 if hasattr(self, 'do_shell'):
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000168 line = 'shell ' + line[1:]
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000169 else:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000170 return None, None, line
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000171 i, n = 0, len(line)
172 while i < n and line[i] in self.identchars: i = i+1
Eric S. Raymond20e44232001-02-09 04:52:11 +0000173 cmd, arg = line[:i], line[i:].strip()
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000174 return cmd, arg, line
Tim Petersab9ba272001-08-09 21:40:30 +0000175
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000176 def onecmd(self, line):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000177 """Interpret the argument as though it had been typed in response
178 to the prompt.
179
180 This may be overridden, but should not normally need to be;
181 see the precmd() and postcmd() methods for useful execution hooks.
182 The return value is a flag indicating whether interpretation of
183 commands by the interpreter should stop.
184
185 """
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000186 cmd, arg, line = self.parseline(line)
187 if not line:
188 return self.emptyline()
189 if cmd is None:
190 return self.default(line)
191 self.lastcmd = line
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000192 if cmd == '':
193 return self.default(line)
194 else:
195 try:
196 func = getattr(self, 'do_' + cmd)
197 except AttributeError:
198 return self.default(line)
199 return func(arg)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000200
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000201 def emptyline(self):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000202 """Called when an empty line is entered in response to the prompt.
203
204 If this method is not overridden, it repeats the last nonempty
205 command entered.
206
207 """
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000208 if self.lastcmd:
209 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000210
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000211 def default(self, line):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000212 """Called on an input line when the command prefix is not recognized.
213
214 If this method is not overridden, it prints an error message and
215 returns.
216
217 """
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000218 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000219
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000220 def completedefault(self, *ignored):
Raymond Hettingeraef22fb2002-05-29 16:18:42 +0000221 """Method called to complete an input line when no command-specific
222 complete_*() method is available.
223
224 By default, it returns an empty list.
225
226 """
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000227 return []
228
229 def completenames(self, text, *ignored):
230 dotext = 'do_'+text
231 return [a[3:] for a in self.get_names() if a.startswith(dotext)]
232
233 def complete(self, text, state):
234 """Return the next possible completion for 'text'.
235
236 If a command has not been entered, then complete against command list.
237 Otherwise try to call complete_<command> to get list of completions.
238 """
239 if state == 0:
240 import readline
241 origline = readline.get_line_buffer()
242 line = origline.lstrip()
243 stripped = len(origline) - len(line)
244 begidx = readline.get_begidx() - stripped
245 endidx = readline.get_endidx() - stripped
246 if begidx>0:
247 cmd, args, foo = self.parseline(line)
248 if cmd == '':
249 compfunc = self.completedefault
250 else:
251 try:
252 compfunc = getattr(self, 'complete_' + cmd)
253 except AttributeError:
254 compfunc = self.completedefault
255 else:
256 compfunc = self.completenames
257 self.completion_matches = compfunc(text, line, begidx, endidx)
258 try:
259 return self.completion_matches[state]
260 except IndexError:
261 return None
Tim Petersab9ba272001-08-09 21:40:30 +0000262
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000263 def get_names(self):
264 # Inheritance says we have to look in class and
265 # base classes; order is not important.
266 names = []
267 classes = [self.__class__]
268 while classes:
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000269 aclass = classes.pop(0)
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000270 if aclass.__bases__:
271 classes = classes + list(aclass.__bases__)
272 names = names + dir(aclass)
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000273 return names
274
275 def complete_help(self, *args):
276 return self.completenames(*args)
277
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000278 def do_help(self, arg):
279 if arg:
280 # XXX check arg syntax
281 try:
282 func = getattr(self, 'help_' + arg)
Skip Montanaro1ce00732002-03-24 16:34:21 +0000283 except AttributeError:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000284 try:
285 doc=getattr(self, 'do_' + arg).__doc__
286 if doc:
287 print doc
288 return
Skip Montanaro1ce00732002-03-24 16:34:21 +0000289 except AttributeError:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000290 pass
291 print self.nohelp % (arg,)
292 return
293 func()
294 else:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000295 names = self.get_names()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000296 cmds_doc = []
297 cmds_undoc = []
298 help = {}
299 for name in names:
300 if name[:5] == 'help_':
301 help[name[5:]]=1
302 names.sort()
303 # There can be duplicates if routines overridden
304 prevname = ''
305 for name in names:
306 if name[:3] == 'do_':
307 if name == prevname:
308 continue
309 prevname = name
310 cmd=name[3:]
Raymond Hettinger54f02222002-06-01 14:18:47 +0000311 if cmd in help:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000312 cmds_doc.append(cmd)
313 del help[cmd]
314 elif getattr(self, name).__doc__:
315 cmds_doc.append(cmd)
316 else:
317 cmds_undoc.append(cmd)
318 print self.doc_leader
319 self.print_topics(self.doc_header, cmds_doc, 15,80)
320 self.print_topics(self.misc_header, help.keys(),15,80)
321 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000322
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000323 def print_topics(self, header, cmds, cmdlen, maxcol):
324 if cmds:
Jeremy Hyltond30e5872001-01-26 17:15:18 +0000325 print header
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000326 if self.ruler:
327 print self.ruler * len(header)
Guido van Rossumc706c282002-12-02 13:08:53 +0000328 self.columnize(cmds, maxcol-1)
329 print
330
331 def columnize(self, list, displaywidth=80):
332 """Display a list of strings as a compact set of columns.
333
334 Each column is only as wide as necessary.
335 Columns are separated by two spaces (one was not legible enough).
336 """
337 if not list:
338 print "<empty>"
339 return
340 nonstrings = [i for i in range(len(list))
341 if not isinstance(list[i], str)]
342 if nonstrings:
343 raise TypeError, ("list[i] not a string for i in %s" %
344 ", ".join(map(str, nonstrings)))
345 size = len(list)
346 if size == 1:
347 print list[0]
348 return
349 # Try every row count from 1 upwards
350 for nrows in range(1, len(list)):
351 ncols = (size+nrows-1) // nrows
352 colwidths = []
353 totwidth = -2
354 for col in range(ncols):
355 colwidth = 0
356 for row in range(nrows):
357 i = row + nrows*col
358 if i >= size:
359 break
360 x = list[i]
361 colwidth = max(colwidth, len(x))
362 colwidths.append(colwidth)
363 totwidth += colwidth + 2
364 if totwidth > displaywidth:
365 break
366 if totwidth <= displaywidth:
367 break
368 else:
369 nrows = len(list)
370 ncols = 1
371 colwidths = [0]
372 for row in range(nrows):
373 texts = []
374 for col in range(ncols):
375 i = row + nrows*col
376 if i >= size:
377 x = ""
378 else:
379 x = list[i]
380 texts.append(x)
381 while texts and not texts[-1]:
382 del texts[-1]
383 for col in range(len(texts)):
384 texts[col] = texts[col].ljust(colwidths[col])
385 print " ".join(texts)