class CommandParser(object):
    """Class defining multiple command line parsers

    This parser can process command lines of the form

    PROG CMD OPTIONS ARGS

    where different sets of options can be defined based on the major
    command ('CMD') supplied at the start of the line.

    Usage:

    Create a simple CommandParser which uses optparse.OptionParser as
    the default subparser backend using:

    >>> p = CommandParser()
    
    Alternatively, specify argparse.ArgumentParser as the subparser
    using:

    >>> p = CommandParser(subparser=argparser.ArgumentParser)

    Add a 'setup' command:

    >>> p.add_command('setup',usage='%prog setup OPTIONS ARGS')

    Add options to the 'setup' command using the appropriate methods
    of the subparser (e.g. 'add_argument' for an
    ArgumentParser instance).

    For example:

    >>> p.parser_for('info').add_argument('-f',...)

    To process a command line, use the 'parse_args' method, for
    example for an OptionParser-based subparser:

    >>> cmd,options,args = p.parse_args()

    Note that the exact form of the returned values depends on
    on the subparser instance; it will be the same as that
    returned by the 'parse_args' method of the subparser.

    """
    def __init__(self, description=None, version=None, subparser=None):
        """Create a command line parser
        with 'subparser' as the backend (default=OptionParser)
        This parser can process command lines of the form

        PROG CMD OPTIONS ARGS

        where different sets of options can be defined based
        on the major command supplied at the start.

        """
        self._name = os.path.basename(sys.argv[0])
        self._description = description
        self._version = version
        self._commands = OrderedDictionary()
        self._help = dict()
        if not subparser:
            subparser = optparse.OptionParser
        self._subparser = subparser

    def add_command(self, cmd, help=None, **args):
        """Add a major command to the CommandParser

        Adds a command, and creates and returns an initial
        subparser instance for it.

        Arguments:
          cmd: the command to be added
          help: (optional) help text for the command

        Other arguments are passed to the subparser instance
        when it is created i.e. 'usage', 'version',
        'description'.

        If 'version' isn't specified then the version
        supplied to the CommandParser object will be used.

        Returns:
          Subparser instance object for the command.

        """
        if cmd in self._commands:
            raise Exception("Command '%s' already defined" % cmd)
        if 'version' not in args:
            args['version'] = self._version
        p = self._subparser(**args)
        self._commands[cmd] = p
        self._help[cmd] = help
        return p

    def parser_for(self, cmd):
        """Return OptionParser for specified command

        Returns:
          The OptionParser object for the specified command.

        """
        return self._commands[cmd]

    def parse_args(self, argv=None):
        """Process a command line

        Parses a command line (either those supplied to the calling
        subprogram e.g. via the Python interpreter, or as a list).

        Once the command is identified from the first argument, the
        remainder of the arguments are passed to the 'parse_args'
        method of the appropriate subparser for that command.

        This method returns a tuple, with the first value being the
        command, and the rest of the values being those returned
        from the 'parse_args' method of the subparser.

        Arguments:
          argv: (optional) a list consisting of a command line.
            If not supplied then defaults to sys.argv[1:].

        Returns:
          A tuple of (cmd,...), where 'cmd' is the command, and '...'
          represents the values returned from the 'parse_args' method
          of the subparser. For example, using the default OptionParser
          backend returns (cmd,options,arguments), where 'options' and
          'arguments' are the options and arguments as returned by
          OptionParser.parse_args; using ArgumentParser as a backend
          returns (cmd,arguments).

        """
        # Collect arguments to process
        if argv is None:
            argv = sys.argv[1:]
        if not argv:
            self.error("Need to supply a command\n%s" %
                       self.print_available_commands())
        # Determine the major command and get the parser
        cmd = argv[0]
        self.handle_generic_commands(cmd)
        try:
            p = self.parser_for(cmd)
        except KeyError:
            # No parser
            self.error("Usage: %s COMMAND [options] [args...]\n\n"
                       "%s: error: no such command: %s" %
                       (self._name, self._name, cmd))
        # Parse the remaining arguments and return
        if isinstance(p, argparse.ArgumentParser):
            options = p.parse_args(argv[1:])
            return (cmd, options)
        # else:
        options, arguments = p.parse_args(argv[1:])
        return (cmd, options, arguments)

    def error(self, message):
        """Exit with error message

        """
        sys.stderr.write("%s\n" % message)
        sys.exit(1)

    def handle_generic_commands(self, cmd):
        """Process 'generic' commands e.g. 'help'

        """
        if cmd in ('-h', '--help', 'help'):
            print "Usage: %s COMMAND [options] [args...]" % self._name
            if self._description is not None:
                print "\n%s" % self._description
            print "%s" % self.print_available_commands()
            sys.exit(0)
        if cmd in ('--version', ):
            if self._version is not None:
                version_str = self._version
                print "%s" % version_str.replace('%prog', self._name)
            sys.exit(0)

    def list_commands(self):
        """Return the list of commands

        """
        return self._commands.keys()

    def print_available_commands(self):
        """Pretty-print available commands

        Returns a 'pretty-printed' string for all options and commands,
        with standard whitespace formatting.

        """
        lines = ["\nOptions:"]
        # Add generic commands
        if self._version is not None:
            lines.append(
                self.print_command("--version",
                                   "show program's version number and exit"))
            lines.append(
                self.print_command("-h, --help, help",
                                   "show this help message and exit"))
        # Add custom commands
        lines.append("\nAvailable commands:")
        for cmd in self.list_commands():
            lines.append(self.print_command(cmd, self._help[cmd]))
        lines.append("")
        return '\n'.join(lines)

    def print_command(self, cmd, message=None):
        """Print a line for a single command

        Returns a 'pretty-printed' line for the specified command
        and text, with standard whitespace formatting.

        """
        text = ['  ', cmd]
        width = 22
        if len(cmd) < width:
            text.append(' ' * (width - len(cmd)))
        else:
            text.append('\n  ' + ' ' * width)
        if message is not None:
            text.append(message)
        return ''.join(text)
