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. The .__call__ methods of these
15 # functions will provide the do_*, help_*, and complete_* methods of
18 class CommandMethod (object):
19 def __init__(self, cmd, command):
21 self.command = command
23 def __call__(self, *args, **kwargs):
24 raise NotImplementedError
26 def command_parser(command):
27 p = optparse.OptionParser()
30 for a in command.arguments:
32 continue # 'help' is a default OptionParser option
33 name = a.name.replace('_', '-')
34 if a.optional == True:
35 p.add_option('--%s' % name, dest=a.name, default=a.default)
36 opts.append((name, a))
38 args.append((name, a))
39 return (p, opts, args)
41 class DoCommand (CommandMethod):
42 def __init__(self, *args, **kwargs):
43 super(DoCommand, self).__init__(*args, **kwargs)
44 self.parser,self.opts,self.args = command_parser(self.command)
46 def __call__(self, args):
47 args = self._parse_args(self.command, args)
48 self.cmd.inqueue.put(CommandMessage(self.command, args))
50 msg = self.cmd.outqueue.get()
51 if isinstance(msg, CommandExit):
53 self.cmd.stdout.write(str(msg).rstrip()+'\n')
55 def _parse_args(self, command, args):
56 argv = shlex.split(args, comments=True, posix=True)
57 options,args = self.parser.parse_args(argv)
59 for namearg in self.opts:
60 name,argument = namearg
61 params[name] = getattr(options, name)
62 for namearg,value in zip(self.args, args):
63 name,argument = namearg
67 class HelpCommand (CommandMethod):
68 def __init__(self, *args, **kwargs):
69 super(HelpCommand, self).__init__(*args, **kwargs)
70 self.parser,self.opts,self.args = command_parser(self.command)
73 blocks = [self.command.help(),
75 'Usage: ' + self._usage_string(),
77 self.cmd.stdout.write('\n'.join(blocks))
80 return self.command.help()
82 def _usage_string(self):
83 if len(self.args) == len(self.command.arguments):
86 options_string = '[options]'
87 arg_string = ' '.join([name for name,arg in self.args])
88 return ' '.join([x for x in [self.command.name,
93 class CompleteCommand (CommandMethod):
94 def __call__(self, text, line, begidx, endidx):
98 # Now onto the main attraction.
100 class HookeCmd (cmd.Cmd):
101 def __init__(self, commands, inqueue, outqueue):
102 cmd.Cmd.__init__(self)
103 self.commands = commands
104 self.prompt = 'hooke> '
105 self._add_command_methods()
106 self.inqueue = inqueue
107 self.outqueue = outqueue
109 def _safe_name(self, name):
110 return name.lower().replace(' ', '_')
112 def _add_command_methods(self):
113 for command in self.commands:
114 command.name = self._safe_name(command.name)
115 command.aliases = [self._safe_name(n) for n in command.aliases]
116 for name in [command.name] + command.aliases:
117 name = self._safe_name(name)
118 setattr(self.__class__, 'do_%s' % name,
119 DoCommand(self, command))
120 setattr(self.__class__, 'help_%s' % name,
121 HelpCommand(self, command))
122 setattr(self.__class__, 'complete_%s' % name,
123 CompleteCommand(self, command))
125 exit_command = Command(
126 name='exit', aliases=['quit', 'EOF'],
127 help='Exit Hooke cleanly.')
128 exit_command.arguments = [] # remove help argument
129 for name in [exit_command.name] + exit_command.aliases:
130 setattr(self.__class__, 'do_%s' % name,
131 lambda self, args : True)
132 # the True return stops .cmdloop execution
133 setattr(self.__class__, 'help_%s' % name,
134 HelpCommand(self, exit_command))
136 help_command = Command(
139 Called with an argument, prints that command's documentation.
141 With no argument, lists all available help topics as well as any
142 undocumented commands.
144 help_command.arguments = [ # overwrite help argument
145 Argument(name='command', type='string', optional=True,
146 help='The name of the command you want help with.')
148 setattr(self.__class__, 'help_help',
149 HelpCommand(self, help_command))
152 class CommandLine (UserInterface):
153 """Command line interface. Simple and powerful.
156 super(CommandLine, self).__init__(name='command line')
158 def run(self, hooke, ui_to_command_queue, command_to_ui_queue):
159 cmd = HookeCmd(hooke,
160 inqueue=ui_to_command_queue,
161 outqueue=command_to_ui_queue)
162 cmd.cmdloop(self._splash_text())