From 0104c445c8dbca784c628ba1a2067296dcd3b5e6 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sun, 9 May 2010 18:41:55 -0400 Subject: [PATCH] help_* working in commandline UI. Still to go: * DoCommand._parse_args * DoHelp._usage_string * CompleteCommand * Interactive communication between user and command --- hooke/command.py | 11 ++-- hooke/ui/commandline.py | 139 +++++++++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 57 deletions(-) diff --git a/hooke/command.py b/hooke/command.py index 5f5228d..bfa4c9f 100644 --- a/hooke/command.py +++ b/hooke/command.py @@ -99,10 +99,13 @@ class Command (object): name_part = 'Command: %s' % self.name if len(self.aliases) > 0: name_part += ' (%s)' % ', '.join(self.aliases) - argument_part = ['Arguments:'] + [a.help() for a in self.arguments] - argument_part = '\n'.join(argument_part) - help_part = self._help - return '\n\n'.join([name_part, argument_part, help_part]) + parts = [name_part] + if len(self.arguments) > 0: + argument_part = ['Arguments:'] + [a.help() for a in self.arguments] + argument_part = '\n'.join(argument_part) + parts.append(argument_part) + parts.append(self._help) # help part + return '\n\n'.join(parts) class Argument (object): """Structured user input for :class:`Command`\s. diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index e4c533f..02174c4 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -5,76 +5,107 @@ line. import cmd import readline -from ..ui import UserInterface +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 + +class DoCommand (CommandMethod): + 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)) + + def _parse_args(self, command, args): + print 'ARGS:', args, type(args) + return {} + +class HelpCommand (CommandMethod): + 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): + return ' '.join([self.command.name, '[args]']) + +class CompleteCommand (CommandMethod): + def __call__(self, text, line, begidx, endidx): + pass + + +# Now onto the main attraction. class HookeCmd (cmd.Cmd): - def __init__(self, hooke): + def __init__(self, hooke, inqueue, outqueue): cmd.Cmd.__init__(self) self.hooke = hooke self.prompt = 'hooke> ' - self._add_do_methods() + self._add_command_methods() + self.inqueue = inqueue + self.outqueue = outqueue def _safe_name(self, name): return name.lower().replace(' ', '_') - def _add_do_methods(self): + def _add_command_methods(self): for command in self.hooke.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) - def do_fn(self, args): - pass - setattr(self.__class__, 'do_%s' % name, do_fn) - def help_fn(self): - pass - setattr(self.__class__, 'help_%s' % name, help_fn) - def complete_fn(self, text, line, begidx, endidx): - pass - setattr(self.__class__, 'complete_%s' % name, complete_fn) - - def _help(self, name, aliases, message, usage_args=None): - if len(aliases) == 0: - alias_string = '' - else: - alias_string = ' (%s)' % ', '.join(aliases) - if usage_args == None: - usage_string = name - else: - usage_string = '%s %s' % (name, usage_args) - self.stdout.write((""" -%s%s - -%s ------- -Usage: %s -""" % (name, alias_string, message.strip(), usage_string)).lstrip()) - - def help_help(self): - return self._help('help', [], """ + 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. -""", - '[command]') - - def do_exit(self, args): - return True # the True return stops .cmdloop execution - - def help_exit(self): - return self._help('exit', ['quit', 'EOF'], "Exit Hooke cleanly.") - - def do_quit(self, args): - return self.do_exit(args) - - def help_quit(self): - return self.help_exit() - - def do_EOF(self, args): - return self.do_exit(args) +""".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)) - def help_EOF(self): - return self.help_exit() class CommandLine (UserInterface): """Command line interface. Simple and powerful. @@ -83,5 +114,7 @@ class CommandLine (UserInterface): super(CommandLine, self).__init__(name='command line') def run(self, hooke, ui_to_command_queue, command_to_ui_queue): - cmd = HookeCmd(hooke) + cmd = HookeCmd(hooke, + inqueue=ui_to_command_queue, + outqueue=command_to_ui_queue) cmd.cmdloop(self._splash_text()) -- 2.26.2