Exemple #2
0
class CommandParser:
    """Class defining multiple command line parsers

    This parser can process command lines of the form

    PROG CMD OPTIONS ARGS

    where different sets of options can be defined based on the major
    command ('CMD') supplied at the start of the line.

    Usage:

    Create a simple CommandParser using:

    >>> p = CommandParser()
    
    Add a 'setup' command:

    >>> p.add_command('setup',usage='%prog setup OPTIONS ARGS')

    Add options to the 'setup' command using normal OptionParser methods,
    e.g.

    >>> p.parser_for('info').add_option('-f',...)

    To process a command line use the 'parse_args' method e.g.:

    >>> cmd,options,args = p.parse_args()

    The options and arguments can be accessed using the normal
    methods from optparse.
    
    """
    def __init__(self,description=None,version=None):
        """Create a command line parser

        This parser can process command lines of the form

        PROG CMD OPTIONS ARGS

        where different sets of options can be defined based
        on the major command supplied at the start.

        """
        self._name = os.path.basename(sys.argv[0])
        self._description = description
        self._version = version
        self._commands = OrderedDictionary()
        self._help = dict()

    def add_command(self,cmd,help=None,**args):
        """Add a major command to the CommandParser

        Adds a command and creates an initial OptionParser
        for it.

        Arguments:
          cmd: the command to be added
          help: (optional) help text for the command

        Other arguments are passed to the OptionParser object
        when it is created i.e. 'usage', 'version',
        'description'.

        If 'version' isn't specified then the version
        supplied to the CommandParser object will be used.

        Returns:
          OptionParser object for the command.

        """
        if cmd in self._commands:
            raise Exception("Command '%s' already defined" % cmd)
        if 'version' not in args:
            args['version'] = self._version
        p = optparse.OptionParser(**args)
        self._commands[cmd] = p
        self._help[cmd] = help
        return p

    def parser_for(self,cmd):
        """Return optionparser for specified command

        Returns:
          The OptionParser object for the specified command.

        """
        return self._commands[cmd]

    def parse_args(self,argv=None):
        """Process a command line

        Arguments:
          argv: (optional) a list consisting of a command line.
            If not supplied then defaults to sys.argv[1:].

        Returns:
          A tuple of (cmd,options,args) where 'cmd' is the
          command, and 'options' and 'args' are the options and
          arguments as returned by OptionParser.parse_args.

        """
        # Collect arguments to process
        if argv is None:
            argv = sys.argv[1:]
        if len(argv) == 0:
            self.error("Need to supply a command\n%s" % 
                       self.print_available_commands())
        # Determine the major command and get the parser
        cmd = argv[0]
        self.handle_generic_commands(cmd)
        try:
            p = self.parser_for(cmd)
        except KeyError:
            # No parser
            self.error("Usage: %s COMMAND [options] [args...]\n\n"
                       "%s: error: no such command: %s" % 
                       (self._name,self._name,cmd))
        # Parse the remaining arguments and return
        options,arguments = p.parse_args(argv[1:])
        return (cmd,options,arguments)

    def error(self,message):
        """Exit with error message

        """
        sys.stderr.write("%s\n" % message)
        sys.exit(1)

    def handle_generic_commands(self,cmd):
        """Process 'generic' commands e.g. 'help'

        """
        if cmd in ('-h','--help','help'):
            print "Usage: %s COMMAND [options] [args...]" % self._name
            if self._description is not None:
                print "\n%s" % self._description
            print "%s" % self.print_available_commands()
            sys.exit(0)
        if cmd in ('--version'):
            if self._version is not None:
                version_str = self._version
                print "%s" % version_str.replace('%prog',self._name)
            sys.exit(0)

    def list_commands(self):
        """Return the list of commands

        """
        return self._commands.keys()

    def print_available_commands(self):
        """Pretty-print available commands

        Returns a 'pretty-printed' string for all options and commands,
        with standard whitespace formatting.

        """
        lines = ["\nOptions:"]
        # Add generic commands
        if self._version is not None:
            lines.append(self.print_command("--version",
                                            "show program's version number and exit"))
            lines.append(self.print_command("-h, --help, help",
                                            "show this help message and exit"))
        # Add custom commands
        lines.append("\nAvailable commands:")
        for cmd in self.list_commands():
            lines.append(self.print_command(cmd,self._help[cmd]))
        lines.append("")
        return '\n'.join(lines)

    def print_command(self,cmd,message=None):
        """Print a line for a single command

        Returns a 'pretty-printed' line for the specified command
        and text, with standard whitespace formatting.

        """
        text = ['  ',cmd]
        width = 22
        if len(cmd) < width:
            text.append(' '*(width-len(cmd)))
        else:
            text.append('\n  '+' '*width)
        if message is not None:
            text.append(message)
        return ''.join(text)
