blob: dcf601f4f231fdf1c24ac6808da86440ba4bd227 [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
38import string
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 Rossum9b3bc711993-06-20 21:02:22 +000057
Guido van Rossum4acc25b2000-02-02 15:10:15 +000058 def __init__(self): pass
Guido van Rossum030eb111998-07-01 22:53:04 +000059
Guido van Rossum4acc25b2000-02-02 15:10:15 +000060 def cmdloop(self, intro=None):
61 self.preloop()
Fred Drake8152d322000-12-12 23:20:45 +000062 if intro is not None:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000063 self.intro = intro
64 if self.intro:
65 print self.intro
66 stop = None
67 while not stop:
68 if self.cmdqueue:
69 line = self.cmdqueue[0]
70 del self.cmdqueue[0]
71 else:
72 try:
73 line = raw_input(self.prompt)
74 except EOFError:
75 line = 'EOF'
76 line = self.precmd(line)
77 stop = self.onecmd(line)
78 stop = self.postcmd(stop, line)
79 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +000080
Guido van Rossum4acc25b2000-02-02 15:10:15 +000081 def precmd(self, line):
82 return line
Guido van Rossum80884071998-06-29 17:58:55 +000083
Guido van Rossum4acc25b2000-02-02 15:10:15 +000084 def postcmd(self, stop, line):
85 return stop
Guido van Rossum80884071998-06-29 17:58:55 +000086
Guido van Rossum4acc25b2000-02-02 15:10:15 +000087 def preloop(self):
88 pass
Guido van Rossum80884071998-06-29 17:58:55 +000089
Guido van Rossum4acc25b2000-02-02 15:10:15 +000090 def postloop(self):
91 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +000092
Guido van Rossum4acc25b2000-02-02 15:10:15 +000093 def onecmd(self, line):
Eric S. Raymond20e44232001-02-09 04:52:11 +000094 line = line.strip()
Eric S. Raymond5f1b2702000-07-11 13:03:55 +000095 if not line:
96 return self.emptyline()
97 elif line[0] == '?':
98 line = 'help ' + line[1:]
99 elif line[0] == '!':
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000100 if hasattr(self, 'do_shell'):
Eric S. Raymond5f1b2702000-07-11 13:03:55 +0000101 line = 'shell ' + line[1:]
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000102 else:
103 return self.default(line)
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000104 self.lastcmd = line
105 i, n = 0, len(line)
106 while i < n and line[i] in self.identchars: i = i+1
Eric S. Raymond20e44232001-02-09 04:52:11 +0000107 cmd, arg = line[:i], line[i:].strip()
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000108 if cmd == '':
109 return self.default(line)
110 else:
111 try:
112 func = getattr(self, 'do_' + cmd)
113 except AttributeError:
114 return self.default(line)
115 return func(arg)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000116
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000117 def emptyline(self):
118 if self.lastcmd:
119 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000120
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000121 def default(self, line):
122 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000123
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000124 def do_help(self, arg):
125 if arg:
126 # XXX check arg syntax
127 try:
128 func = getattr(self, 'help_' + arg)
129 except:
130 try:
131 doc=getattr(self, 'do_' + arg).__doc__
132 if doc:
133 print doc
134 return
135 except:
136 pass
137 print self.nohelp % (arg,)
138 return
139 func()
140 else:
141 # Inheritance says we have to look in class and
142 # base classes; order is not important.
143 names = []
144 classes = [self.__class__]
145 while classes:
146 aclass = classes[0]
147 if aclass.__bases__:
148 classes = classes + list(aclass.__bases__)
149 names = names + dir(aclass)
150 del classes[0]
151 cmds_doc = []
152 cmds_undoc = []
153 help = {}
154 for name in names:
155 if name[:5] == 'help_':
156 help[name[5:]]=1
157 names.sort()
158 # There can be duplicates if routines overridden
159 prevname = ''
160 for name in names:
161 if name[:3] == 'do_':
162 if name == prevname:
163 continue
164 prevname = name
165 cmd=name[3:]
166 if help.has_key(cmd):
167 cmds_doc.append(cmd)
168 del help[cmd]
169 elif getattr(self, name).__doc__:
170 cmds_doc.append(cmd)
171 else:
172 cmds_undoc.append(cmd)
173 print self.doc_leader
174 self.print_topics(self.doc_header, cmds_doc, 15,80)
175 self.print_topics(self.misc_header, help.keys(),15,80)
176 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000177
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000178 def print_topics(self, header, cmds, cmdlen, maxcol):
179 if cmds:
Jeremy Hyltond30e5872001-01-26 17:15:18 +0000180 print header
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000181 if self.ruler:
182 print self.ruler * len(header)
183 (cmds_per_line,junk)=divmod(maxcol,cmdlen)
184 col=cmds_per_line
185 for cmd in cmds:
186 if col==0: print
187 print (("%-"+`cmdlen`+"s") % cmd),
188 col = (col+1) % cmds_per_line
189 print "\n"