1 """Defines :class:`CommandLine` for driving Hooke from the command
7 import readline # including readline makes cmd.Cmd.cmdloop() smarter
10 from ..command import CommandExit, Command, Argument
11 from ..compat.odict import odict
12 from ..ui import UserInterface, CommandMessage
15 # Define a few helper classes.
17 class CommandLineParser (optparse.OptionParser):
18 """Implement a command line syntax for a
19 :class:`hooke.command.Command`.
21 def __init__(self, command):
22 optparse.OptionParser.__init__(self, prog=command.name)
23 self.command = command
24 self.command_opts = odict()
25 self.command_args = odict()
26 for a in command.arguments:
28 continue # 'help' is a default OptionParser option
29 name = a.name.replace('_', '-')
30 if a.optional == True:
31 self.add_option('--%s' % name, dest=a.name, default=a.default)
32 self.command_opts[name] = a
34 self.command_args[name] = a
36 def exit(self, status=0, msg=None):
37 """Override :meth:`optparse.OptionParser.exit` which calls
41 raise optparse.OptParseError(msg)
42 raise optparse.OptParseError('OptParse EXIT')
44 class CommandMethod (object):
45 """Base class for method replacer.
47 The .__call__ methods of `CommandMethod` subclasses functions will
48 provide the `do_*`, `help_*`, and `complete_*` methods of
51 def __init__(self, cmd, command):
53 self.command = command
55 def __call__(self, *args, **kwargs):
56 raise NotImplementedError
58 class DoCommand (CommandMethod):
59 def __init__(self, *args, **kwargs):
60 super(DoCommand, self).__init__(*args, **kwargs)
61 self.parser = CommandLineParser(self.command)
63 def __call__(self, args):
65 args = self._parse_args(self.command, args)
66 except optparse.OptParseError, e:
67 self.cmd.stdout.write(str(e))
68 self.cmd.stdout.write('Failure\n')
70 self.cmd.inqueue.put(CommandMessage(self.command, args))
72 msg = self.cmd.outqueue.get()
73 if isinstance(msg, CommandExit):
74 self.cmd.stdout.write(msg.__class__.__name__+'\n')
75 self.cmd.stdout.write(str(msg).rstrip()+'\n')
77 self.cmd.stdout.write(str(msg).rstrip()+'\n')
79 def _parse_args(self, command, args):
80 argv = shlex.split(args, comments=True, posix=True)
81 options,args = self.parser.parse_args(argv)
83 for name,argument in self.parser.command_opts.items():
84 params[name] = getattr(options, name)
85 for name,argument in self.parser.command_args.items():
89 class HelpCommand (CommandMethod):
90 def __init__(self, *args, **kwargs):
91 super(HelpCommand, self).__init__(*args, **kwargs)
92 self.parser = CommandLineParser(self.command)
95 blocks = [self.command.help(),
97 'Usage: ' + self._usage_string(),
99 self.cmd.stdout.write('\n'.join(blocks))
102 return self.command.help()
104 def _usage_string(self):
105 if len(self.parser.command_opts) == 0:
108 options_string = '[options]'
109 arg_string = ' '.join([name for name,arg
110 in self.parser.command_args.items()])
111 return ' '.join([x for x in [self.command.name,
116 class CompleteCommand (CommandMethod):
117 def __call__(self, text, line, begidx, endidx):
121 # Now onto the main attraction.
123 class HookeCmd (cmd.Cmd):
124 def __init__(self, commands, inqueue, outqueue):
125 cmd.Cmd.__init__(self)
126 self.commands = commands
127 self.prompt = 'hooke> '
128 self._add_command_methods()
129 self.inqueue = inqueue
130 self.outqueue = outqueue
132 def _safe_name(self, name):
133 return name.lower().replace(' ', '_')
135 def _add_command_methods(self):
136 for command in self.commands:
137 command.name = self._safe_name(command.name)
138 command.aliases = [self._safe_name(n) for n in command.aliases]
139 for name in [command.name] + command.aliases:
140 name = self._safe_name(name)
141 setattr(self.__class__, 'do_%s' % name,
142 DoCommand(self, command))
143 setattr(self.__class__, 'help_%s' % name,
144 HelpCommand(self, command))
145 setattr(self.__class__, 'complete_%s' % name,
146 CompleteCommand(self, command))
148 exit_command = Command(
149 name='exit', aliases=['quit', 'EOF'],
150 help='Exit Hooke cleanly.')
151 exit_command.arguments = [] # remove help argument
152 for name in [exit_command.name] + exit_command.aliases:
153 setattr(self.__class__, 'do_%s' % name,
154 lambda self, args : True)
155 # the True return stops .cmdloop execution
156 setattr(self.__class__, 'help_%s' % name,
157 HelpCommand(self, exit_command))
159 help_command = Command(
162 Called with an argument, prints that command's documentation.
164 With no argument, lists all available help topics as well as any
165 undocumented commands.
167 help_command.arguments = [ # overwrite help argument
168 Argument(name='command', type='string', optional=True,
169 help='The name of the command you want help with.')
171 setattr(self.__class__, 'help_help',
172 HelpCommand(self, help_command))
175 class CommandLine (UserInterface):
176 """Command line interface. Simple and powerful.
179 super(CommandLine, self).__init__(name='command line')
181 def run(self, commands, ui_to_command_queue, command_to_ui_queue):
182 cmd = HookeCmd(commands,
183 inqueue=ui_to_command_queue,
184 outqueue=command_to_ui_queue)
185 cmd.cmdloop(self._splash_text())