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 ..ui import UserInterface, CommandMessage
14 # Define a few helper classes.
16 class CommandLineParser (optparse.OptionParser):
17 """Implement a command line syntax for a
18 :class:`hooke.command.Command`.
20 def __init__(self, command, name_fn):
21 optparse.OptionParser.__init__(self, prog=name_fn(command.name))
22 self.command = command
23 self.command_opts = []
24 self.command_args = []
25 for a in command.arguments:
27 continue # 'help' is a default OptionParser option
28 if a.optional == True:
29 name = name_fn(a.name)
31 '--%s' % name, dest=name, default=a.default)
32 self.command_opts.append(a)
34 self.command_args.append(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, name_fn):
53 self.command = command
54 self.name_fn = name_fn
56 def __call__(self, *args, **kwargs):
57 raise NotImplementedError
59 class DoCommand (CommandMethod):
60 def __init__(self, *args, **kwargs):
61 super(DoCommand, self).__init__(*args, **kwargs)
62 self.parser = CommandLineParser(self.command, self.name_fn)
64 def __call__(self, args):
66 args = self._parse_args(args)
67 except optparse.OptParseError, e:
68 self.cmd.stdout.write(str(e).lstrip()+'\n')
69 self.cmd.stdout.write('Failure\n')
71 self.cmd.inqueue.put(CommandMessage(self.command, args))
73 msg = self.cmd.outqueue.get()
74 if isinstance(msg, CommandExit):
75 self.cmd.stdout.write(msg.__class__.__name__+'\n')
76 self.cmd.stdout.write(str(msg).rstrip()+'\n')
78 self.cmd.stdout.write(str(msg).rstrip()+'\n')
80 def _parse_args(self, args):
81 argv = shlex.split(args, comments=True, posix=True)
82 options,args = self.parser.parse_args(argv)
83 if len(args) != len(self.parser.command_args):
84 raise optparse.OptParseError('%d arguments given, but %s takes %d'
86 self.name_fn(self.command.name),
87 len(self.parser.command_args)))
89 for argument in self.parser.command_opts:
90 params[argument.name] = getattr(options,
91 self.name_fn(argument.name))
92 for i,argument in enumerate(self.parser.command_args):
93 params[argument.name] = args[i]
96 class HelpCommand (CommandMethod):
97 def __init__(self, *args, **kwargs):
98 super(HelpCommand, self).__init__(*args, **kwargs)
99 self.parser = CommandLineParser(self.command, self.name_fn)
102 blocks = [self.command.help(name_fn=self.name_fn),
104 'Usage: ' + self._usage_string(),
106 self.cmd.stdout.write('\n'.join(blocks))
109 return self.command.help(name_fn=self.name_fn)
111 def _usage_string(self):
112 if len(self.parser.command_opts) == 0:
115 options_string = '[options]'
116 arg_string = ' '.join(
117 [self.name_fn(arg.name) for arg in self.parser.command_args])
118 return ' '.join([x for x in [self.parser.prog,
123 class CompleteCommand (CommandMethod):
124 def __call__(self, text, line, begidx, endidx):
128 # Now onto the main attraction.
130 class HookeCmd (cmd.Cmd):
131 def __init__(self, commands, inqueue, outqueue):
132 cmd.Cmd.__init__(self)
133 self.commands = commands
134 self.prompt = 'hooke> '
135 self._add_command_methods()
136 self.inqueue = inqueue
137 self.outqueue = outqueue
139 def _name_fn(self, name):
140 return name.lower().replace(' ', '_')
142 def _add_command_methods(self):
143 for command in self.commands:
144 for name in [command.name] + command.aliases:
145 name = self._name_fn(name)
146 setattr(self.__class__, 'do_%s' % name,
147 DoCommand(self, command, self._name_fn))
148 setattr(self.__class__, 'help_%s' % name,
149 HelpCommand(self, command, self._name_fn))
150 setattr(self.__class__, 'complete_%s' % name,
151 CompleteCommand(self, command, self._name_fn))
153 exit_command = Command(
154 name='exit', aliases=['quit', 'EOF'],
155 help='Exit Hooke cleanly.')
156 exit_command.arguments = [] # remove help argument
157 for name in [exit_command.name] + exit_command.aliases:
158 setattr(self.__class__, 'do_%s' % name,
159 lambda self, args : True)
160 # the True return stops .cmdloop execution
161 setattr(self.__class__, 'help_%s' % name,
162 HelpCommand(self, exit_command, self._name_fn))
164 help_command = Command(
167 Called with an argument, prints that command's documentation.
169 With no argument, lists all available help topics as well as any
170 undocumented commands.
172 help_command.arguments = [ # overwrite help argument
173 Argument(name='command', type='string', optional=True,
174 help='The name of the command you want help with.')
176 setattr(self.__class__, 'help_help',
177 HelpCommand(self, help_command, self._name_fn))
180 class CommandLine (UserInterface):
181 """Command line interface. Simple and powerful.
184 super(CommandLine, self).__init__(name='command line')
186 def run(self, commands, ui_to_command_queue, command_to_ui_queue):
187 cmd = HookeCmd(commands,
188 inqueue=ui_to_command_queue,
189 outqueue=command_to_ui_queue)
190 cmd.cmdloop(self._splash_text())