I'm trying to figure out how to add global option in a sub-parser scenario with pythons arparse library.
Right now my code looks like this:
def parseArgs(self):
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--debug', default=False, required=False,
action='store_true', dest="debug", help='debug flag')
main_parser = argparse.ArgumentParser()
main_parser.add_argument('--debug', default=False, required=False,
action='store_true', dest="debug", help='debug flag')
service_subparsers = main_parser.add_subparsers(title="category",
dest="category")
agent_parser = service_subparsers.add_parser("agent",
help="agent commands", parents=[parent_parser])
return main_parser.parse_args()
This works for the command line ./test --help
and the --debug
option is listed as global:
usage: test [-h] [--debug] {agent} ... optional arguments: -h, --help show this help message and exit --debug debug flag category: {agent} agent agent commands
However when I trigger the agent sub-parser with the command line ./test agent --help
the --debug
option is now no longer listed as a global option but as an option for the sub-parser. Also it must now specified as ./test agent --debug
and ./test --debug agent
no longer works:
usage: test agent [-h] [--debug] optional arguments: -h, --help show this help message and exit --debug debug flag
What I'd like to be able to do is define --debug
is global so that it can always be specified for all sub-parsers and appropriately listed as such in the help output.
main_parser
fills in the defaults into namespace
(False
for debug
); if it encounters --debug
it changes debug
to True
. When it sees the agent
string, it calls the subparser, passing it the remaining argument strings, and the namespace that it has been using.
Now the subparser does the normal parser things - if fills in the defaults for its arguments, setting default
to False
. If it encounters --debug
in the remaining strings, it changes that to True
. Otherwise it leaves it as is. Once it is done, it passes the namespace back to the main parser, which then returns it to your code.
So for
myprog.py --debug agent --debug
namespace(debug=False)
has flipped from False to True to False and back to True.
This a consequence of sharing the same dest
for both the main parser (I don't like the use of 'global' in this context), and the subparser.
There was a bug/issue that tried to change the behavior a bit, passing the subparser a 'virgin' namespace, and then somehow merging its result with the main one. But that produced some backward compatibility issues. I could look it up if needed.
For now, trying to define the same optional in both the main and subparser is bound to create confusion for you and your user.
If I change the parent to
parent_parser.add_argument('--Debug', action='store_true', help='debug flag')
(no need for default, or the dest if it is sames as the option flag)
the resulting namespace will look like
1721:~/mypy$ python stack37933480.py --debug agent --Debug
Namespace(Debug=True, category='agent', debug=True)
Or I could define
parent_parser.add_argument('--debug', dest='debug1', action='store_true', help='debug flag')
and get:
1724:~/mypy$ python stack37933480.py --debug agent --debug
Namespace(category='agent', debug=True, debug1=True)
Same flag in both places, but different entries in the namespace. After parsing I could do something like:
args.debug = args.debug or args.debug1
to unify the two flags. Your user will see '--debug' regardless of which help asks for.
Sorry if the description is a bit long winded, but I think it's important to understand the behavior first. Then solutions become more apparent.
In this case the use of a parent doesn't complicate the issue. I assume you are using it just to add this debug to all subparsers.
Another option is to just define debug
for the main parser. Yes, it will be missing from the subparsers help, but you can always add a note in the description.
===================
The subparser definition takes a prog
parameter. If not given it is defined base on the main prog
.
If I add prog
as:
agent_parser = service_subparsers.add_parser("agent",
prog='myprog.py [--debug] agent',
help="agent commands", parents=[parent_parser])
subparser usage becomes:
1824:~/mypy$ python3 stack37933480.py agent -h
usage: myprog.py [--debug] agent [-h] [--debug]
or I can add that prog
to the add_subparsers
definition
service_subparsers = main_parser.add_subparsers(title="category",
prog='myprog.py [--debug]',
dest="category")
Check the code for that method to see how it constructs the default usage prefix. It includes main
positionals, but not optionals.
http://bugs.python.org/issue9351 - in this patch the original developer thought that users would expect the subparser definition of an argument should override the main parser's values and actions. You were, on the other hand, expecting the main definition to have priority.
http://bugs.python.org/issue24251 - but the correction proposed in 9351 caused problems for other users. That's why I think it is better not to define the same dest
in the main and sub. It is hard to satisfy everyone's expectations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With