Subcommand alternative to argparse and optparse

Is there an intuitive alternative for argparse / optparse for subcommands? Both of them are bad - this is either a crazy config or a crazy conclusion.

Real world example ( stolen , not needed):

>>> parser = argparse.ArgumentParser() >>> subparsers = parser.add_subparsers(title='subcommands', ... description='valid subcommands', ... help='additional help') >>> subparsers.add_parser('foo') >>> subparsers.add_parser('bar') >>> parser.parse_args(['-h']) usage: [-h] {foo,bar} ... optional arguments: -h, --help show this help message and exit subcommands: valid subcommands {foo,bar} additional help 

Required:

 >>> parser = cmdline.Parser( ... tplheader='Usage: tool [command] [options]', ... tplcommandhead='Available commands:', ... tplfooter='Use \"tool help\" to get full list of supported commands.') >>> parser.add('foo', help='foo.') >>> parser.add('bar', help='bar.') >>> parser.parse(['-h']) Usage: tool [command] [options] Available commands: foo foo. bar bar. Use "tool help" to get full list of supported commands. 

UPDATE I would accept an answer that contains an example of checking and parsing a command that gives a help message exactly like the last snippet.

+5
source share
4 answers

You can get closer to your requested release by simply modifying your argparse code a bit:

  • Set the usage text by specifying the usage parameter to ArgumentParser .
  • Omit the description and help arguments in add_subparsers .
  • Change the title parameter to Available subcommands .
  • Use the metavar parameter to override unsightly text {foo,bar} .
  • Use the help arguments available in add_parser .

Here is the finished product:

 import argparse parser = argparse.ArgumentParser(usage='tool [command] [options]') subparsers = parser.add_subparsers(title='Available commands', metavar='') subparsers.add_parser('foo', help='foo.') subparsers.add_parser('bar', help='bar.') parser.parse_args(['-h']) 

This code prints this:

  usage: tool [command] [options]

 optional arguments:
   -h, --help show this help message and exit

 Available commands:

     foo foo.
     bar bar.
+5
source

Looks like you are looking for argh .

Here is a snippet from the presentation on the main page.

A potentially modular application with several commands:

 import argh # declaring: def echo(text): "Returns given word as is." return text def greet(name, greeting='Hello'): "Greets the user with given name. The greeting is customizable." return greeting + ', ' + name # assembling: parser = argh.ArghParser() parser.add_commands([echo, greet]) # dispatching: if __name__ == '__main__': parser.dispatch() 

Of course it works:

 $ ./app.py greet Andy Hello, Andy $ ./app.py greet Andy -g Arrrgh Arrrgh, Andy 

Help message on the site is slightly reduced. This is what it actually outputs for me ( argh 0.26.1).

 $ ./app.py --help usage: app.py [-h] {greet,echo} ... positional arguments: {greet,echo} echo Returns given word as is. greet Greets the user with given name. The greeting is customizable. optional arguments: -h, --help show this help message and exit 
0
source

I'm not sure I understand what happened to what you described. I use something a little different though:

 parser = argparse.ArgumentParser(description='My description') parser.add_argument('-i', '--input', type=str, required=True, help='Inputfile') parser.add_argument('-o', '--output', type=str, required=False, help='Output file') args = parser.parse_args() input_filename = args.input if not args.output: output_filename = input_filename else: output_filename = args.output 
0
source

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)) # print(special_help(parser)) 

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.

0
source

Source: https://habr.com/ru/post/1214804/


All Articles