"""Defines :class:`CommandLine` for driving Hooke from the command line. """ import cmd import optparse import readline # including readline makes cmd.Cmd.cmdloop() smarter import shlex from ..command import CommandExit, Command, Argument from ..ui import UserInterface, CommandMessage # Define a few helper classes. The .__call__ methods of these # functions will provide the do_*, help_*, and complete_* methods of # HookeCmd. class CommandMethod (object): def __init__(self, cmd, command): self.cmd = cmd self.command = command def __call__(self, *args, **kwargs): raise NotImplementedError def command_parser(command): p = optparse.OptionParser() opts = [] args = [] for a in command.arguments: if a.name == 'help': continue # 'help' is a default OptionParser option name = a.name.replace('_', '-') if a.optional == True: p.add_option('--%s' % name, dest=a.name, default=a.default) opts.append((name, a)) else: args.append((name, a)) return (p, opts, args) class DoCommand (CommandMethod): def __init__(self, *args, **kwargs): super(DoCommand, self).__init__(*args, **kwargs) self.parser,self.opts,self.args = command_parser(self.command) def __call__(self, args): args = self._parse_args(self.command, args) self.cmd.inqueue.put(CommandMessage(self.command, args)) while True: msg = self.cmd.outqueue.get() if isinstance(msg, CommandExit): break self.cmd.stdout.write(str(msg).rstrip()+'\n') def _parse_args(self, command, args): argv = shlex.split(args, comments=True, posix=True) options,args = self.parser.parse_args(argv) params = {} for namearg in self.opts: name,argument = namearg params[name] = getattr(options, name) for namearg,value in zip(self.args, args): name,argument = namearg params[name] = value return params class HelpCommand (CommandMethod): def __init__(self, *args, **kwargs): super(HelpCommand, self).__init__(*args, **kwargs) self.parser,self.opts,self.args = command_parser(self.command) def __call__(self): blocks = [self.command.help(), '------', 'Usage: ' + self._usage_string(), ''] self.cmd.stdout.write('\n'.join(blocks)) def _message(self): return self.command.help() def _usage_string(self): if len(self.args) == len(self.command.arguments): options_string = '' else: options_string = '[options]' arg_string = ' '.join([name for name,arg in self.args]) return ' '.join([x for x in [self.command.name, options_string, arg_string] if x != '']) class CompleteCommand (CommandMethod): def __call__(self, text, line, begidx, endidx): pass # Now onto the main attraction. class HookeCmd (cmd.Cmd): def __init__(self, commands, inqueue, outqueue): cmd.Cmd.__init__(self) self.commands = commands self.prompt = 'hooke> ' self._add_command_methods() self.inqueue = inqueue self.outqueue = outqueue def _safe_name(self, name): return name.lower().replace(' ', '_') def _add_command_methods(self): for command in self.commands: command.name = self._safe_name(command.name) command.aliases = [self._safe_name(n) for n in command.aliases] for name in [command.name] + command.aliases: name = self._safe_name(name) setattr(self.__class__, 'do_%s' % name, DoCommand(self, command)) setattr(self.__class__, 'help_%s' % name, HelpCommand(self, command)) setattr(self.__class__, 'complete_%s' % name, CompleteCommand(self, command)) exit_command = Command( name='exit', aliases=['quit', 'EOF'], help='Exit Hooke cleanly.') exit_command.arguments = [] # remove help argument for name in [exit_command.name] + exit_command.aliases: setattr(self.__class__, 'do_%s' % name, lambda self, args : True) # the True return stops .cmdloop execution setattr(self.__class__, 'help_%s' % name, HelpCommand(self, exit_command)) help_command = Command( name='help', help=""" Called with an argument, prints that command's documentation. With no argument, lists all available help topics as well as any undocumented commands. """.strip()) help_command.arguments = [ # overwrite help argument Argument(name='command', type='string', optional=True, help='The name of the command you want help with.') ] setattr(self.__class__, 'help_help', HelpCommand(self, help_command)) class CommandLine (UserInterface): """Command line interface. Simple and powerful. """ def __init__(self): super(CommandLine, self).__init__(name='command line') def run(self, hooke, ui_to_command_queue, command_to_ui_queue): cmd = HookeCmd(hooke, inqueue=ui_to_command_queue, outqueue=command_to_ui_queue) cmd.cmdloop(self._splash_text())