blob: 5bbf4bcc7c8e4e9b833b246771f83fc1b9c1695c [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)
Neil Schemenauerd03c3422002-03-23 20:43:59 +000097 sys.stdout.flush()
Guido van Rossumbfb91842001-03-22 21:59:20 +000098 line = sys.stdin.readline()
99 if not len(line):
100 line = 'EOF'
101 else:
102 line = line[:-1] # chop \n
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000103 line = self.precmd(line)
104 stop = self.onecmd(line)
105 stop = self.postcmd(stop, line)
106 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +0000107
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000108 def precmd(self, line):
109 return line
Guido van Rossum80884071998-06-29 17:58:55 +0000110
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000111 def postcmd(self, stop, line):
112 return stop
Guido van Rossum80884071998-06-29 17:58:55 +0000113
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000114 def preloop(self):
115 pass
Guido van Rossum80884071998-06-29 17:58:55 +0000116
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000117 def postloop(self):
118 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +0000119
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000120 def parseline(self, line):
Eric S. Raymond20e44232001-02-09 04:52:11 +0000121 line = line.strip()
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000122 if not line:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000123 return None, None, line
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000124 elif line[0] == '?':
125 line = 'help ' + line[1:]
126 elif line[0] == '!':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000127 if hasattr(self, 'do_shell'):
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000128 line = 'shell ' + line[1:]
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000129 else:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000130 return None, None, line
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000131 i, n = 0, len(line)
132 while i < n and line[i] in self.identchars: i = i+1
Eric S. Raymond20e44232001-02-09 04:52:11 +0000133 cmd, arg = line[:i], line[i:].strip()
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000134 return cmd, arg, line
Tim Petersab9ba272001-08-09 21:40:30 +0000135
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000136 def onecmd(self, line):
137 cmd, arg, line = self.parseline(line)
138 if not line:
139 return self.emptyline()
140 if cmd is None:
141 return self.default(line)
142 self.lastcmd = line
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000143 if cmd == '':
144 return self.default(line)
145 else:
146 try:
147 func = getattr(self, 'do_' + cmd)
148 except AttributeError:
149 return self.default(line)
150 return func(arg)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000151
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000152 def emptyline(self):
153 if self.lastcmd:
154 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000155
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000156 def default(self, line):
157 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000158
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000159 def completedefault(self, *ignored):
160 return []
161
162 def completenames(self, text, *ignored):
163 dotext = 'do_'+text
164 return [a[3:] for a in self.get_names() if a.startswith(dotext)]
165
166 def complete(self, text, state):
167 """Return the next possible completion for 'text'.
168
169 If a command has not been entered, then complete against command list.
170 Otherwise try to call complete_<command> to get list of completions.
171 """
172 if state == 0:
173 import readline
174 origline = readline.get_line_buffer()
175 line = origline.lstrip()
176 stripped = len(origline) - len(line)
177 begidx = readline.get_begidx() - stripped
178 endidx = readline.get_endidx() - stripped
179 if begidx>0:
180 cmd, args, foo = self.parseline(line)
181 if cmd == '':
182 compfunc = self.completedefault
183 else:
184 try:
185 compfunc = getattr(self, 'complete_' + cmd)
186 except AttributeError:
187 compfunc = self.completedefault
188 else:
189 compfunc = self.completenames
190 self.completion_matches = compfunc(text, line, begidx, endidx)
191 try:
192 return self.completion_matches[state]
193 except IndexError:
194 return None
Tim Petersab9ba272001-08-09 21:40:30 +0000195
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000196 def get_names(self):
197 # Inheritance says we have to look in class and
198 # base classes; order is not important.
199 names = []
200 classes = [self.__class__]
201 while classes:
202 aclass = classes[0]
203 if aclass.__bases__:
204 classes = classes + list(aclass.__bases__)
205 names = names + dir(aclass)
206 del classes[0]
207 return names
208
209 def complete_help(self, *args):
210 return self.completenames(*args)
211
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000212 def do_help(self, arg):
213 if arg:
214 # XXX check arg syntax
215 try:
216 func = getattr(self, 'help_' + arg)
Skip Montanaro1ce00732002-03-24 16:34:21 +0000217 except AttributeError:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000218 try:
219 doc=getattr(self, 'do_' + arg).__doc__
220 if doc:
221 print doc
222 return
Skip Montanaro1ce00732002-03-24 16:34:21 +0000223 except AttributeError:
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000224 pass
225 print self.nohelp % (arg,)
226 return
227 func()
228 else:
Martin v. Löwis66b6e192001-07-28 14:44:03 +0000229 names = self.get_names()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000230 cmds_doc = []
231 cmds_undoc = []
232 help = {}
233 for name in names:
234 if name[:5] == 'help_':
235 help[name[5:]]=1
236 names.sort()
237 # There can be duplicates if routines overridden
238 prevname = ''
239 for name in names:
240 if name[:3] == 'do_':
241 if name == prevname:
242 continue
243 prevname = name
244 cmd=name[3:]
245 if help.has_key(cmd):
246 cmds_doc.append(cmd)
247 del help[cmd]
248 elif getattr(self, name).__doc__:
249 cmds_doc.append(cmd)
250 else:
251 cmds_undoc.append(cmd)
252 print self.doc_leader
253 self.print_topics(self.doc_header, cmds_doc, 15,80)
254 self.print_topics(self.misc_header, help.keys(),15,80)
255 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000256
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000257 def print_topics(self, header, cmds, cmdlen, maxcol):
258 if cmds:
Jeremy Hyltond30e5872001-01-26 17:15:18 +0000259 print header
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000260 if self.ruler:
261 print self.ruler * len(header)
262 (cmds_per_line,junk)=divmod(maxcol,cmdlen)
263 col=cmds_per_line
264 for cmd in cmds:
265 if col==0: print
266 print (("%-"+`cmdlen`+"s") % cmd),
267 col = (col+1) % cmds_per_line
268 print "\n"