blob: 2a7be430833506ca792ab39cc1fdfc1d540077c4 [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:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000056 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"
Guido van Rossumbfb91842001-03-22 21:59:20 +000067 use_rawinput = 1
Guido van Rossum9b3bc711993-06-20 21:02:22 +000068
Tim Petersab9ba272001-08-09 21:40:30 +000069 def __init__(self, completekey='tab'):
Martin v. Löwis66b6e192001-07-28 14:44:03 +000070 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
Guido van Rossum030eb111998-07-01 22:53:04 +000077
Guido van Rossum4acc25b2000-02-02 15:10:15 +000078 def cmdloop(self, intro=None):
79 self.preloop()
Fred Drake8152d322000-12-12 23:20:45 +000080 if intro is not None:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000081 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:
Guido van Rossumbfb91842001-03-22 21:59:20 +000090 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
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000102 line = self.precmd(line)
103 stop = self.onecmd(line)
104 stop = self.postcmd(stop, line)
105 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +0000106
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000107 def precmd(self, line):
108 return line
Guido van Rossum80884071998-06-29 17:58:55 +0000109
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000110 def postcmd(self, stop, line):
111 return stop
Guido van Rossum80884071998-06-29 17:58:55 +0000112
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000113 def preloop(self):
114 pass
Guido van Rossum80884071998-06-29 17:58:55 +0000115
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000116 def postloop(self):
117 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +0000118
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000119 def parseline(self, line):
Eric S. Raymond20e44232001-02-09 04:52:11 +0000120 line = line.strip()
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000121 if not line:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000122 return None, None, line
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000123 elif line[0] == '?':
124 line = 'help ' + line[1:]
125 elif line[0] == '!':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000126 if hasattr(self, 'do_shell'):
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000127 line = 'shell ' + line[1:]
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000128 else:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000129 return None, None, line
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000130 i, n = 0, len(line)
131 while i < n and line[i] in self.identchars: i = i+1
Eric S. Raymond20e44232001-02-09 04:52:11 +0000132 cmd, arg = line[:i], line[i:].strip()
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000133 return cmd, arg, line
Tim Petersab9ba272001-08-09 21:40:30 +0000134
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000135 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
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000142 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)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000150
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000151 def emptyline(self):
152 if self.lastcmd:
153 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000154
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000155 def default(self, line):
156 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000157
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000158 def completedefault(self, *ignored):
159 return []
160
161 def completenames(self, text, *ignored):
162 dotext = 'do_'+text
163 return [a[3:] for a in self.get_names() if a.startswith(dotext)]
164
165 def complete(self, text, state):
166 """Return the next possible completion for 'text'.
167
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.
170 """
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
Tim Petersab9ba272001-08-09 21:40:30 +0000194
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000195 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
207
208 def complete_help(self, *args):
209 return self.completenames(*args)
210
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000211 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:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000228 names = self.get_names()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000229 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)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000255
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000256 def print_topics(self, header, cmds, cmdlen, maxcol):
257 if cmds:
Jeremy Hyltond30e5872001-01-26 17:15:18 +0000258 print header
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000259 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"