I'd like my program to allow any string to be passed as an argument, but also to allow subcommands. For example, these would all be valid:
$ python argtest.py 'hello world'
$ python argtest.py version
$ python argtest.py help
I do not want users to have to use --, e.g., python argparse.py -- 'hello world'.
If I set up a subcommand parser to handle 'version' and 'help', it will then complain that 'hello world' isn't a valid subcommand.
My code:
parser = argparse_flags.ArgumentParser()
subparsers = parser.add_subparsers(required=False)
subparsers.add_parser('version')
subparsers.add_parser('help')
ns, remainder = parser.parse_known_args(argv[1:])
Yields:
$ python argtest.py 'hello world'
usage: argtest.py [-h] {version,help} ...
argtest.py: error: invalid choice: 'hello world' (choose from 'version', 'help')
The only fix I've come up with is to create a "fallback" subcommand and then manually add it with parser.parse_known_args(['fallback'] + argv[1:]) by either:
- Looking for this first argument not prefixed with '-' and then see if it's in
subparsers.choices. - Catch the SystemExit raised by argparse when no subparsers match (and temporarily redirecting stderr so it doesn't spam the CLI).
All of this is kind of gross, is there a better way to get the behavior I want? I just want argparse to be a little more chill about not finding a subcommand.
add_subparsersis a variant onadd_argument, creating aActionsubclass withnargs=argparse.PARSER.Only
optionalsget theirflagstring selected by value. Positionals are allocated strictly by postion andnargs. This is done in_get_values. For a parserSo all remaining strings in
arg_stringsare allocated._get_valuetests againsttype, which here is just a pass throughstr._check_valuetests the first of that list againstchoices.For subparsers, the
choicesare the subparser names and their aliases. So this is where your 'hello_world' fails.As you note, exit errors can be caught. Recently a
exit_on_errorparameter has been added that will raise the error instead of exiting. It doesn't work in all cases, but I think it should work here. And as documented you can also tweak theerrorandexitmethods to change how errors are raised.Normally a
positionalis not-required only ifnargsis '?' or '*'. And a due to tweak of old patching, also this 'PARSER' case. There's no mechanism to retry allocating a string if it fails thetypeorchoices.If there are multiple
positionals, strings are allocated in aregreedy sense. First argument gets as many values as it wants, etc. So be careful when stringing '+*?'nargstogether.In sum, I think the subparsers mechanism works best if you define a bunch of
optionalsfor the main parsers, and then theadd_subparsersAction. That way it can handle theoptionals, if any, and then branch to the chosen subparser to handle the rest. Apositionalcan be defined for the main parser, but avoid open endednargs.Another thing with subparsers. Use different argument
destin the main and subparsers. That way the subparser defaults won't step on main parser defaults.