The code
from argparse import ArgumentParser
p = ArgumentParser(description = 'foo')
p.add_argument('-b', '--bar', help = 'a description')
p.parse_args()
...results in the output:
$ python argparsetest.py -h
usage: argparsetest.py [-h] [-b BAR]
foo
optional arguments:
-h, --help show this help message and exit
-b BAR, --bar BAR a description
What I'd like is:
$ python argparsetest.py -h
foo
optional arguments:
-h, --help show this help message and exit
-b BAR, --bar BAR a description
e.g., no usage message when asking for help. Is there some way to do this?
definitely possible -- but I'm not sure about documented ...
from argparse import ArgumentParser,SUPPRESS
p = ArgumentParser(description = 'foo',usage=SUPPRESS)
p.add_argument('-b', '--bar', help = 'a description')
p.parse_args()
From reading the source, I've hacked a little something together which seems to work when displaying error messages as well ... warning -- This stuff is mostly undocumented, and therefore liable to change at any time :-)
from argparse import ArgumentParser,SUPPRESS
import sys as _sys
from gettext import gettext as _
class MyParser(ArgumentParser):
def error(self, message):
usage = self.usage
self.usage = None
self.print_usage(_sys.stderr)
self.exit(2, _('%s: error: %s\n') % (self.prog, message))
self.usage = usage
p = MyParser(description = 'foo',usage=SUPPRESS)
p.add_argument('-b', '--bar', help = 'a description')
p.parse_args()
Note: to not show usage for a specific argument, use
parser.add_argument('--foo', help=argparse.SUPPRESS)
per the documentation.
One additional note: if you don't want help at all you can construct the parser with add_help=False
def parse_args():
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("arg")
return parser.parse_known_args()
def main():
args, remaining = parse_args()
print(args)
print(remaining)
if __name__ == '__main__':
main()
user#host % python3 /tmp/test.py f
Namespace(arg='f')
[]
user#host % python3 /tmp/test.py f -h
Namespace(arg='f')
['-h']
user#host % python3 /tmp/test.py f -h --help
Namespace(arg='f')
['-h', '--help']
user#host %
Related
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?
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.
I want to disable exit_on_error for a parser and subparser, to prevent error messages:
import argparse
if __name__ == '__main__':
parser = argparse.ArgumentParser(exit_on_error=False)
subparsers = parser.add_subparsers()
subcommand = subparsers.add_parser('subcommand')
subcommand.add_argument('argument')
try:
arguments = parser.parse_args()
except:
parser.print_usage(std.stderr)
With no arguments (ie: no subparser invoked) it works as intended:
$ python3 main.py
usage: main.py {subcommand} ...
But when I use a subcommand, it doesn't:
$ python3 main.py subcommand
usage: main.py subcommand [-h] argument
main.py subcommand: error: the following arguments are required: argument
usage: main.py {subcommand} ...
In this instance, I'd want it to print this instead:
$ python3 main.py subcommand
usage: main.py {subcommand} ...
I checked, and neither add_subparsers nor add_parser take an exit_on_error argument. How can I suppress printing those error messages?
All I want is this.
a = first()
print("aaa updated")
b = second()
print("apt updated")
c = third()
print("fax updated")
import argparse parser = argparse.ArgumentParser()
parser.add_argument("a", help="<description>", action="store_true")
parser.add_argument("b", help="<description>", action="store_true")
parser.add_argument("c", help="<description>", action="store_true")
args = parser.parse_args()
To call either a or b or c using argparse. I don't want all of them called. I want to be able to run on CMD like myscript.py -a and that will call a.
sample script:
import argparse
parser=argparse.ArgumentParser()
parser.add_argument('--cmd', choices=['one','two','three'])
parser.add_argument('-a', action='store_true')
args = parser.parse_args()
print(args)
if args.a: # is True
print('A is True')
if args.cmd == 'one':
print('do action one')
elif args.cmd == 'two':
print('doing action two')
else:
print('doing three')
sample runs:
1651:~/mypy$ python3 stack60822121.py -h
usage: stack60822121.py [-h] [--cmd {one,two,three}] [-a]
optional arguments:
-h, --help show this help message and exit
--cmd {one,two,three}
-a
1651:~/mypy$ python3 stack60822121.py -a
Namespace(a=True, cmd=None)
A is True
doing three
1651:~/mypy$ python3 stack60822121.py --cmd one
Namespace(a=False, cmd='one')
do action one
argparse is just a way of parsing/decoding the user input. It does not perform actions. For that use ordinary Python logic. That's your code. At its most basic argparse set values to strings, boolean values, and numbers. Use those.
I'm using python click for my CLI. When I pass in the wrong set of arguments or flags, a usage message pops up. However, when I use the --help flag a more detailed usage message pops up with a list of all options and arguments. Is there a way to change the default behavior so that a usage error prints the full detailed help?
For example, a missing argument prints
mycli foo
Usage: mycli foo [OPTIONS] MY_ARG
Error: Missing argument "my_arg".
But adding --help prints
mycli foo --help
Usage: mycli foo [OPTIONS] MY_ARG
Long and useful description of the command and stuff.
Options:
-h, --help Show this message and exit.
The command is implemented roughly like so
#click.group()
#click.pass_context
def cli(ctx):
ctx.obj = {}
#cli.command()
#click.argument('my_arg')
#click.pass_context
#report_errors
def foo(ctx, my_arg):
# some stuff here
it could be done by monkey-patching UsageError
import click
from click.exceptions import UsageError
from click._compat import get_text_stderr
from click.utils import echo
def _show_usage_error(self, file=None):
if file is None:
file = get_text_stderr()
color = None
if self.ctx is not None:
color = self.ctx.color
echo(self.ctx.get_help() + '\n', file=file, color=color)
echo('Error: %s' % self.format_message(), file=file, color=color)
UsageError.show = _show_usage_error
#click.group()
#click.pass_context
def cli(ctx):
ctx.obj = {}
#cli.command()
#click.argument('my_arg')
#click.pass_context
#report_errors
def foo(ctx, my_arg):
# some stuff here
I'm using argprase to create an option, and it's a very specific option to do one specific job. The script currently has roughly 30 knobs, and most aren't used regularly.
I'm creating an option:
opt.add_argument('-opt',help="Some Help", help=argparse.SUPPRESS)
But i want there to be two ways to show the help for the script:
my_script -help
my_script -help-long
I want the -help-long to also show all the hidden args. I couldn't find a way to do this.
Is there a way to implement this behavior?
I don't think there's a builtin way to support this. You can probably hack around it by checking sys.argv directly and using that to modify how you build the parser:
import sys
show_hidden_args = '--help-long' in sys.argv
opt = argparse.ArgumentParser()
opt.add_argument('--hidden-arg', help='...' if show_hidden_args else argparse.SUPPRESS)
opt.add_argument('--help-long', help='Show all options.', action='help')
args = opt.parse_args()
Of course, if writing this over and over is too inconvenient, you can wrap it in a helper function (or subclass ArgumentParser):
def add_hidden_argument(*args, **kwargs):
if not show_hidden_args:
kwargs['help'] = argparse.SUPPRESS
opt.add_argument(*args, **kwargs)
And you'll probably want to add a non-hidden --help-long argument so that users know what it supposedly does...
This is a variation on #mgilson's answer, looking in sys.argv to see whether we should suppress some help or not
import argparse
import sys
def hide_args(arglist):
for action in arglist:
action.help=argparse.SUPPRESS
hidelist=[]
parser = argparse.ArgumentParser()
a1 = parser.add_argument('--foo',help='normal')
a2 = parser.add_argument('--bar',help='hidden')
hidelist.append(a2)
if '-h' in sys.argv[1:]:
hide_args(hidelist)
args = parser.parse_args()
Here I've chosen to interpret --help as asking for a long help; -h for the short. I could have added a separate --longhelp argument instead.
1207:~/mypy$ python3 stack37303960.py --help
usage: stack37303960.py [-h] [--foo FOO] [--bar BAR]
optional arguments:
-h, --help show this help message and exit
--foo FOO normal
--bar BAR hidden
for a short help
1207:~/mypy$ python3 stack37303960.py -h
usage: stack37303960.py [-h] [--foo FOO]
optional arguments:
-h, --help show this help message and exit
--foo FOO normal
add_argument returns a pointer to the Action object that it created. Here I save selected ones in the hidelist. Then I conditionally iterate through that list and change the help to SUPPRESS. Many of the attributes of an Action can be changed after the initial creation (experiment in an interactive session).
The parser also maintains a list of actions. The default help is the first one on the parser._actions list. It uses this list both for parsing and formatting the help.
In [540]: parser._actions[0]
Out[540]: _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)
You could achieve something by subclassing ArgumentParser and _HelpAction:
class LongHelp(argparse._HelpAction):
def __init__(self,*args, **kwargs):
super().__init__(*args, **kwargs)
def __call__(cls, parser, namespace, values, option_string):
print(parser.long_help)
class ArgParserWithLongHelp(argparse.ArgumentParser):
def __init__(self):
super().__init__(self)
self.long_help = {}
self.add_argument("--long-help", action=LongHelp )
def add_argument(self, *args, **kwargs):
if kwargs.get('long_help'):
self.long_help.update({k:kwargs['long_help'] for k in args})
kwargs.pop('long_help')
super().add_argument(*args, **kwargs)
opt = ArgParserWithLongHelp()
opt.add_argument('-opt', help=argparse.SUPPRESS, long_help='Some extra help')
args = opt.parse_args()