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 usages = [usage, ]
209 if usage.find("%command") > -1:
210 if self.subCommands:
211 usage = usage.split("%command")[0] + '[command]'
212 usages = [usage, ]
213 else:
214
215 usages = usage.split("%command")
216 usages.reverse()
217
218
219
220
221
222
223 c = self.parentCommand
224 while c:
225 usage = c.usage or c.name
226 if usage.find(" %command") > -1:
227 usage = usage.split(" %command")[0]
228 usages.append(usage)
229 c = c.parentCommand
230 usages.reverse()
231 usage = " ".join(usages)
232
233
234 description = self.description or self.summary
235 if description:
236 description = description.strip()
237 self.parser = CommandOptionParser(
238 usage=usage, description=description,
239 formatter=formatter)
240 self.parser.set_stdout(self.stdout)
241 self.parser.disable_interspersed_args()
242
243
244 self.addOptions()
245
247 """
248 Override me to add options to the parser.
249 """
250 pass
251
252 - def do(self, args):
253 """
254 Override me to implement the functionality of the command.
255
256 @rtype: int
257 @returns: an exit code, or None if no actual action was taken.
258 """
259 raise NotImplementedError('Implement %s.do()' % self.__class__)
260
261 return 1
262
358
360 """
361 Handle the parsed options.
362 """
363 pass
364
374
385
387 """
388 Return the top-level command, which is typically the program.
389 """
390 c = self
391 while c.parentCommand:
392 c = c.parentCommand
393 return c
394
395 - def debug(self, format, *args):
396 """
397 Override me to handle debug output from this class.
398 """
399 pass
400
409
421
422 stdout = property(_getStdout)
423
424 stderr = property(_getStdout)
425
426
433
434
439
440
445
446
448 """
449 @type command: L{Command}
450
451 Take a Command instance and create a L{cmd.Cmd} class from it that
452 implements a command line interpreter, using the commands under the given
453 Command instance as its subcommands.
454
455 Example use in a command:
456
457 >>> def do(self, args):
458 ... cmd = command.commandToCmdClass(self)()
459 ... cmd.prompt = 'prompt> '
460 ... while not cmd.exited:
461 ... cmd.cmdloop()
462
463 @rtype: L{cmd.Cmd}
464 """
465 import cmd
466
467
468
469 class _CommandWrappingCmd(cmd.Cmd):
470 prompt = '(command) '
471 exited = False
472 command = None
473
474 def __repr__(self):
475 return "<_CommandWrappingCmd for Command %r>" % self.command
476
477 def do_EOF(self, args):
478 self.stdout.write('\n')
479 self.exited = True
480 sys.exit(0)
481
482 def do_exit(self, args):
483 self.exited = True
484 sys.exit(0)
485
486 def help_EOF(self):
487 print 'Exit.'
488
489 def help_exit(self):
490 print 'Exit.'
491
492
493 cmdClass = _CommandWrappingCmd
494 cmdClass.command = command
495
496 for name, subCommand in command.subCommands.items() \
497 + command.aliasedSubCommands.items():
498 if name == 'shell':
499 continue
500 command.debug('Adding shell command %s for %r' % (name, subCommand))
501
502
503 methodName = 'do_' + name
504
505 def generateDo(c):
506
507 def do_(s, line):
508
509
510
511
512 line = line.decode('utf-8')
513
514
515 args = line.split(' ')
516 command.debug('Asking %r to parse %r' % (c, args))
517 return c.parse(args)
518 return do_
519
520 method = generateDo(subCommand)
521 setattr(cmdClass, methodName, method)
522
523
524
525 methodName = 'help_' + name
526
527 def generateHelp(c):
528
529 def help_(s):
530 command.parser.print_help(file=s.stdout)
531 return help_
532
533 method = generateHelp(subCommand)
534 setattr(cmdClass, methodName, method)
535
536 return cmdClass
537
538
542