X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fui%2Fcommandline.py;h=4b4914398b896682b0b6edee0e30ec3432e6a407;hp=9b2effe5695623419621f18a8a0f79e6d517b81c;hb=45c5d07228fbe9cefd209012849faa86dd7a020f;hpb=aa94795b6f66934ecbb1b0d8c5561f8916dfd1bd diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index 9b2effe..4b49143 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -1,15 +1,42 @@ +# Copyright (C) 2010 W. Trevor King +# +# This file is part of Hooke. +# +# Hooke is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Hooke is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General +# Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with Hooke. If not, see +# . + """Defines :class:`CommandLine` for driving Hooke from the command line. """ +import codecs import cmd +import logging import optparse -import readline # including readline makes cmd.Cmd.cmdloop() smarter +try: + import readline # including readline makes cmd.Cmd.cmdloop() smarter +except ImportError, e: + import logging + logging.warn('Could not import readline, bash-like line editing disabled.') import shlex from ..command import CommandExit, Exit, Command, Argument, StoreValue +from ..engine import CommandMessage from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig -from ..ui import UserInterface, CommandMessage +from ..ui import UserInterface +from ..util.convert import from_string +from ..util.encoding import get_input_encoding, get_output_encoding # Define a few helper classes. @@ -33,8 +60,27 @@ class CommandLineParser (optparse.OptionParser): continue # 'help' is a default OptionParser option if a.optional == True: name = name_fn(a.name) + type = a.type + if type == 'bool': + if a.default == True: + self.add_option( + '--disable-%s' % name, dest=name, default=Default, + action='store_false') + self.command_opts.append(a) + continue + elif a.default == False: + self.add_option( + '--enable-%s' % name, dest=name, default=Default, + action='store_true') + self.command_opts.append(a) + continue + else: + type = 'string' + elif type not in ['string', 'int', 'long', 'choice', 'float', + 'complex']: + type = 'string' self.add_option( - '--%s' % name, dest=name, default=Default) + '--%s' % name, dest=name, type=type, default=Default) self.command_opts.append(a) else: self.command_args.append(a) @@ -74,6 +120,7 @@ class DoCommand (CommandMethod): def __init__(self, *args, **kwargs): super(DoCommand, self).__init__(*args, **kwargs) self.parser = CommandLineParser(self.command, self.name_fn) + self.log = logging.getLogger('hooke') def __call__(self, args): try: @@ -82,6 +129,7 @@ class DoCommand (CommandMethod): self.cmd.stdout.write(str(e).lstrip()+'\n') self.cmd.stdout.write('Failure\n') return + self.log.debug('executing %s with %s' % (self.command.name, args)) self.cmd.inqueue.put(CommandMessage(self.command, args)) while True: msg = self.cmd.outqueue.get() @@ -100,8 +148,7 @@ class DoCommand (CommandMethod): self.cmd.stdout.write(str(msg).rstrip()+'\n') def _parse_args(self, args): - argv = shlex.split(args, comments=True, posix=True) - options,args = self.parser.parse_args(argv) + options,args = self.parser.parse_args(args) self._check_argument_length_bounds(args) params = {} for argument in self.parser.command_opts: @@ -111,12 +158,15 @@ class DoCommand (CommandMethod): arg_index = 0 for argument in self.parser.command_args: if argument.count == 1: - params[argument.name] = args[arg_index] + params[argument.name] = from_string(args[arg_index], + argument.type) elif argument.count > 1: - params[argument.name] = \ - args[arg_index:arg_index+argument.count] + params[argument.name] = [ + from_string(a, argument.type) + for a in args[arg_index:arg_index+argument.count]] else: # argument.count == -1: - params[argument.name] = args[arg_index:] + params[argument.name] = [ + from_string(a, argument.type) for a in args[arg_index:]] arg_index += argument.count return params @@ -198,12 +248,17 @@ class DoCommand (CommandMethod): return msg.msg + d def _string_request_parser(self, msg, response): + response = response.strip() + if response == '': + return msg.default return response.strip() def _float_request_prompt(self, msg): return self._string_request_prompt(msg) def _float_request_parser(self, msg, resposne): + if response.strip() == '': + return msg.default return float(response) def _selection_request_prompt(self, msg): @@ -215,9 +270,29 @@ class DoCommand (CommandMethod): prompt = '? ' else: prompt = '? [%d] ' % msg.default - return '\n'.join([msg,options,prompt]) + return '\n'.join([msg.msg,options,prompt]) def _selection_request_parser(self, msg, response): + if response.strip() == '': + return msg.default + return int(response) + + def _point_request_prompt(self, msg): + block = msg.curve.data[msg.block] + block_info = ('(curve: %s, block: %s, %d points)' + % (msg.curve.name, + block.info['name'], + block.shape[0])) + + if msg.default == None: + prompt = '? ' + else: + prompt = '? [%d] ' % msg.default + return ' '.join([msg.msg,block_info,prompt]) + + def _point_request_parser(self, msg, response): + if response.strip() == '': + return msg.default return int(response) @@ -339,6 +414,45 @@ class HookeCmd (cmd.Cmd): setattr(self.__class__, 'complete_%s' % name, CompleteCommand(self, command, self._name_fn)) + def parseline(self, line): + """Override Cmd.parseline to use shlex.split. + + Notes + ----- + This allows us to handle comments cleanly. With the default + Cmd implementation, a pure comment line will call the .default + error message. + + Since we use shlex to strip comments, we return a list of + split arguments rather than the raw argument string. + """ + line = line.strip() + argv = shlex.split(line, comments=True, posix=True) + if len(argv) == 0: + return None, None, '' # return an empty line + cmd = argv[0] + args = argv[1:] + if cmd == '?': + cmd = 'help' + elif cmd == '!': + cmd = 'system' + return cmd, args, line + + def do_help(self, arg): + """Wrap Cmd.do_help to handle our .parseline argument list. + """ + if len(arg) == 0: + return cmd.Cmd.do_help(self, '') + return cmd.Cmd.do_help(self, arg[0]) + + def emptyline(self): + """Override Cmd.emptyline to not do anything. + + Repeating the last non-empty command seems unwise. Explicit + is better than implicit. + """ + pass + class CommandLine (UserInterface): """Command line interface. Simple and powerful. @@ -346,16 +460,22 @@ class CommandLine (UserInterface): def __init__(self): super(CommandLine, self).__init__(name='command line') - def run(self, commands, ui_to_command_queue, command_to_ui_queue): + def _cmd(self, commands, ui_to_command_queue, command_to_ui_queue): cmd = HookeCmd(self, commands, inqueue=ui_to_command_queue, outqueue=command_to_ui_queue) - cmd.cmdloop(self._splash_text()) + #cmd.stdin = codecs.getreader(get_input_encoding())(cmd.stdin) + cmd.stdout = codecs.getwriter(get_output_encoding())(cmd.stdout) + return cmd + + def run(self, commands, ui_to_command_queue, command_to_ui_queue): + cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue) + cmd.cmdloop(self._splash_text(extra_info={ + 'get-details':'run `license`', + })) def run_lines(self, commands, ui_to_command_queue, command_to_ui_queue, lines): - cmd = HookeCmd(self, commands, - inqueue=ui_to_command_queue, - outqueue=command_to_ui_queue) + cmd = self._cmd(commands, ui_to_command_queue, command_to_ui_queue) for line in lines: cmd.onecmd(line)