class CommandParser(object):
    """Class defining multiple command line parsers

    This parser can process command lines of the form

    PROG CMD OPTIONS ARGS

    where different sets of options can be defined based on the major
    command ('CMD') supplied at the start of the line.

    Usage:

    Create a simple CommandParser which uses optparse.OptionParser as
    the default subparser backend using:

    >>> p = CommandParser()
    
    Alternatively, specify argparse.ArgumentParser as the subparser
    using:

    >>> p = CommandParser(subparser=argparser.ArgumentParser)

    Add a 'setup' command:

    >>> p.add_command('setup',usage='%prog setup OPTIONS ARGS')

    Add options to the 'setup' command using the appropriate methods
    of the subparser (e.g. 'add_argument' for an
    ArgumentParser instance).

    For example:

    >>> p.parser_for('info').add_argument('-f',...)

    To process a command line, use the 'parse_args' method, for
    example for an OptionParser-based subparser:

    >>> cmd,options,args = p.parse_args()

    Note that the exact form of the returned values depends on
    on the subparser instance; it will be the same as that
    returned by the 'parse_args' method of the subparser.

    """
    def __init__(self,description=None,version=None,subparser=None):
        """Create a command line parser
        with 'subparser' as the backend (default=OptionParser)
        This parser can process command lines of the form

        PROG CMD OPTIONS ARGS

        where different sets of options can be defined based
        on the major command supplied at the start.

        """
        self._name = os.path.basename(sys.argv[0])
        self._description = description
        self._version = version
        self._commands = OrderedDictionary()
        self._help = dict()
        if not subparser:
            subparser = optparse.OptionParser
        self._subparser = subparser

    def add_command(self,cmd,help=None,**args):
        """Add a major command to the CommandParser

        Adds a command, and creates and returns an initial
        subparser instance for it.

        Arguments:
          cmd: the command to be added
          help: (optional) help text for the command

        Other arguments are passed to the subparser instance
        when it is created i.e. 'usage', 'version',
        'description'.

        If 'version' isn't specified then the version
        supplied to the CommandParser object will be used.

        Returns:
          Subparser instance object for the command.

        """
        if cmd in self._commands:
            raise Exception("Command '%s' already defined" % cmd)
        if 'version' not in args:
            args['version'] = self._version
        p = self._subparser(**args)
        self._commands[cmd] = p
        self._help[cmd] = help
        return p

    def parser_for(self,cmd):
        """Return OptionParser for specified command

        Returns:
          The OptionParser object for the specified command.

        """
        return self._commands[cmd]

    def parse_args(self,argv=None):
        """Process a command line

        Parses a command line (either those supplied to the calling
        subprogram e.g. via the Python interpreter, or as a list).

        Once the command is identified from the first argument, the
        remainder of the arguments are passed to the 'parse_args'
        method of the appropriate subparser for that command.

        This method returns a tuple, with the first value being the
        command, and the rest of the values being those returned
        from the 'parse_args' method of the subparser.

        Arguments:
          argv: (optional) a list consisting of a command line.
            If not supplied then defaults to sys.argv[1:].

        Returns:
          A tuple of (cmd,...), where 'cmd' is the command, and '...'
          represents the values returned from the 'parse_args' method
          of the subparser. For example, using the default OptionParser
          backend returns (cmd,options,arguments), where 'options' and
          'arguments' are the options and arguments as returned by
          OptionParser.parse_args; using ArgumentParser as a backend
          returns (cmd,arguments).

        """
        # Collect arguments to process
        if argv is None:
            argv = sys.argv[1:]
        if not argv:
            self.error("Need to supply a command\n%s" %
                       self.print_available_commands())
        # Determine the major command and get the parser
        cmd = argv[0]
        self.handle_generic_commands(cmd)
        try:
            p = self.parser_for(cmd)
        except KeyError:
            # No parser
            self.error("Usage: %s COMMAND [options] [args...]\n\n"
                       "%s: error: no such command: %s" %
                       (self._name,self._name,cmd))
        # Parse the remaining arguments and return
        if isinstance(p,argparse.ArgumentParser):
            options = p.parse_args(argv[1:])
            return (cmd,options)
        # else:
        options,arguments = p.parse_args(argv[1:])
        return (cmd,options,arguments)

    def error(self,message):
        """Exit with error message

        """
        sys.stderr.write("%s\n" % message)
        sys.exit(1)

    def handle_generic_commands(self,cmd):
        """Process 'generic' commands e.g. 'help'

        """
        if cmd in ('-h','--help','help'):
            print "Usage: %s COMMAND [options] [args...]" % self._name
            if self._description is not None:
                print "\n%s" % self._description
            print "%s" % self.print_available_commands()
            sys.exit(0)
        if cmd in ('--version',):
            if self._version is not None:
                version_str = self._version
                print "%s" % version_str.replace('%prog',self._name)
            sys.exit(0)

    def list_commands(self):
        """Return the list of commands

        """
        return self._commands.keys()

    def print_available_commands(self):
        """Pretty-print available commands

        Returns a 'pretty-printed' string for all options and commands,
        with standard whitespace formatting.

        """
        lines = ["\nOptions:"]
        # Add generic commands
        if self._version is not None:
            lines.append(self.print_command("--version",
                                            "show program's version number and exit"))
            lines.append(self.print_command("-h, --help, help",
                                            "show this help message and exit"))
        # Add custom commands
        lines.append("\nAvailable commands:")
        for cmd in self.list_commands():
            lines.append(self.print_command(cmd,self._help[cmd]))
        lines.append("")
        return '\n'.join(lines)

    def print_command(self,cmd,message=None):
        """Print a line for a single command

        Returns a 'pretty-printed' line for the specified command
        and text, with standard whitespace formatting.

        """
        text = ['  ',cmd]
        width = 22
        if len(cmd) < width:
            text.append(' '*(width-len(cmd)))
        else:
            text.append('\n  '+' '*width)
        if message is not None:
            text.append(message)
        return ''.join(text)