I have a CLI I'm building which utilizes subparsers for sub-commands similar to tools like git. Some of my sub-commands share common options so I have a group parser that defines the options, and each sub-command that needs them uses parents=group_parser
as one of the arguments. For example:
group_parser = argparse.ArgumentParser()
group_parser.add_argument('-f', '--foo', action='store_true')
command1_parser = subparsers.add_parser('command1', parents=[group_parser])
command2_parser = subparsers.add_parser('command2', parents=[group_parser])
So as you see both command1 and command2 inherit the option --foo
. What I'm trying to do is update the helptext for foo separately on command1 and command2. For instance if I run myprog command1 -h
, I want it to say a different help message for --foo
that when I run myprog command2 -h
. The problem is until I do parse_args()
there isn't a namespace to update for that argument, so something like this doesn't work:
group_parser = argparse.ArgumentParser()
group_parser.add_argument('-f', '--foo', action='store_true')
command1_parser = subparsers.add_parser('command1', parents=[group_parser])
command1.foo['help'] = "foo help for command1"
command2_parser = subparsers.add_parser('command2', parents=[group_parser])
command2.foo['help'] = "foo help for command2"
Is this possible to somehow add additional parameters to an argument outside of the add_argument()
function? The only other solution is to not use the inherited parent and just define foo separately for each sub-command, but if there's a way to update parameters, that would be ideal.
There is a way of changing the help
(and other Action attributes) after the Action (argument) has been created. But with the parents
mechanism there's another problem - Actions are copied by reference. So even if you can change the help
for command1
, you end up changing it for command2
as well. I've encountered this when trying to change attributes like default
.
I'll add an illustration, and may be a link to previous discussions.
In [2]: parent = argparse.ArgumentParser(add_help=False)
In [4]: fooObj = parent.add_argument('--foo',default='foo1', help='foo help')
fooObj
is a reference to the Action created by this add_argument
.
In [5]: fooObj.default
Out[5]: 'foo1'
In [6]: fooObj.help # the help parameter
Out[6]: 'foo help'
In [7]: parent.print_help()
usage: ipython3 [--foo FOO]
optional arguments:
--foo FOO foo help
changing the help attribute:
In [8]: fooObj.help = 'new help'
In [9]: parent.print_help()
usage: ipython3 [--foo FOO]
optional arguments:
--foo FOO new help
Now make a parser and subparsers
In [10]: parser = argparse.ArgumentParser()
In [11]: sp = parser.add_subparsers()
In [13]: cmd1 = sp.add_parser('cmd1',parents=[parent])
In [14]: cmd2 = sp.add_parser('cmd2',parents=[parent])
In [15]: cmd2.print_help()
usage: ipython3 cmd2 [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO new help
_actions
is a list of the arguments defined for a parser:
In [16]: cmd1._actions
Out[16]:
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
_StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default='foo1', type=None, choices=None, help='new help', metavar=None)]
Comparing ids we can see that the 2d action is the same as fooObj
. Same with cmd2
.
In [17]: id(cmd1._actions[1])
Out[17]: 2885458060
In [18]: id(fooObj)
Out[18]: 2885458060
Changing the help
for cmd1
changes it for cmd2
as well
In [19]: cmd1._actions[1].help = 'cmd1 foo'
In [20]: cmd2.print_help()
usage: ipython3 cmd2 [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO cmd1 foo
In [21]: fooObj.help
Out[21]: 'cmd1 foo'
Here's a case of trying to provide different defaults for the subparsers:
argparse - Combining parent parser, subparsers and default values
It might be best to use your own utility function(s) to add the common arguments to the subparsers. That way each subparser can have its own copy of the Action objects, rather than sharing them. I think the parents
mechanism is nicer in theory than in practice.