1
2
3
4
5
6 """
7 Command class.
8 """
9
10 import optparse
11 import sys
12
13
73
74
76 """
77 I parse options as usual, but I explicitly allow setting stdout
78 so that our print_help() method (invoked by default with -h/--help)
79 defaults to writing there.
80
81 I also override exit() so that I can be used in interactive shells.
82
83 @ivar help_printed: whether help was printed during parsing
84 @ivar usage_printed: whether usage was printed during parsing
85 """
86 help_printed = False
87 usage_printed = False
88
89 _stdout = sys.stdout
90
93
98
99
100 __pychecker__ = 'no-shadowbuiltin'
101
109
113
114 - def exit(self, status=0, msg=None):
119
120
122 """
123 I am a class that handles a command for a program.
124 Commands can be nested underneath a command for further processing.
125
126 @cvar name: name of the command, lowercase;
127 defaults to the lowercase version of the class name
128 @cvar aliases: list of alternative lowercase names recognized
129 @type aliases: list of str
130 @cvar usage: short one-line usage string;
131 %command gets expanded to a sub-command or [commands]
132 as appropriate. Don't specify the command name itself,
133 it will be added automatically. If not set, defaults
134 to name.
135 @cvar summary: short one-line summary of the command
136 @cvar description: longer paragraph explaining the command
137 @cvar subCommands: dict of name -> commands below this command
138 @type subCommands: dict of str -> L{Command}
139 @cvar parser: the option parser used for parsing
140 @type parser: L{optparse.OptionParser}
141 """
142 name = None
143 aliases = None
144 usage = None
145 summary = None
146 description = None
147 parentCommand = None
148 subCommands = None
149 subCommandClasses = None
150 aliasedSubCommands = None
151 parser = None
152
153 - def __init__(self, parentCommand=None, stdout=None,
154 stderr=None, width=None):
155 """
156 Create a new command instance, with the given parent.
157 Allows for redirecting stdout and stderr if needed.
158 This redirection will be passed on to child commands.
159 """
160 if not self.name:
161 self.name = self.__class__.__name__.lower()
162 self._stdout = stdout
163 self._stderr = stderr
164 self.parentCommand = parentCommand
165
166
167 self.subCommands = {}
168 self.aliasedSubCommands = {}
169 if self.subCommandClasses:
170 for C in self.subCommandClasses:
171 c = C(self, stdout=stdout, stderr=stderr, width=width)
172 self.subCommands[c.name] = c
173 if c.aliases:
174 for alias in c.aliases:
175 self.aliasedSubCommands[alias] = c
176
177
178 formatter = CommandHelpFormatter(width=width)
179 if self.subCommands:
180 if not self.description:
181 if self.summary:
182 self.description = self.summary
183 else:
184 raise AttributeError, \
185 "%r needs a summary or description " \
186 "for help formatting" % self
187
188 for name, command in self.subCommands.items():
189 formatter.addCommand(name, command.summary or
190 command.description)
191
192 if self.aliases:
193 for alias in self.aliases:
194 formatter.addAlias(alias)
195
196
197 usage = self.usage or ''
198 if not usage:
199
200 if self.subCommands:
201 usage = "%command"
202
203
204
205 if not usage.startswith('%prog'):
206 usage = self.name + ' ' + usage
207
208 if usage.find("%command") > -1:
209 usage = usage.split("%command")[0] + '[command]'
210 usages = [usage, ]
211
212
213
214
215
216
217 c = self.parentCommand
218 while c:
219 usage = c.usage or c.name
220 if usage.find(" %command") > -1:
221 usage = usage.split(" %command")[0]
222 usages.append(usage)
223 c = c.parentCommand
224 usages.reverse()
225 usage = " ".join(usages)
226
227
228 description = self.description or self.summary
229 if description:
230 description = description.strip()
231 self.parser = CommandOptionParser(
232 usage=usage, description=description,
233 formatter=formatter)
234 self.parser.set_stdout(self.stdout)
235 self.parser.disable_interspersed_args()
236
237
238 self.addOptions()
239
241 """
242 Override me to add options to the parser.
243 """
244 pass
245
246 - def do(self, args):
247 """
248 Override me to implement the functionality of the command.
249
250 @rtype: int
251 @returns: an exit code, or None if no actual action was taken.
252 """
253 raise NotImplementedError('Implement %s.do()' % self.__class__)
254
255 return 1
256
352
354 """
355 Handle the parsed options.
356 """
357 pass
358
368
379
381 """
382 Return the top-level command, which is typically the program.
383 """
384 c = self
385 while c.parentCommand:
386 c = c.parentCommand
387 return c
388
389 - def debug(self, format, *args):
390 """
391 Override me to handle debug output from this class.
392 """
393 pass
394
403
415
416 stdout = property(_getStdout)
417
418 stderr = property(_getStdout)
419
420
427
428
433
434
439
440
442 """
443 @type command: L{Command}
444
445 Take a Command instance and create a L{cmd.Cmd} class from it that
446 implements a command line interpreter, using the commands under the given
447 Command instance as its subcommands.
448
449 Example use in a command:
450
451 >>> def do(self, args):
452 ... cmd = command.commandToCmdClass(self)()
453 ... cmd.prompt = 'prompt> '
454 ... while not cmd.exited:
455 ... cmd.cmdloop()
456
457 @rtype: L{cmd.Cmd}
458 """
459 import cmd
460
461
462
463 class _CommandWrappingCmd(cmd.Cmd):
464 prompt = '(command) '
465 exited = False
466 command = None
467
468 def __repr__(self):
469 return "<_CommandWrappingCmd for Command %r>" % self.command
470
471 def do_EOF(self, args):
472 self.stdout.write('\n')
473 self.exited = True
474 sys.exit(0)
475
476 def do_exit(self, args):
477 self.exited = True
478 sys.exit(0)
479
480 def help_EOF(self):
481 print 'Exit.'
482
483 def help_exit(self):
484 print 'Exit.'
485
486
487 cmdClass = _CommandWrappingCmd
488 cmdClass.command = command
489
490 for name, subCommand in command.subCommands.items() \
491 + command.aliasedSubCommands.items():
492 if name == 'shell':
493 continue
494 command.debug('Adding shell command %s for %r' % (name, subCommand))
495
496
497 methodName = 'do_' + name
498
499 def generateDo(c):
500
501 def do_(s, line):
502
503
504
505
506 line = line.decode('utf-8')
507
508
509 args = line.split(' ')
510 command.debug('Asking %r to parse %r' % (c, args))
511 return c.parse(args)
512 return do_
513
514 method = generateDo(subCommand)
515 setattr(cmdClass, methodName, method)
516
517
518
519 methodName = 'help_' + name
520
521 def generateHelp(c):
522
523 def help_(s):
524 command.parser.print_help(file=s.stdout)
525 return help_
526
527 method = generateHelp(subCommand)
528 setattr(cmdClass, methodName, method)
529
530 return cmdClass
531
532
536