blob: d0c749831e5a665eda037245fe90c3edace16182 [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
40PROMPT = '(Cmd) '
41IDENTCHARS = string.letters + string.digits + '_'
42
43class Cmd:
Guido van Rossum4acc25b2000-02-02 15:10:15 +000044 prompt = PROMPT
45 identchars = IDENTCHARS
46 ruler = '='
47 lastcmd = ''
48 cmdqueue = []
49 intro = None
50 doc_leader = ""
51 doc_header = "Documented commands (type help <topic>):"
52 misc_header = "Miscellaneous help topics:"
53 undoc_header = "Undocumented commands:"
54 nohelp = "*** No help on %s"
Guido van Rossum9b3bc711993-06-20 21:02:22 +000055
Guido van Rossum4acc25b2000-02-02 15:10:15 +000056 def __init__(self): pass
Guido van Rossum030eb111998-07-01 22:53:04 +000057
Guido van Rossum4acc25b2000-02-02 15:10:15 +000058 def cmdloop(self, intro=None):
59 self.preloop()
60 if intro != None:
61 self.intro = intro
62 if self.intro:
63 print self.intro
64 stop = None
65 while not stop:
66 if self.cmdqueue:
67 line = self.cmdqueue[0]
68 del self.cmdqueue[0]
69 else:
70 try:
71 line = raw_input(self.prompt)
72 except EOFError:
73 line = 'EOF'
74 line = self.precmd(line)
75 stop = self.onecmd(line)
76 stop = self.postcmd(stop, line)
77 self.postloop()
Guido van Rossum80884071998-06-29 17:58:55 +000078
Guido van Rossum4acc25b2000-02-02 15:10:15 +000079 def precmd(self, line):
80 return line
Guido van Rossum80884071998-06-29 17:58:55 +000081
Guido van Rossum4acc25b2000-02-02 15:10:15 +000082 def postcmd(self, stop, line):
83 return stop
Guido van Rossum80884071998-06-29 17:58:55 +000084
Guido van Rossum4acc25b2000-02-02 15:10:15 +000085 def preloop(self):
86 pass
Guido van Rossum80884071998-06-29 17:58:55 +000087
Guido van Rossum4acc25b2000-02-02 15:10:15 +000088 def postloop(self):
89 pass
Guido van Rossumb53e6781992-01-24 01:12:17 +000090
Guido van Rossum4acc25b2000-02-02 15:10:15 +000091 def onecmd(self, line):
92 line = string.strip(line)
93 if line == '?':
94 line = 'help'
95 elif line == '!':
96 if hasattr(self, 'do_shell'):
97 line = 'shell'
98 else:
99 return self.default(line)
100 elif not line:
101 return self.emptyline()
102 self.lastcmd = line
103 i, n = 0, len(line)
104 while i < n and line[i] in self.identchars: i = i+1
105 cmd, arg = line[:i], string.strip(line[i:])
106 if cmd == '':
107 return self.default(line)
108 else:
109 try:
110 func = getattr(self, 'do_' + cmd)
111 except AttributeError:
112 return self.default(line)
113 return func(arg)
Guido van Rossumb53e6781992-01-24 01:12:17 +0000114
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000115 def emptyline(self):
116 if self.lastcmd:
117 return self.onecmd(self.lastcmd)
Guido van Rossum80884071998-06-29 17:58:55 +0000118
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000119 def default(self, line):
120 print '*** Unknown syntax:', line
Guido van Rossumb53e6781992-01-24 01:12:17 +0000121
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000122 def do_help(self, arg):
123 if arg:
124 # XXX check arg syntax
125 try:
126 func = getattr(self, 'help_' + arg)
127 except:
128 try:
129 doc=getattr(self, 'do_' + arg).__doc__
130 if doc:
131 print doc
132 return
133 except:
134 pass
135 print self.nohelp % (arg,)
136 return
137 func()
138 else:
139 # Inheritance says we have to look in class and
140 # base classes; order is not important.
141 names = []
142 classes = [self.__class__]
143 while classes:
144 aclass = classes[0]
145 if aclass.__bases__:
146 classes = classes + list(aclass.__bases__)
147 names = names + dir(aclass)
148 del classes[0]
149 cmds_doc = []
150 cmds_undoc = []
151 help = {}
152 for name in names:
153 if name[:5] == 'help_':
154 help[name[5:]]=1
155 names.sort()
156 # There can be duplicates if routines overridden
157 prevname = ''
158 for name in names:
159 if name[:3] == 'do_':
160 if name == prevname:
161 continue
162 prevname = name
163 cmd=name[3:]
164 if help.has_key(cmd):
165 cmds_doc.append(cmd)
166 del help[cmd]
167 elif getattr(self, name).__doc__:
168 cmds_doc.append(cmd)
169 else:
170 cmds_undoc.append(cmd)
171 print self.doc_leader
172 self.print_topics(self.doc_header, cmds_doc, 15,80)
173 self.print_topics(self.misc_header, help.keys(),15,80)
174 self.print_topics(self.undoc_header, cmds_undoc, 15,80)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000175
Guido van Rossum4acc25b2000-02-02 15:10:15 +0000176 def print_topics(self, header, cmds, cmdlen, maxcol):
177 if cmds:
178 print header;
179 if self.ruler:
180 print self.ruler * len(header)
181 (cmds_per_line,junk)=divmod(maxcol,cmdlen)
182 col=cmds_per_line
183 for cmd in cmds:
184 if col==0: print
185 print (("%-"+`cmdlen`+"s") % cmd),
186 col = (col+1) % cmds_per_line
187 print "\n"