Reputation: 169
I have a small CLI app (myscript.py
) that is defined like so.
import sys
import argparse
class MyParser(argparse.ArgumentParser):
'''
Overriden to show help on default.
'''
def error(self, message):
print(f'error: {message}')
self.print_help()
sys.exit(2)
def myfunc(args):
'''
Some function.
'''
print(args.input_int**2)
def main():
# Define Main Parser
main_par = MyParser(
prog='myapp',
description='main help')
# Define Command Parser
cmd_par = main_par.add_subparsers(
dest='command',
required=True)
# Add Subcommand Parser
subcmd_par = cmd_par.add_parser(
'subcmd',
description='subcmd help')
# Add Subcommand Argument
subcmd_par.add_argument(
'-i', '--input-int',
type=int,
help='some integer',
required=True)
# Add FromName Dispatcher
subcmd_par.set_defaults(
func=myfunc)
# Parse Arguments
args = main_par.parse_args()
# Call Method
args.func(args)
if __name__ == '__main__':
main()
The MyParser
class simply overrides the error()
method in argparse.ArgumentParser
class to print help on error.
When I execute
$ python myscript.py
I see the default / main help. Expected.
When I execute
$ python myscript.py subcmd
I see the subcmd
help. Expected.
When I execute
$ python myscript.py subcmd -i ClearlyWrongValue
I also see the subcmd
help. Expected.
However, very annoyingly if I do the following
$ python myscript.py subcmd -i 2 --non-existent-argument WhateverValue
I see the default / main help and not subcmd
help.
What can I do, to ensure that this last case shows me the subcmd
help and not the main help? I thought the subparser structure would automatically procure the help from subcmd
as found in the third case, but it is not so? Why?
Upvotes: 0
Views: 158
Reputation: 231355
The unrecognized args
error is raised by parse_args
def parse_args(self, args=None, namespace=None):
args, argv = self.parse_known_args(args, namespace)
if argv:
msg = _('unrecognized arguments: %s')
self.error(msg % ' '.join(argv))
return args
The subparser is called via the cmd_par.__call__
with:
subnamespace, arg_strings = parser.parse_known_args(arg_strings, None)
for key, value in vars(subnamespace).items():
setattr(namespace, key, value)
if arg_strings:
vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
That is it is called with parse_known_args
, and it's extras
are returned to the main as UNRECOGNIZED
. So it's the main
than handles these, not the subparser.
In the $ python myscript.py subcmd -i ClearlyWrongValue
case, the subparser raises a ArgumentError
which is caught and converted into a self.error
call.
Similarly, the newish exit_on_error
parameter handles this kind of ArgumentError
, but does not handle the urecognized
error. There was some discussion of this in the bug/issues.
If you used parse_known_args
, the extras
would be ['--non-existent-argument', 'WhateverValue']
, without distinguishing which parser initially classified them as such.
Upvotes: 1