import readline # including readline makes cmd.Cmd.cmdloop() smarter
import shlex
-from ..command import CommandExit, Command, Argument
+from ..command import CommandExit, Exit, BooleanRequest, BooleanResponse, \
+ Command, Argument, StoreValue
from ..ui import UserInterface, CommandMessage
# Define a few helper classes.
+class Default (object):
+ """Marker for options not given on the command line.
+ """
+ pass
+
class CommandLineParser (optparse.OptionParser):
"""Implement a command line syntax for a
:class:`hooke.command.Command`.
if a.optional == True:
name = name_fn(a.name)
self.add_option(
- '--%s' % name, dest=name, default=a.default)
+ '--%s' % name, dest=name, default=Default)
self.command_opts.append(a)
else:
self.command_args.append(a)
self.cmd.inqueue.put(CommandMessage(self.command, args))
while True:
msg = self.cmd.outqueue.get()
- if isinstance(msg, CommandExit):
+ if isinstance(msg, Exit):
+ return True
+ elif isinstance(msg, CommandExit):
self.cmd.stdout.write(msg.__class__.__name__+'\n')
self.cmd.stdout.write(str(msg).rstrip()+'\n')
break
+ elif isinstance(msg, BooleanRequest):
+ self._boolean_request(msg)
+ continue
self.cmd.stdout.write(str(msg).rstrip()+'\n')
def _parse_args(self, args):
len(self.parser.command_args)))
params = {}
for argument in self.parser.command_opts:
- params[argument.name] = getattr(options,
- self.name_fn(argument.name))
+ value = getattr(options, self.name_fn(argument.name))
+ if value != Default:
+ params[argument.name] = value
for i,argument in enumerate(self.parser.command_args):
params[argument.name] = args[i]
return params
+ def _boolean_request(self, msg):
+ if msg.default == True:
+ yn = ' [Y/n] '
+ else:
+ yn = ' [y/N] '
+ self.cmd.stdout.write(msg.msg+yn)
+ response = self.cmd.stdin.readline().strip().lower()
+ if response.startswith('y'):
+ self.cmd.inqueue.put(BooleanResponse(True))
+ elif response.startswith('n'):
+ self.cmd.inqueue.put(BooleanResponse(False))
+ else:
+ self.cmd.inqueue.put(BooleanResponse(msg.default))
+
class HelpCommand (CommandMethod):
def __init__(self, *args, **kwargs):
super(HelpCommand, self).__init__(*args, **kwargs)
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,
+ callback=StoreValue(True), 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:
+ # TODO: get results of hooke.playlists.current().is_saved()
+ is_saved = True
+ msg = 'Exit?'
+ default = True
+ if is_saved == False:
+ msg = 'You did not save your playlist. ' + msg
+ default = False
+ outqueue.put(BooleanRequest(msg, default))
+ result = inqueue.get()
+ assert isinstance(result, BooleanResponse)
+ _exit = result.value
+ if _exit == True:
+ raise Exit()
+
+
# Now onto the main attraction.
class HookeCmd (cmd.Cmd):
def __init__(self, commands, inqueue, outqueue):
cmd.Cmd.__init__(self)
self.commands = commands
+ self.local_commands = [LocalExitCommand(), LocalHelpCommand()]
self.prompt = 'hooke> '
self._add_command_methods()
self.inqueue = inqueue
self.outqueue = outqueue
def _name_fn(self, name):
- return name.lower().replace(' ', '_')
+ return name.replace(' ', '_')
def _add_command_methods(self):
- for command in self.commands:
+ for command in self.commands + self.local_commands:
for name in [command.name] + command.aliases:
name = self._name_fn(name)
- setattr(self.__class__, 'do_%s' % name,
- DoCommand(self, command, self._name_fn))
setattr(self.__class__, 'help_%s' % name,
HelpCommand(self, command, self._name_fn))
- setattr(self.__class__, 'complete_%s' % name,
- CompleteCommand(self, command, self._name_fn))
-
- exit_command = Command(
- name='exit', aliases=['quit', 'EOF'],
- help='Exit Hooke cleanly.')
- exit_command.arguments = [] # remove help argument
- for name in [exit_command.name] + exit_command.aliases:
- setattr(self.__class__, 'do_%s' % name,
- lambda self, args : True)
- # the True return stops .cmdloop execution
- setattr(self.__class__, 'help_%s' % name,
- HelpCommand(self, exit_command, self._name_fn))
-
- help_command = Command(
- name='help',
- help="""
-Called with an argument, prints that command's documentation.
-
-With no argument, lists all available help topics as well as any
-undocumented commands.
-""".strip())
- help_command.arguments = [ # overwrite help argument
- Argument(name='command', type='string', optional=True,
- help='The name of the command you want help with.')
- ]
- setattr(self.__class__, 'help_help',
- HelpCommand(self, help_command, self._name_fn))
+ if name != 'help':
+ setattr(self.__class__, 'do_%s' % name,
+ DoCommand(self, command, self._name_fn))
+ setattr(self.__class__, 'complete_%s' % name,
+ CompleteCommand(self, command, self._name_fn))
class CommandLine (UserInterface):