X-Git-Url: http://git.tremily.us/?p=hooke.git;a=blobdiff_plain;f=hooke%2Fui%2Fcommandline.py;h=be0f14a8b02184dc4ecf1551587a152d09a0605a;hp=4b4914398b896682b0b6edee0e30ec3432e6a407;hb=be1cce68dfffc3f65de215c1e6fe010b84eab14b;hpb=45c5d07228fbe9cefd209012849faa86dd7a020f diff --git a/hooke/ui/commandline.py b/hooke/ui/commandline.py index 4b49143..be0f14a 100644 --- a/hooke/ui/commandline.py +++ b/hooke/ui/commandline.py @@ -28,12 +28,13 @@ 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.') + logging.warn('could not import readline, bash-like line editing disabled.') import shlex +import sys from ..command import CommandExit, Exit, Command, Argument, StoreValue -from ..engine import CommandMessage -from ..interaction import Request, BooleanRequest, ReloadUserInterfaceConfig +from ..engine import CommandMessage, CloseEngine +from ..interaction import EOFResponse, Request, ReloadUserInterfaceConfig from ..ui import UserInterface from ..util.convert import from_string from ..util.encoding import get_input_encoding, get_output_encoding @@ -41,6 +42,13 @@ from ..util.encoding import get_input_encoding, get_output_encoding # Define a few helper classes. +class EOF (EOFError): + """Raise upon reaching the end of the input file. + + After this point, no more user interaction is possible. + """ + pass + class Default (object): """Marker for options not given on the command line. """ @@ -63,15 +71,25 @@ class CommandLineParser (optparse.OptionParser): type = a.type if type == 'bool': if a.default == True: - self.add_option( - '--disable-%s' % name, dest=name, default=Default, - action='store_false') + try: + self.add_option( + '--disable-%s' % name, dest=name, + default=Default, action='store_false', + help=self._argument_help(a)) + except optparse.OptionConflictError, e: + logging.warn('error in %s: %s' % (command, e)) + raise self.command_opts.append(a) continue elif a.default == False: - self.add_option( - '--enable-%s' % name, dest=name, default=Default, - action='store_true') + try: + self.add_option( + '--enable-%s' % name, dest=name, + default=Default, action='store_true', + help=self._argument_help(a)) + except optparse.OptionConflictError, e: + logging.warn('error in %s: %s' % (command, e)) + raise self.command_opts.append(a) continue else: @@ -79,8 +97,13 @@ class CommandLineParser (optparse.OptionParser): elif type not in ['string', 'int', 'long', 'choice', 'float', 'complex']: type = 'string' - self.add_option( - '--%s' % name, dest=name, type=type, default=Default) + try: + self.add_option( + '--%s' % name, dest=name, type=type, default=Default, + help=self._argument_help(a)) + except optparse.OptionConflictError, e: + logging.warn('error in %s: %s' % (command, e)) + raise self.command_opts.append(a) else: self.command_args.append(a) @@ -93,6 +116,10 @@ class CommandLineParser (optparse.OptionParser): self.command_args.remove(infinite_counter) self.command_args.append(infinite_counter) + def _argument_help(self, argument): + return '%s (%s)' % (argument._help, argument.default) + # default in the case of callbacks, config-backed values, etc.? + def exit(self, status=0, msg=None): """Override :meth:`optparse.OptionParser.exit` which calls :func:`sys.exit`. @@ -120,7 +147,6 @@ 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: @@ -129,8 +155,8 @@ 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)) + cm = CommandMessage(self.command.name, args) + self.cmd.ui._submit_command(cm, self.cmd.inqueue) while True: msg = self.cmd.outqueue.get() if isinstance(msg, Exit): @@ -143,9 +169,12 @@ class DoCommand (CommandMethod): self.cmd.ui.reload_config(msg.config) continue elif isinstance(msg, Request): - self._handle_request(msg) + try: + self._handle_request(msg) + except EOF: + return True continue - self.cmd.stdout.write(str(msg).rstrip()+'\n') + self.cmd.stdout.write(unicode(msg).rstrip()+'\n') def _parse_args(self, args): options,args = self.parser.parse_args(args) @@ -215,7 +244,17 @@ class DoCommand (CommandMethod): self.cmd.stdout.write(''.join([ error.__class__.__name__, ': ', str(error), '\n'])) self.cmd.stdout.write(prompt_string) - value = parser(msg, self.cmd.stdin.readline()) + stdin = sys.stdin + try: + sys.stdin = self.cmd.stdin + raw_response = raw_input() + except EOFError, e: + self.cmd.inqueue.put(EOFResponse()) + self.cmd.inqueue.put(CloseEngine()) + raise EOF + finally: + sys.stdin = stdin + value = parser(msg, raw_response) try: response = msg.response(value) break @@ -297,19 +336,22 @@ class DoCommand (CommandMethod): class HelpCommand (CommandMethod): + """Supersedes :class:`hooke.plugin.engine.HelpCommand`. + """ def __init__(self, *args, **kwargs): super(HelpCommand, self).__init__(*args, **kwargs) self.parser = CommandLineParser(self.command, self.name_fn) def __call__(self): - blocks = [self.command.help(name_fn=self.name_fn), + blocks = [self.parser.format_help(), + self._command_message(), '----', 'Usage: ' + self._usage_string(), ''] self.cmd.stdout.write('\n'.join(blocks)) - def _message(self): - return self.command.help(name_fn=self.name_fn) + def _command_message(self): + return self.command._help def _usage_string(self): if len(self.parser.command_opts) == 0: @@ -328,63 +370,6 @@ class CompleteCommand (CommandMethod): pass -# Define some additional commands - -class LocalHelpCommand (Command): - """Called with an argument, prints that command's documentation. - - With no argument, lists all available help topics as well as any - undocumented commands. - """ - def __init__(self): - super(LocalHelpCommand, self).__init__(name='help', help=self.__doc__) - # We set .arguments now (vs. using th arguments option to __init__), - # to overwrite the default help argument. We don't override - # :meth:`cmd.Cmd.do_help`, so `help --help` is not a valid command. - self.arguments = [ - Argument(name='command', type='string', optional=True, - help='The name of the command you want help with.') - ] - - def _run(self, hooke, inqueue, outqueue, params): - raise NotImplementedError # cmd.Cmd already implements .do_help() - -class LocalExitCommand (Command): - """Exit Hooke cleanly. - """ - def __init__(self): - super(LocalExitCommand, self).__init__( - name='exit', aliases=['quit', 'EOF'], help=self.__doc__, - arguments = [ - Argument(name='force', type='bool', default=False, - help=""" -Exit without prompting the user. Use if you save often or don't make -typing mistakes ;). -""".strip()), - ]) - - def _run(self, hooke, inqueue, outqueue, params): - """The guts of the `do_exit/_quit/_EOF` commands. - - A `True` return stops :meth:`.cmdloop` execution. - """ - _exit = True - if params['force'] == False: - not_saved = [p.name for p in hooke.playlists - if p.is_saved() == False] - msg = 'Exit?' - default = True - if len(not_saved) > 0: - msg = 'Unsaved playlists (%s). %s' \ - % (', '.join([str(p) for p in not_saved]), msg) - default = False - outqueue.put(BooleanRequest(msg, default)) - result = inqueue.get() - assert result.type == 'boolean' - _exit = result.value - if _exit == True: - raise Exit() - # Now onto the main attraction. @@ -393,7 +378,6 @@ class HookeCmd (cmd.Cmd): cmd.Cmd.__init__(self) self.ui = ui self.commands = commands - self.local_commands = [LocalExitCommand(), LocalHelpCommand()] self.prompt = 'hooke> ' self._add_command_methods() self.inqueue = inqueue @@ -403,7 +387,9 @@ class HookeCmd (cmd.Cmd): return name.replace(' ', '_') def _add_command_methods(self): - for command in self.commands + self.local_commands: + for command in self.commands: + if command.name == 'exit': + command.aliases.extend(['quit', 'EOF']) for name in [command.name] + command.aliases: name = self._name_fn(name) setattr(self.__class__, 'help_%s' % name,