Hiding selected subcommands using argparse
Question:
I am using argparse and have set-up subcommands to my program. I have created sub-parsers to define these sub-commands. I have some admin commands that shouldn’t be shown to users in the help screen. I know we could hide arguments of a sub-command, but I don’t know how we could hide few of the subcommands from showing up in the help list.
Here’s my code snippet,
parser = argparse.ArgumentParser(prog='myProg',
description=desc,
formatter_class=argparse.RawDescriptionHelpFormatter)
subparsers = parser.add_subparsers(dest='sub_parser_name')
myProg_query.add_subparser(subparsers)
myProg_update.add_subparser(subparsers)
myProg_configure.add_subparser(subparsers)
myProg_result.add_subparser(subparsers)
When I run the help command, I get this
%> myProg --help
usage: myProg [-h]
positional arguments:
{query,update,configure,result}
query query information
update Update
configure Configure system
result tabulate the result
From the help output, I would want to display only “query” and “result” to the user. I tried to use argparse.SUPPRESS in add_subparser method, but it would hide all the subcommands. Whatever I searched only talked about hiding individual arguments of each sub-command, but not about hiding sub-command. I might have to create a custom formatter method, but wanted to check if there were any other way to achieve this.
Answers:
metavar
might do the trick:
import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(metavar='{cmd1,cmd2}')
sp1 = sp.add_parser('cmd1')
sp2 = sp.add_parser('cmd2')
sp3 = sp.add_parser('cmd3')
parser.parse_args()
With this cmd3
does not appear in the usage or help. But it does appear in the error message
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’ (choose from ‘cmd1’, ‘cmd2’, ‘cmd3’)
You may have already discovered this use of help=SUPPRESS
. But it requires a custom usage (and possibly description) parameters:
import argparse
parser = argparse.ArgumentParser(usage='%(prog)s [-h] {cmd1,cmd2}')
sp = parser.add_subparsers(help=argparse.SUPPRESS)
sp1 = sp.add_parser('cmd1')
sp2 = sp.add_parser('cmd2')
sp3 = sp.add_parser('cmd3')
parser.parse_args()
To the main parser, subparsers look like choices
of a positional argument. As best I can tell there isn’t a way of selectively suppressing choices
.
With this level of question, examining the argparse.py
code itself can be more help than the docs. In this case I looked at the code for class _SubParsersAction(Action)
. That’s doubly true if you want to customize the formatter. The existing alternative formatters modify just one or two methods buried deep in the class.
This issue has been raised as a bug issue, http://bugs.python.org/issue22848.
There is a patch that would modify the choices display based on help=SUPPRESS
for individual subparsers. But I’m recommending the metavar
solution, at least for now. There are other proposed patches for dealing with choices
.
looks like I found a solution for this issue without any patches for argparse.
It’s enough to modify ‘metavar’ and do not set ‘help’ for the certain subparser.
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='commands', metavar='{command}')
command = subparsers.add_parser("command",
help='command help.',
description='command description.')
suppress_command = subparsers.add_parser("suppress_command",
help=argparse.SUPPRESS,
description='suppress command.')
hidden_command = subparsers.add_parser("hidden_command",
description='Hidden command.')
This results in
[root@localhost ~]# parser -h
usage: parser [-h] {command} ...
optional arguments:
-h, --help print help message and exit
commands:
{command}
command command help.
suppress_command ==SUPPRESS==
[root@localhost ~]# parser hidden_command -h
usage: parser hidden_command [-h]
Hidden command.
optional arguments:
-h, --help show this help message and exit
In addition to the previous answer. For the hidden command appearing in error message, I have a dirty solution, tested in Python 3.7 (might require adaptation for higher versions).
Once metavar
is set the (choose from ...)
becomes redundant. To suppress it, this does the trick:
import argparse
class ArgumentErrorPatch(argparse.ArgumentError):
def __str__(self):
return re.sub(r" (choose from .*)", "", super().__str__())
argparse.ArgumentError = ArgumentErrorPatch
and
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’ (choose from ‘cmd1’, ‘cmd2’, ‘cmd3’)
becomes
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’
that is, according to me, clear enough and more readable (especially if you have many commands).
Of course, this requires to set metavar
for each subcommand (e.g., for subcmd
in myapp cmd subcmd args
). Still in Python 3.7, this does the trick to fill metavar
for a complete subparser (i.e., whose other subparsers have been added) without having to detail commands manually:
def _metavar(parser, hidden_cmds=set()):
parser.metavar = (
'{' + ','.join(
cmd for cmd in parser._name_parser_map if not cmd in hidden_cmds
) + '}'
)
Full example
If you have cmd1
, cmd2
, cmd3
and you want to hide cmd3
:
import argparse
import re
class ArgumentErrorPatch(argparse.ArgumentError):
def __str__(self):
return re.sub(r" (choose from .*)", "", super().__str__())
argparse.ArgumentError = ArgumentErrorPatch
def _metavar(parser, hidden_cmds=set()):
parser.metavar = (
'{' + ','.join(
cmd for cmd in parser._name_parser_map if not cmd in hidden_cmds
) + '}'
)
parser = argparse.ArgumentParser()
sp = parser.add_subparsers()
sp1 = sp.add_parser("cmd1")
sp2 = sp.add_parser("cmd2")
sp3 = sp.add_parser("cmd3")
_metavar(sp, hidden_cmds={"cmd3"})
parser.parse_args()
print("ok")
Giving cmd3
results in:
ok
while giving cmd4
results in:
usage: script.py [-h] {cmd1,cmd2} …
script.py: error: argument {cmd1,cmd2}: invalid choice: ‘cmd4’
I am using argparse and have set-up subcommands to my program. I have created sub-parsers to define these sub-commands. I have some admin commands that shouldn’t be shown to users in the help screen. I know we could hide arguments of a sub-command, but I don’t know how we could hide few of the subcommands from showing up in the help list.
Here’s my code snippet,
parser = argparse.ArgumentParser(prog='myProg',
description=desc,
formatter_class=argparse.RawDescriptionHelpFormatter)
subparsers = parser.add_subparsers(dest='sub_parser_name')
myProg_query.add_subparser(subparsers)
myProg_update.add_subparser(subparsers)
myProg_configure.add_subparser(subparsers)
myProg_result.add_subparser(subparsers)
When I run the help command, I get this
%> myProg --help
usage: myProg [-h]
positional arguments:
{query,update,configure,result}
query query information
update Update
configure Configure system
result tabulate the result
From the help output, I would want to display only “query” and “result” to the user. I tried to use argparse.SUPPRESS in add_subparser method, but it would hide all the subcommands. Whatever I searched only talked about hiding individual arguments of each sub-command, but not about hiding sub-command. I might have to create a custom formatter method, but wanted to check if there were any other way to achieve this.
metavar
might do the trick:
import argparse
parser = argparse.ArgumentParser()
sp = parser.add_subparsers(metavar='{cmd1,cmd2}')
sp1 = sp.add_parser('cmd1')
sp2 = sp.add_parser('cmd2')
sp3 = sp.add_parser('cmd3')
parser.parse_args()
With this cmd3
does not appear in the usage or help. But it does appear in the error message
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’ (choose from ‘cmd1’, ‘cmd2’, ‘cmd3’)
You may have already discovered this use of help=SUPPRESS
. But it requires a custom usage (and possibly description) parameters:
import argparse
parser = argparse.ArgumentParser(usage='%(prog)s [-h] {cmd1,cmd2}')
sp = parser.add_subparsers(help=argparse.SUPPRESS)
sp1 = sp.add_parser('cmd1')
sp2 = sp.add_parser('cmd2')
sp3 = sp.add_parser('cmd3')
parser.parse_args()
To the main parser, subparsers look like choices
of a positional argument. As best I can tell there isn’t a way of selectively suppressing choices
.
With this level of question, examining the argparse.py
code itself can be more help than the docs. In this case I looked at the code for class _SubParsersAction(Action)
. That’s doubly true if you want to customize the formatter. The existing alternative formatters modify just one or two methods buried deep in the class.
This issue has been raised as a bug issue, http://bugs.python.org/issue22848.
There is a patch that would modify the choices display based on help=SUPPRESS
for individual subparsers. But I’m recommending the metavar
solution, at least for now. There are other proposed patches for dealing with choices
.
looks like I found a solution for this issue without any patches for argparse.
It’s enough to modify ‘metavar’ and do not set ‘help’ for the certain subparser.
import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title='commands', metavar='{command}')
command = subparsers.add_parser("command",
help='command help.',
description='command description.')
suppress_command = subparsers.add_parser("suppress_command",
help=argparse.SUPPRESS,
description='suppress command.')
hidden_command = subparsers.add_parser("hidden_command",
description='Hidden command.')
This results in
[root@localhost ~]# parser -h
usage: parser [-h] {command} ...
optional arguments:
-h, --help print help message and exit
commands:
{command}
command command help.
suppress_command ==SUPPRESS==
[root@localhost ~]# parser hidden_command -h
usage: parser hidden_command [-h]
Hidden command.
optional arguments:
-h, --help show this help message and exit
In addition to the previous answer. For the hidden command appearing in error message, I have a dirty solution, tested in Python 3.7 (might require adaptation for higher versions).
Once metavar
is set the (choose from ...)
becomes redundant. To suppress it, this does the trick:
import argparse
class ArgumentErrorPatch(argparse.ArgumentError):
def __str__(self):
return re.sub(r" (choose from .*)", "", super().__str__())
argparse.ArgumentError = ArgumentErrorPatch
and
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’ (choose from ‘cmd1’, ‘cmd2’, ‘cmd3’)
becomes
error: argument {cmd1,cmd2}: invalid choice: ‘cmd’
that is, according to me, clear enough and more readable (especially if you have many commands).
Of course, this requires to set metavar
for each subcommand (e.g., for subcmd
in myapp cmd subcmd args
). Still in Python 3.7, this does the trick to fill metavar
for a complete subparser (i.e., whose other subparsers have been added) without having to detail commands manually:
def _metavar(parser, hidden_cmds=set()):
parser.metavar = (
'{' + ','.join(
cmd for cmd in parser._name_parser_map if not cmd in hidden_cmds
) + '}'
)
Full example
If you have cmd1
, cmd2
, cmd3
and you want to hide cmd3
:
import argparse
import re
class ArgumentErrorPatch(argparse.ArgumentError):
def __str__(self):
return re.sub(r" (choose from .*)", "", super().__str__())
argparse.ArgumentError = ArgumentErrorPatch
def _metavar(parser, hidden_cmds=set()):
parser.metavar = (
'{' + ','.join(
cmd for cmd in parser._name_parser_map if not cmd in hidden_cmds
) + '}'
)
parser = argparse.ArgumentParser()
sp = parser.add_subparsers()
sp1 = sp.add_parser("cmd1")
sp2 = sp.add_parser("cmd2")
sp3 = sp.add_parser("cmd3")
_metavar(sp, hidden_cmds={"cmd3"})
parser.parse_args()
print("ok")
Giving cmd3
results in:
ok
while giving cmd4
results in:
usage: script.py [-h] {cmd1,cmd2} …
script.py: error: argument {cmd1,cmd2}: invalid choice: ‘cmd4’