blob: 10c11a22214cfa044e8fb8bdc949a65a43df4cf7 [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.
18
19The `default' method may be overridden to intercept commands for which there
20is no do_ method.
21
22The data member `self.ruler' sets the character used to draw separator lines
23in the help messages. If empty, no ruler line is drawn. It defaults to "=".
24
25If the value of `self.intro' is nonempty when the cmdloop method is called,
26it is printed out on interpreter startup. This value may be overridden
27via an optional argument to the cmdloop() method.
28
29The data members `self.doc_header', `self.misc_header', and
30`self.undoc_header' set the headers used for the help function's
31listings of documented functions, miscellaneous topics, and undocumented
32functions respectively.
33
34These interpreters use raw_input; thus, if the readline module is loaded,
35they automatically support Emacs-like command history and editing features.
36"""
Guido van Rossumb53e6781992-01-24 01:12:17 +000037
Guido van Rossumbfb91842001-03-22 21:59:20 +000038import string, sys
Guido van Rossumb53e6781992-01-24 01:12:17 +000039
Skip Montanaroe99d5ea2001-01-20 19:54:20 +000040__all__ = ["Cmd"]
41
Guido van Rossumb53e6781992-01-24 01:12:17 +000042PROMPT = '(Cmd) '
43IDENTCHARS = string.letters + string.digits + '_'
44
45class Cmd:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000046 prompt = PROMPT
47 identchars = IDENTCHARS
48 ruler = '='
49 lastcmd = ''
50 cmdqueue = []
51 intro = None
52 doc_leader = ""
53 doc_header = "Documented commands (type help <topic>):"
54 misc_header = "Miscellaneous help topics:"
55 undoc_header = "Undocumented commands:"
56 nohelp = "*** No help on %s"
Guido van Rossumbfb91842001-03-22 21:59:20 +000057 use_rawinput = 1
Guido van Rossum9b3bc711993-06-20 21:02:22 +000058
Guido van Rossum4acc25b2000-02-02 15:10:15 +000059 def __init__(self): pass
Guido van Rossum030eb111998-07-01 22:53:04 +000060
Guido van Rossum4acc25b2000-02-02 15:10:15 +000061 def cmdloop(self, intro=None):
62 self.preloop()
Fred Drake8152d322000-12-12 23:20:45 +000063 if intro is not None:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000064 self.intro = intro
65 if self.intro:
66 print self.intro
67 stop = None
68 while not stop:
69 if self.cmdqueue:
70 line = self.cmdqueue[0]
71 del self.cmdqueue[0]
72 else:
Guido van Rossumbfb91842001-03-22 21:59:20 +000073 if self.use_rawinput:
74 try:
75 line = raw_input(self.prompt)
76 except EOFError:
77 line = 'EOF'
78 else:
79 sys.stdout.write(self.prompt)
80 line = sys.stdin.readline()
81 if not len(line):
82 line = 'EOF'
83 else:
84 line = line[:-1] # chop \n
Guido van Rossum4acc25b2000-02-02 15:10:15 +000085 line = self.precmd(line)
86 stop = self.onecmd(line)
87 stop = self.postcmd(stop, line)
88 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +000089
Guido van Rossum4acc25b2000-02-02 15:10:15 +000090 def precmd(self, line):
91 return line
Guido van Rossum80884071998-06-29 17:58:55 +000092
Guido van Rossum4acc25b2000-02-02 15:10:15 +000093 def postcmd(self, stop, line):
94 return stop
Guido van Rossum80884071998-06-29 17:58:55 +000095
Guido van Rossum4acc25b2000-02-02 15:10:15 +000096 def preloop(self):
97 pass
Guido van Rossum80884071998-06-29 17:58:55 +000098
Guido van Rossum4acc25b2000-02-02 15:10:15 +000099 def postloop(self):
100 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +0000101
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000102 def onecmd(self, line):
Eric S. Raymond20e44232001-02-09 04:52:11 +0000103 line = line.strip()
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000104 if not line:
105 return self.emptyline()
106 elif line[0] == '?':
107 line = 'help ' + line[1:]
108 elif line[0] == '!':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000109 if hasattr(self, 'do_shell'):
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000110 line = 'shell ' + line[1:]
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000111 else:
112 return self.default(line)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000113 self.lastcmd = line
114 i, n = 0, len(line)
115 while i < n and line[i] in self.identchars: i = i+1
Eric S. Raymond20e44232001-02-09 04:52:11 +0000116 cmd, arg = line[:i], line[i:].strip()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000117 if cmd == '':
118 return self.default(line)
119 else:
120 try:
121 func = getattr(self, 'do_' + cmd)
122 except AttributeError:
123 return self.default(line)
124 return func(arg)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000125
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000126 def emptyline(self):
127 if self.lastcmd:
128 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000129
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000130 def default(self, line):
131 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000132
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000133 def do_help(self, arg):
134 if arg:
135 # XXX check arg syntax
136 try:
137 func = getattr(self, 'help_' + arg)
138 except:
139 try:
140 doc=getattr(self, 'do_' + arg).__doc__
141 if doc:
142 print doc
143 return
144 except:
145 pass
146 print self.nohelp % (arg,)
147 return
148 func()
149 else:
150 # Inheritance says we have to look in class and
151 # base classes; order is not important.
152 names = []
153 classes = [self.__class__]
154 while classes:
155 aclass = classes[0]
156 if aclass.__bases__:
157 classes = classes + list(aclass.__bases__)
158 names = names + dir(aclass)
159 del classes[0]
160 cmds_doc = []
161 cmds_undoc = []
162 help = {}
163 for name in names:
164 if name[:5] == 'help_':
165 help[name[5:]]=1
166 names.sort()
167 # There can be duplicates if routines overridden
168 prevname = ''
169 for name in names:
170 if name[:3] == 'do_':
171 if name == prevname:
172 continue
173 prevname = name
174 cmd=name[3:]
175 if help.has_key(cmd):
176 cmds_doc.append(cmd)
177 del help[cmd]
178 elif getattr(self, name).__doc__:
179 cmds_doc.append(cmd)
180 else:
181 cmds_undoc.append(cmd)
182 print self.doc_leader
183 self.print_topics(self.doc_header, cmds_doc, 15,80)
184 self.print_topics(self.misc_header, help.keys(),15,80)
185 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000186
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000187 def print_topics(self, header, cmds, cmdlen, maxcol):
188 if cmds:
Jeremy Hyltond30e5872001-01-26 17:15:18 +0000189 print header
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000190 if self.ruler:
191 print self.ruler * len(header)
192 (cmds_per_line,junk)=divmod(maxcol,cmdlen)
193 col=cmds_per_line
194 for cmd in cmds:
195 if col==0: print
196 print (("%-"+`cmdlen`+"s") % cmd),
197 col = (col+1) % cmds_per_line
198 print "\n"