Does it get a win? :)
Custom options
Rob Kennedy has a better setup.
In [158]: parser=argparse.ArgumentParser(usage='tool [command] [options]', description= "Available commands:\n\n foo foo.\n bar bar.\n", epilog= 'Use "tool help" to get full list of supported commands', formatter_class=argparse.RawDescriptionHelpFormatter, add_help=False) In [159]: parser.print_help() usage: tool [command] [options] Available commands: foo foo. bar bar. Use "tool help" to get full list of supported commands
What I did is configure help with the options available.
Alternative API and / or parser?
But your other lines, the parse.add() tags, suggest that you don't like the argparse method for defining "commands". You could add some methods to your parser that use this more compact syntax, but still eventually call the existing subparser mechanism.
But perhaps you want to replace the entire parsing scheme with your own. One, for example, expects the first argument to be "command." What about other "positioners"? Who or what processes the "parameters"?
You understand that the argparse subparameter argparse built on top of the simpler parser scheme for optionals and positionals . The parser.add_subparsers command is a specialized form of add_argument . The subparsers object is a positional argument with a special Action class. {foo,bar} is actually a list of choices that you defined for this argument (subcommand names or aliases). The subcommands themselves are parsers.
Front End Custom Parser
If the sys.argv[1] element will always be the command name, you can configure something like this:
if sys.argv[1:]: cmd = sys.argv[1] rest = sys.argv[2:] parser = parser_dict.get(cmd, None) if parser: args = parser.parse_args(rest) else: print_default_help()
Where parser_dict is a dictionary matching cmd strings with specific parsers. This is actually just an interface that captures the first line of the argument and sends processing to the rest of the other defining parsers. They can be a combination of argparse , optparse and custom parsers. This external interface should not be fantastic if all it processes is the first line of the command.
print_default_help will be slightly larger than the pretty parser_dict print.
In further thoughts, I realized that the sp.choices attribute of the sp.choices object is just such a dictionary — with commands as keys and parsers as values.
Format_help custom methods
Here are a few custom help forms.
A simple one that only gets prog and _choices_actions from parser . subparsers._choices_actions is a list of objects containing help information and aliases for individual subparallels.
def simple_help(parser, subparsers): # format a help message with just the subparser choices usage = "Usage: %s command [options]"%parser.prog desc = "Available commands:\n" epilog = '\nUse "%s help" to get full list of supported commands.'%parser.prog choices = fmt_choices(subparsers._choices_actions) astr = [usage] astr.append(desc) astr.extend(choices) astr.append(epilog) return '\n'.join(astr) def fmt_choices(choices): # format names and help in 2 columns x = max(len(k.metavar) for k in choices) fmt = ' {:<%s} {}'%x astr = [] for k in choices: # k.metavar lists aliases as well astr.append(fmt.format(k.dest, k.help)) return astr
This model is modeled on parser.format_help and uses the use of Formatter and all wrapper and distance information. I wrote it to use options other than the default options where possible. However, it is difficult to suppress blank lines.
def special_help(parser, subparsers=None, usage=None, epilog=None): # format help message using a Formatter # modeled on parser.format_help # uses nondefault parameters where possible if usage is None: usage = "%(prog)s command [options]" if epilog is None: epilog = "Use '%(prog)s help' for command list" if subparsers is None: # find the subparsers action in the parser for action in parser._subparsers._group_actions: if hasattr(action, '_get_subactions'): subparsers = action break # if none found, subparsers is still None? if parser._subparsers != parser._positionals: title = parser._subparsers.title desc = parser._subparsers.description else: title = "Available commands" desc = None if subparsers.metavar is None: subparsers.metavar = '_________' # restore to None at end? formatter = parser._get_formatter() if parser.usage is None: formatter.add_usage(usage, [], []) else: formatter.add_usage(parser.usage, parser._actions, parser._mutually_exclusive_groups) # can I get rid of blank line here? formatter.start_section(title) formatter.add_text(desc) formatter.add_arguments([subparsers]) formatter.end_section() formatter.add_text(epilog) return formatter.format_help()
They can be triggered in many ways. Any of them can replace the parser's format_help and thus be created using the -h option, as well as with parser.print_help() .
Or you can include the help subcommand. This will match the epilog . -h will still give complete, ugly help.
sp3 = sp.add_parser('help') # help='optional help message'
and args test:
if args.cmd in ['help']: print(simple_help(parser, sp))
Another option is to check sys.argv before parser.parser_args and call the help function if this list is not long enough or includes the help line. This is roughly what Ipython does to circumvent the usual argparse help.